Administrator
2024-03-12 3f17034c768ba4fc330e01f014b4f880e6a1569c
pipIrr-platform/pipIrr-web/pipIrr-web-sell/src/main/java/com/dy/pipIrrSell/wechatpay/PaymentCtrl.java
@@ -1,11 +1,12 @@
package com.dy.pipIrrSell.wechatpay;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.dy.common.aop.SsoAop;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.common.webUtil.ResultCodeMsg;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefund;
import com.dy.pipIrrGlobal.pojoSe.SeVcRefundItem;
import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard;
import com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState;
import com.dy.pipIrrGlobal.voSe.VoClient;
@@ -16,7 +17,12 @@
import com.dy.pipIrrSell.util.RestTemplateUtil;
import com.dy.pipIrrSell.virtualCard.VirtualCardSv;
import com.dy.pipIrrSell.virtualCard.dto.DtoVirtualCard;
import com.dy.pipIrrSell.wechatpay.dto.*;
import com.dy.pipIrrSell.virtualCard.enums.LastOperateENUM;
import com.dy.pipIrrSell.virtualCard.enums.RefundItemStateENUM;
import com.dy.pipIrrSell.wallet.enums.RefundStatusENUM;
import com.dy.pipIrrSell.wechatpay.dto.Code2Session;
import com.dy.pipIrrSell.wechatpay.dto.DtoOrder;
import com.dy.pipIrrSell.wechatpay.dto.OrderNotify;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -65,25 +71,23 @@
    private final VirtualCardSv virtualCardSv;
    private final ClientSv clientSv;
    private String privateCertFileName = PayInfo.privateCertFileName;
    private String appid = PayInfo.appid;
    private String mchid = PayInfo.mchid;
    private String schema = PayInfo.schema;
    private String signType = PayInfo.signType;
    private String description = PayInfo.description;
    private String loginUrl = PayInfo.loginUrl;
    private String notifyUrl = PayInfo.notifyUrl;
    private String grantType = PayInfo.grantType;
    private String refundUrl = PayInfo.refundUrl;
    private final String privateCertFileName = PayInfo.privateCertFileName;
    private final String appid = PayInfo.appid;
    private final String mchid = PayInfo.mchid;
    private final String schema = PayInfo.schema;
    private final String signType = PayInfo.signType;
    private final String description = PayInfo.description;
    private final String loginUrl = PayInfo.loginUrl;
    private final String notifyUrl = PayInfo.notifyUrl;
    private final String grantType = PayInfo.grantType;
    // 平台证书公钥
    private Map CERTIFICATE_MAP = new HashMap();
    private final Map CERTIFICATE_MAP = new HashMap();
    /**
     * 登录凭证校验
     * @param appid 小程序 appId
     * @param secret 小程序 appSecret
     * @param js_code 临时登录凭证code
     * @param code2Session 登录凭证校验传入对象
     * @param bindingResult
     * @return
     * @throws Exception
     */
@@ -99,11 +103,15 @@
    @PostMapping(path = "getSessionId")
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public BaseResponse<Boolean> getSessionId(@RequestParam("appid")  String appid, @RequestParam("secret") String secret, @RequestParam("js_code") String js_code) throws Exception {
    public BaseResponse<Boolean> getSessionId(@RequestBody @Valid Code2Session code2Session, BindingResult bindingResult) throws Exception {
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("appid", appid);
        queryParams.put("secret", secret);
        queryParams.put("js_code", js_code);
        queryParams.put("secret", code2Session.getSecret());
        queryParams.put("js_code", code2Session.getJs_code());
        queryParams.put("grant_type", grantType);
        Map<String, String> headerParams = new HashMap<>();
        JSONObject job = restTemplateUtil.get(loginUrl, queryParams, headerParams);
@@ -116,20 +124,20 @@
        String sessionKey = job.getString("session_key");
        // 检验登录态
        JSONObject checkSessionKey = payHelper.checkSessionKey(appid, secret, openid, sessionKey);
        if(checkSessionKey != null) {
            Integer errcode = checkSessionKey.getInteger("errcode");
            String errmsg = checkSessionKey.getString("errmsg");
        }
        //JSONObject checkSessionKey = payHelper.checkSessionKey(appid, secret, openid, sessionKey);
        //if(checkSessionKey != null) {
        //    Integer errcode = checkSessionKey.getInteger("errcode");
        //    String errmsg = checkSessionKey.getString("errmsg");
        //}
        // 重置登录态
        JSONObject resetUserSessionKey = payHelper.resetUserSessionKey(appid, secret, openid, sessionKey);
        if(resetUserSessionKey != null) {
            Integer errcode = checkSessionKey.getInteger("errcode");
            String errmsg = checkSessionKey.getString("errmsg");
            String openid_New = checkSessionKey.getString("openid");
            String sessionKey_New = checkSessionKey.getString("session_key");
        }
        //JSONObject resetUserSessionKey = payHelper.resetUserSessionKey(appid, secret, openid, sessionKey);
        //if(resetUserSessionKey != null) {
        //    Integer errcode = checkSessionKey.getInteger("errcode");
        //    String errmsg = checkSessionKey.getString("errmsg");
        //    String openid_New = checkSessionKey.getString("openid");
        //    String sessionKey_New = checkSessionKey.getString("session_key");
        //}
        // 添加登录态记录
        SeWebchatLogonState po = new SeWebchatLogonState();
@@ -277,110 +285,14 @@
        headers.put("Accept", "application/json");
        headers.put("Content-Type", "application/json");
        JSONObject job_result = restTemplateUtil.post(PayInfo.orderUrl, body, headers);
        if(job_result != null) {
            System.out.println(job_result.toString());
            prepayId = job_result.getString("prepay_id");
        }
        // 暂时注释掉,认证通过后再放开
        //JSONObject job_result = restTemplateUtil.post(PayInfo.orderUrl, body, headers);
        //if(job_result != null) {
        //    System.out.println(job_result.toString());
        //    prepayId = job_result.getString("prepay_id");
        //}
        return BaseResponseUtils.buildSuccess(prepayId) ;
    }
    /**
     * 申请退款
     * @param po 退款请求对象
     * @param bindingResult
     * @return
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws IOException
     * @throws SignatureException
     * @throws InvalidKeyException
     */
    @Operation(summary = "申请退款", description = "申请退款")
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE,
                    description = "操作结果:true:成功,false:失败(BaseResponse.content)",
                    content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
                            schema = @Schema(implementation = Boolean.class))}
            )
    })
    @PostMapping(path = "refunds")
    @Transactional(rollbackFor = Exception.class)
    @SsoAop()
    public BaseResponse<Boolean> refunds(@RequestBody @Valid Refund po, BindingResult bindingResult) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException {
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        /**
         * 1. 判断交易时间是否超过一年
         * 2. 判断退款总金额是否超过订单金额
         * 3. 判断当前订单退款次数是否超过50次
         * 4. 判断与该订单上次退款是否相隔1分钟
         */
        String tradeNo = po.getTradeNo();
        String refundNo = po.getRefundNo();
        Integer refund = po.getRefund();
        if(refundNo == null || refundNo.length() <= 0) {
            // 新提退款申请,生成退款单号
            //refundNo = generateRefundNo(tradeNo);
        }
        // 根据订单号获取总支付金额和总退款金额
        Integer totalTradeAmount = 0;
        Integer totalRefundAmount = 0;
        //Integer totalTradeAmount = getTotalTradeAmount(tradeNo);
        //Integer totalRefundAmount = getTotalRefundAmount(tradeNo);
        if(totalRefundAmount > totalTradeAmount) {
            return BaseResponseUtils.buildFail(SellResultCode.TOTAL_REFUND_EXCEED_TRADE.getMessage());
        }
        // 生成body
        RefundRequest.Amount amount = new RefundRequest.Amount();
        amount.setRefund(refund);
        amount.setTotal(totalTradeAmount);
        amount.setCurrency("CNY");
        RefundRequest refundRequest = new RefundRequest();
        refundRequest.setOut_trade_no(tradeNo);
        refundRequest.setOut_refund_no(refundNo);
        refundRequest.setNotify_url(notifyUrl);
        refundRequest.setAmount(amount);
        // 生成header
        String nonceStr = payHelper.generateRandomString();
        Long timestamp = System.currentTimeMillis() / 1000;
        String method = "POST";
        String httpUrl = "/v3/refund/domestic/refunds";
        String body = JSONObject.toJSONString(refundRequest);
        String header = schema + " " + payHelper.getToken(method, httpUrl, body, nonceStr, timestamp, privateCertFileName);
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", header);
        headers.put("Accept", "application/json");
        headers.put("Content-Type", "application/json");
        JSONObject job_refundResponse = restTemplateUtil.post(PayInfo.orderUrl, body, headers);
        RefundResponse refundResponse = JSON.parseObject(job_refundResponse.toJSONString(), RefundResponse.class);
        String status = refundResponse.getStatus();
        if(status != null && status.equals("SUCCESS")) {
            // 退款申请已受理
            return BaseResponseUtils.buildSuccess(true) ;
        } else if(status != null && status.equals("PROCESSING")) {
            // 退款处理中
            return BaseResponseUtils.buildFail(SellResultCode.PROCESSING.getMessage());
        } else {
            // 退款异常
            return BaseResponseUtils.buildError(SellResultCode.ABNORMAL.getMessage());
        }
    }
    /**
@@ -491,7 +403,68 @@
            }
        } else if(eventType != null && eventType.equals("REFUND.SUCCESS")) {
            // 退款成功后回调
            /**
             * 退款成功的回调
             * 取出通知数据对象,继而取出解密所需的associatedData和nonce,以及密文ciphertext
             * 解密ciphertext得到
             */
            OrderNotify.NotifyResource notifyResource = orderNotify.getResource();
            String associatedData = notifyResource.getAssociated_data();
            String nonce = notifyResource.getNonce();
            String ciphertext = notifyResource.getCiphertext();
            String resource = AesUtil.decryptToString(PayInfo.key.getBytes("utf-8"), associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
            JSONObject job_resource = JSONObject.parseObject(resource);
            // 解密后取出:商户订单员、微信支付订单号、交易状态、支付完成时间
            String out_trade_no = job_resource.getString("out_trade_no");
            String transaction_id = job_resource.getString("transaction_id");
            String out_refund_no = job_resource.getString("out_refund_no");
            String refund_status = job_resource.getString("refund_status");
            Date success_time = job_resource.getDate("success_time");
            if(!refund_status.equals("SUCCESS")) {
                response.setStatus(500);
                result.put("code", "FAIL");
                result.put("message", "失败");
                return result;
            }
            // 更新虚拟卡表及充值表响应字段
            SeVcRefundItem seVcRefundItem = new SeVcRefundItem();
            seVcRefundItem.setRefundTime(success_time);
            seVcRefundItem.setRefundStatus(RefundItemStateENUM.REFUNDED.getCode());
            Integer rec = virtualCardSv.updateRefundItem(seVcRefundItem);
            if(rec == null && rec <= 0) {
                response.setStatus(500);
                result.put("code", "FAIL");
                result.put("message", "失败");
                return result;
            }
            // 根据退款单号反查退款ID,根据退款ID获取退款状态是未退款的记录数量,如果是0则说明全部退款完成,更新退款表状态为已退款,将退款后金额更新到虚拟卡表
            /**
             * 根据退款通知接口返回的退款单号反查退款ID,查询该退款ID下未退款记录数量
             *      如果结果为0,则该退款已经完成
             *      1. 更新退款表状态为已退款
             *      2. 将退款后余额更新到虚拟卡表中
             */
            Integer noRefundedCount = virtualCardSv.getNoRefundedCount(out_refund_no);
            if(noRefundedCount != null && noRefundedCount == 0) {
                // 获取退款对象并修改退款状态
                Long refundId = virtualCardSv.getRefundIdByRefundNumber(out_refund_no);
                SeVcRefund seVcRefund = virtualCardSv.selectRefundByRefundId(refundId);
                seVcRefund.setRefundStatus(RefundStatusENUM.REFUNDED.getCode());
                virtualCardSv.updateRefund(seVcRefund);
                // 获取虚拟卡对象并修改余额、最后操作、最后操作时间
                Long vcId = seVcRefund.getVcId();
                Double afterRefund = seVcRefund.getAfterRefund();
                SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(vcId);
                seVirtualCard.setMoney(afterRefund);
                seVirtualCard.setLastOperate(LastOperateENUM.REFUND.getCode());
                seVirtualCard.setLastOperateTime(new Date());
                virtualCardSv.updateVirtualCard(seVirtualCard);
            }
        }
        // 通知应答