WechatPaymentController.java 10.3 KB
package com.diligrp.cashier.boss.controller;

import com.diligrp.cashier.boss.exception.BossServiceException;
import com.diligrp.cashier.boss.util.HttpUtils;
import com.diligrp.cashier.pipeline.core.WechatPartnerPipeline;
import com.diligrp.cashier.pipeline.core.WechatPipeline;
import com.diligrp.cashier.pipeline.domain.OnlinePaymentResponse;
import com.diligrp.cashier.pipeline.domain.OnlineRefundResponse;
import com.diligrp.cashier.pipeline.domain.wechat.*;
import com.diligrp.cashier.pipeline.service.IPaymentPipelineManager;
import com.diligrp.cashier.pipeline.service.IWechatPaymentService;
import com.diligrp.cashier.pipeline.type.OutPaymentType;
import com.diligrp.cashier.pipeline.type.PaymentState;
import com.diligrp.cashier.pipeline.util.WechatConstants;
import com.diligrp.cashier.pipeline.util.WechatSignatureUtils;
import com.diligrp.cashier.pipeline.util.WechatStateUtils;
import com.diligrp.cashier.shared.ErrorCode;
import com.diligrp.cashier.shared.domain.Message;
import com.diligrp.cashier.shared.util.DateUtils;
import com.diligrp.cashier.shared.util.JsonUtils;
import com.diligrp.cashier.trade.model.OnlinePayment;
import com.diligrp.cashier.trade.service.ICashierPaymentService;
import com.diligrp.cashier.trade.service.ITradeAssistantService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;

@RestController
@RequestMapping(value = "/wechat")
public class WechatPaymentController {

    private static final Logger LOG = LoggerFactory.getLogger(WechatPaymentController.class);

    @Resource
    private IWechatPaymentService wechatPaymentService;

    @Resource
    private ITradeAssistantService tradeAssistantService;

    @Resource
    private ICashierPaymentService cashierPaymentService;

    @Resource
    private IPaymentPipelineManager paymentPipelineManager;

    @RequestMapping(value = "/payment/openId.do")
    public Message<?> openId(@RequestParam("pipelineId") Long pipelineId, @RequestParam("code") String code) {
        String openId = wechatPaymentService.openIdByCode(pipelineId, code);
        return Message.success(openId);
    }

    @RequestMapping(value = "/payment/deliver.do")
    public Message<?> deliverGoods(@RequestParam("paymentId") String paymentId,
                                   @RequestParam("logisticsType") Integer logisticsType) {
        OnlinePayment payment = tradeAssistantService.findByPaymentId(paymentId);
        if (!PaymentState.SUCCESS.equalTo(payment.getState())) {
            throw new BossServiceException(ErrorCode.INVALID_OBJECT_STATE, "微信订单未完成支付,不能进行发货操作");
        }

        UploadShippingRequest request = UploadShippingRequest.of(payment.getOutTradeNo(), logisticsType,
            payment.getGoods(), payment.getPayerId());
        wechatPaymentService.deliverGoods(payment.getPipelineId(), request);
        return Message.success();
    }

    /**
     * 微信支付结果通知
     */
    @RequestMapping(value = "/payment/{paymentId}/notify.do")
    public ResponseEntity<NotifyResult> paymentNotify(HttpServletRequest request, @PathVariable("paymentId") String paymentId) {
        LOG.info("Receiving wechat payment result notify for {}", paymentId);
        String payload = HttpUtils.httpBody(request);

        try {
            OnlinePayment payment = tradeAssistantService.findByPaymentId(paymentId);
            WechatPipeline<?> pipeline = paymentPipelineManager.findPipelineById(payment.getPipelineId(), WechatPipeline.class);
            if (dataVerify(request, pipeline, payload)) {
                WechatNotifyResponse response = JsonUtils.fromJsonString(payload, WechatNotifyResponse.class);
                OnlinePaymentResponse paymentResponse = paymentResponse(pipeline, response);
                if (WechatConstants.NOTIFY_EVENT_TYPE.equals(response.getEvent_type())) {
                    cashierPaymentService.notifyPaymentResponse(paymentResponse);
                }
                return ResponseEntity.ok(NotifyResult.success());
            } else {
                LOG.error("Wechat payment result notify data verify failed");
                return ResponseEntity.badRequest().body(NotifyResult.failure("Data verify failed"));
            }
        } catch (Exception ex) {
            LOG.error("Process wechat payment result notify exception", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(NotifyResult.failure("Process wechat payment result notify exception"));
        }
    }

    @RequestMapping(value = "/refund/{refundId}/notify.do")
    public ResponseEntity<NotifyResult> refundNotify(HttpServletRequest request, @PathVariable("refundId") String refundId) {
        LOG.info("Receiving wechat refund result notify for {}", refundId);
        String payload = HttpUtils.httpBody(request);

        try {
            OnlinePayment refund = tradeAssistantService.findByRefundId(refundId);
            WechatPipeline<?> pipeline = paymentPipelineManager.findPipelineById(refund.getPipelineId(), WechatPipeline.class);

            if (dataVerify(request, pipeline, payload)) {
                WechatNotifyResponse notifyResponse = JsonUtils.fromJsonString(payload, WechatNotifyResponse.class);
                if (WechatConstants.REFUND_EVENT_TYPE.equals(notifyResponse.getEvent_type())) {
                    OnlineRefundResponse refundResponse = refundResponse(pipeline, notifyResponse);
                    cashierPaymentService.notifyRefundResult(refundResponse);
                }
                return ResponseEntity.ok(NotifyResult.success());
            } else {
                LOG.error("Wechat refund result notify data verify failed");
                return ResponseEntity.badRequest().body(NotifyResult.failure("Data verify failed"));
            }
        } catch (Exception ex) {
            LOG.error("Process wechat refund result notify exception", ex);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(NotifyResult.failure("Process wechat refund result notify exception"));
        }
    }

    private OnlinePaymentResponse paymentResponse(WechatPipeline<?> pipeline, WechatNotifyResponse notifyResponse) throws Exception {
        WechatNotifyResponse.Resource resource = notifyResponse.getResource();
        String payload = WechatSignatureUtils.decrypt(resource.getCiphertext(), resource.getNonce(),
            resource.getAssociated_data(), pipeline.getClient().getWechatConfig().getApiV3Key());
        if (pipeline instanceof WechatPartnerPipeline) {
            PartnerPaymentResponse response = JsonUtils.fromJsonString(payload, PartnerPaymentResponse.class);
            LocalDateTime when = DateUtils.parseDateTime(response.getSuccess_time(), WechatConstants.RFC3339_FORMAT);
            String payer = response.getPayer() == null ? null : response.getPayer().getSp_openid();
            PaymentState paymentState = WechatStateUtils.getPaymentState(response.getTrade_state());
            return new OnlinePaymentResponse(response.getOut_trade_no(), response.getTransaction_id(), OutPaymentType.WXPAY,
                payer, when, paymentState, response.getTrade_state_desc());
        } else {
            DirectPaymentResponse response = JsonUtils.fromJsonString(payload, DirectPaymentResponse.class);
            LocalDateTime when = DateUtils.parseDateTime(response.getSuccess_time(), WechatConstants.RFC3339_FORMAT);
            String payer = response.getPayer() == null ? null : response.getPayer().getOpenid();
            PaymentState paymentState = WechatStateUtils.getPaymentState(response.getTrade_state());
            return new OnlinePaymentResponse(response.getOut_trade_no(), response.getTransaction_id(), OutPaymentType.WXPAY,
                payer, when, paymentState, response.getTrade_state_desc());
        }
    }

    private OnlineRefundResponse refundResponse(WechatPipeline<?> pipeline, WechatNotifyResponse notifyResponse) throws Exception {
        WechatNotifyResponse.Resource resource = notifyResponse.getResource();
        String payload = WechatSignatureUtils.decrypt(resource.getCiphertext(), resource.getNonce(),
            resource.getAssociated_data(), pipeline.getClient().getWechatConfig().getApiV3Key());
        RefundNotifyResponse response = JsonUtils.fromJsonString(payload, RefundNotifyResponse.class);
        LocalDateTime when = DateUtils.parseDateTime(response.getSuccess_time(), WechatConstants.RFC3339_FORMAT);
        PaymentState refundState = WechatStateUtils.getRefundState(response.getRefund_status());
        return new OnlineRefundResponse(response.getOut_refund_no(), response.getRefund_id(), when,
            refundState, response.getRefund_status());
    }

    private boolean dataVerify(HttpServletRequest request, WechatPipeline<?> pipeline, String payload) {
        String serialNo = request.getHeader(WechatConstants.HEADER_SERIAL_NO);
        String timestamp = request.getHeader(WechatConstants.HEADER_TIMESTAMP);
        String nonce = request.getHeader(WechatConstants.HEADER_NONCE);
        String sign = request.getHeader(WechatConstants.HEADER_SIGNATURE);

        try {
            return pipeline.getClient().dataVerify(serialNo, timestamp, nonce, sign, payload);
        } catch (Exception ex) {
            LOG.error("Wechat result notify data verify failed", ex);
            return false;
        }
    }

    public static class NotifyResult {
        private String code;
        private String message;

        public static NotifyResult success() {
            NotifyResult result = new NotifyResult();
            result.code = "SUCCESS";
            result.message = "SUCCESS";
            return result;
        }

        public static NotifyResult failure(String message) {
            NotifyResult result = new NotifyResult();
            result.code = "FAILED";
            result.message = message;
            return result;
        }

        public String getCode() {
            return code;
        }

        public String getMessage() {
            return message;
        }
    }

}