wxPay微信支付订单提交提示「订单号重复」问题

1 摘要

在项目中使用微信、支付宝等第三方支付的时候,可能会遇到各种偶然性问题,本文将记录关于接入微信支付过程中出现的订单号重复的问题

2 问题复现

微信支付官方文档: https://pay.weixin.qq.com/wiki/doc/api/index.html

常用的微信支付方式包括JSAPI支付、Native支付、APP支付、小程序支付等,对于商户而言,一般都有自己的订单系统,只是支付需要外接第三方,因此商户会有自己的订单编号,一个订单对应一个订单编号,那么会出现这样的场景,商家的用户在平台上下了一个订单,然后使用Native支付的方式提交过一次订单(调用微信的「统一下单接口」),但是未支付,然后再切换到APP支付/小程序支付,由于在微信支付系统中,一个商户的同一个订单号只能选择一种支付方式提交,当这个订单第二次使用不同的方式进行提交时便会出现「订单号重复」的错误

此时如果依旧使用原来的支付方式进行提交,则可以成功,但是切换到其他支付方式,就会返回「订单号重复」的错误

支付宝支付没有这种现象

3 解决办法

3.1 解决思路

将微信支付的每一种支付方式使用一个常量进行标识,在调用微信「统一下单」接口时,将商户自身系统的订单号后边追加支付方式标识,即:

微信「统一下单」订单号 = 商户订单号 + 微信支付方式标识

在微信支付回调时,将获取到的微信「统一下单」订单号还原到商户系统的订单号,然后再进行操作

这样,即使同一个订单通过不同的不同的微信支付方式进行提交,也不会再出现「订单号重复」的错误了(当然,每一次提交订单时也不要忘了校验订单是否已经支付/取消等等)

3.2 核心代码

支付方式标识常量

./src/main/java/com/ljq/demo/pay/common/constant/PayTypeConst.java
package com.ljq.demo.pay.common.constant;

/**
 * @Description: 支付方式常量
 * @Author: junqiang.lu
 * @Date: 2019/4/17
 */
public class PayTypeConst{

    /**
     * 支付方式
     * 1 : 支付宝支付数字标识
     * 11: 支付宝电脑网站支付
     * 12: 支付宝手机网站支付
     * 13: 支付宝 APP 支付
     * AliPay: 支付宝支付文字说明
     *
     * 2: 微信支付标识
     * 21: 微信 NATIVE 支付(二维码支付)
     * 22: 微信 JSAPI 支付
     * 23: 微信 H5 支付
     * 24: 微信 APP 支付
     * WxPay: 微信支付文字说明
     */
    public static final int ORDER_PAY_TYPE_ALIPAY = 1;
    public static final int ORDER_PAY_TYPE_ALIPAY_PC = 11;
    public static final int ORDER_PAY_TYPE_ALIPAY_WAP = 12;
    public static final int ORDER_PAY_TYPE_ALIPAY_APP = 13;
    public static final String ORDER_PAY_TYPE_ALIPAY_NOTE = "AliPay";
    public static final int ORDER_PAY_TYPE_WX = 2;
    public static final int ORDER_PAY_TYPE_WX_NATIVE = 21;
    public static final int ORDER_PAY_TYPE_WX_JSAPI = 22;
    public static final int ORDER_PAY_TYPE_WX_H5 = 23;
    public static final int ORDER_PAY_TYPE_WX_APP = 24;
    public static final String ORDER_PAY_TYPE_WX_NOTE = "WxPay";

}

订单支付业务层示例

./src/main/java/com/ljq/demo/pay/service/impl/PayServiceImpl.java
package com.ljq.demo.pay.service.impl;

import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.ljq.demo.pay.bean.PayBean;
import com.ljq.demo.pay.common.api.ApiResult;
import com.ljq.demo.pay.common.api.ResponseCode;
import com.ljq.demo.pay.common.constant.PayTypeConst;
import com.ljq.demo.pay.common.util.*;
import com.ljq.demo.pay.configure.AliPayConfig;
import com.ljq.demo.pay.configure.WxPayConfig;
import com.ljq.demo.pay.service.PayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Description: 支付业务具体实现
 * @Author: junqiang.lu
 * @Date: 2018/7/10
 */
@Service("payService")
@Slf4j
public class PayServiceImpl implements PayService {

    @Autowired
    private WxPayConfig wxPayConfig;
    @Autowired
    private AliPayConfig aliPayConfig;


    /**
     * 创建支付订单
     *
     * @param payBean json 格式参数
     * @return
     */
    @Override
    public ApiResult createPayOrder(PayBean payBean) throws Exception {
        // 微信支付金额换算
        int amountWxPay = CalculateUtil.multiply(Double.valueOf(payBean.getAmount()), 100, 2).intValue();
        // 返回结果
        Map<String, String> resultMap = new HashMap<>(16);
        // 创建支付订单
        switch (payBean.getPayType()) {
            case PayTypeConst.ORDER_PAY_TYPE_ALIPAY_PC:
                // 支付宝电脑网站支付
                String aliPayPCForm = AliPayManager.createPCOrder(payBean.getOrderNo(),
                        String.valueOf(payBean.getAmount()), aliPayConfig);
                if (!StringUtils.isEmpty(aliPayPCForm)) {
                    resultMap.put("prePayOrderInfo",aliPayPCForm);
                    return ApiResult.success(resultMap);
                }
                break;
            case PayTypeConst.ORDER_PAY_TYPE_ALIPAY_WAP:
                // 支付宝手机网站支付
                String aliPayWapForm = AliPayManager.createWapOrder(payBean.getOrderNo(),
                        String.valueOf(payBean.getAmount()), aliPayConfig);
                if (!StringUtils.isEmpty(aliPayWapForm)) {
                    resultMap.put("prePayOrderInfo",aliPayWapForm);
                    return ApiResult.success(resultMap);
                }
                break;
            case PayTypeConst.ORDER_PAY_TYPE_ALIPAY_APP:
                // 支付宝 APP 支付
                String aliPayAppForm = AliPayManager.createAppOrder(payBean.getOrderNo(),
                        String.valueOf(payBean.getAmount()), aliPayConfig);
                if (!StringUtils.isEmpty(aliPayAppForm)) {
                    resultMap.put("prePayOrderInfo",aliPayAppForm);
                    return ApiResult.success(resultMap);
                }
                break;
            case PayTypeConst.ORDER_PAY_TYPE_WX_NATIVE:
                // 微信 NATIVE 支付(二维码)
                Map<String,String> wxPayNativeMap = WxPayManager.createNativeOrder(wxPayConfig,
                        payBean.getOrderNo() + PayTypeConst.ORDER_PAY_TYPE_WX_NATIVE,
                        amountWxPay, payBean.getIp());
                if (wxPayNativeMap != null &&
                        Objects.equals(wxPayNativeMap.get("pre_pay_order_status"), wxPayConfig.getResponseSuccess())) {
                    resultMap.put("prePayOrderInfo",wxPayNativeMap.get("code_url"));
                    return ApiResult.success(resultMap);
                }
                break;
            case PayTypeConst.ORDER_PAY_TYPE_WX_JSAPI:
                // 微信 JsAPI 支付(公众号)
                if (StringUtils.isEmpty(payBean.getOpenId())) {
                    return ApiResult.failure(ResponseCode.PAY_SUBMIT_ERROR);
                }
                Map<String, String> wxPayJsAPIMap = WxPayManager.createJsAPIOrder(wxPayConfig,
                        payBean.getOrderNo() + PayTypeConst.ORDER_PAY_TYPE_WX_JSAPI,
                        amountWxPay, payBean.getIp(), payBean.getOpenId());
                if (wxPayJsAPIMap != null &&
                        Objects.equals(wxPayJsAPIMap.get("pre_pay_order_status"), wxPayConfig.getResponseSuccess())) {
                    return ApiResult.success(wxPayJsAPIMap);
                }
                break;
            case PayTypeConst.ORDER_PAY_TYPE_WX_H5:
                // 微信 H5 支付
                Map<String, String> wxPayH5Map = WxPayManager.createH5Order(wxPayConfig,
                        payBean.getOrderNo() + PayTypeConst.ORDER_PAY_TYPE_WX_H5,
                        amountWxPay, payBean.getIp());
                if (wxPayH5Map != null &&
                        Objects.equals(wxPayH5Map.get("pre_pay_order_status"), wxPayConfig.getResponseSuccess())) {
                    resultMap.put("prePayOrderInfo",wxPayH5Map.get("mweb_url"));
                    return ApiResult.success(resultMap);
                }
                break;
            case PayTypeConst.ORDER_PAY_TYPE_WX_APP:
                // 微信 APP 支付
                Map<String, String> wxPayAppMap = WxPayManager.createAppOrder(wxPayConfig,
                        payBean.getOrderNo() + PayTypeConst.ORDER_PAY_TYPE_WX_APP,
                        amountWxPay, payBean.getIp());
                if (wxPayAppMap != null &&
                        Objects.equals(wxPayAppMap.get("pre_pay_order_status"), wxPayConfig.getResponseSuccess())) {
                    return ApiResult.success(wxPayAppMap);
                }
                break;
            case PayTypeConst.ORDER_PAY_TYPE_WX_MINI:
                // 微信 小程序 支付
                if (StringUtils.isEmpty(payBean.getOpenId())) {
                    return ApiResult.failure(ResponseCode.PAY_SUBMIT_ERROR);
                }
                Map<String, String> wxPayMiniMap = WxPayManager.createJsAPIOrder(wxPayConfig,
                        payBean.getOrderNo() + PayTypeConst.ORDER_PAY_TYPE_WX_MINI,
                        amountWxPay, payBean.getIp(), payBean.getOpenId());
                if (wxPayMiniMap != null &&
                        Objects.equals(wxPayMiniMap.get("pre_pay_order_status"), wxPayConfig.getResponseSuccess())) {
                    return ApiResult.success(wxPayMiniMap);
                }
                break;
            default:
                return ApiResult.failure(ResponseCode.PAY_TYPE_ERROR);
        }
        return ApiResult.failure(ResponseCode.PAY_SUBMIT_ERROR);
    }

    /**
     * (主动)获取支付结果
     *
     * @param payBean 订单信息(json 格式参数)
     * @return
     */
    @Override
    public ApiResult getPayResult(PayBean payBean) throws Exception {
        // 返回结果
        Map<String, String> resultMap;
        switch (payBean.getPayType()) {
            case PayTypeConst.ORDER_PAY_TYPE_ALIPAY_PC:
            case PayTypeConst.ORDER_PAY_TYPE_ALIPAY_WAP:
            case PayTypeConst.ORDER_PAY_TYPE_ALIPAY_APP:
                resultMap = AliPayManager.getPayResult(aliPayConfig, payBean.getOrderNo());
                break;
            case PayTypeConst.ORDER_PAY_TYPE_WX_NATIVE:
            case PayTypeConst.ORDER_PAY_TYPE_WX_JSAPI:
            case PayTypeConst.ORDER_PAY_TYPE_WX_H5:
            case PayTypeConst.ORDER_PAY_TYPE_WX_APP:
            case PayTypeConst.ORDER_PAY_TYPE_WX_MINI:
                resultMap = WxPayManager.getPayResult(wxPayConfig, payBean.getOrderNo() + payBean.getPayType());
                break;
            default:
                return ApiResult.failure(ResponseCode.PAY_TYPE_ERROR);
        }
        if (MapUtil.isEmpty(resultMap)) {
            return ApiResult.failure(ResponseCode.PAY_STATUS_ERROR);
        }

        return ApiResult.success(resultMap);
    }

    /**
     * 微信支付结果通知
     *
     * @param request 微信支付回调请求
     * @return 支付结果
     */
    @Override
    public String wxPayNotify(HttpServletRequest request) {

        String result = null;
        try {
            InputStream inputStream = request.getInputStream();
            /**
             * 读取通知参数
             */
            String strXML = FileUtil.getStringFromStream(inputStream);
            Map<String,String> reqMap = MapUtil.xml2Map(strXML);
            if(MapUtil.isEmpty(reqMap)){
                log.warn("request param is null");
                return wxPayConfig.getResponseFail();
            }
            /**
             * 校验签名
             */
            if(!SignUtil.signValidate(reqMap, wxPayConfig.getKey(), wxPayConfig.getFieldSign())){
                log.warn("wxPay sign is error");
                return wxPayConfig.getResponseFail();
            }
            String orderNo = reqMap.get("out_trade_no").substring(0,reqMap.get("out_trade_no").length()-2);
            log.debug("微信支付回调,订单编号: {}", orderNo);
            // TODO 其他业务处理


            Map<String, String> resultMap = new HashMap<>(16);
            resultMap.put("return_code",wxPayConfig.getResponseSuccess());
            resultMap.put("return_msg","OK");
            result = MapUtil.map2Xml(resultMap);
        } catch (IOException e) {
            log.error("get request inputStream error",e);
            return wxPayConfig.getResponseFail();
        } catch (Exception e) {
            log.error("resolve request param error",e);
            return wxPayConfig.getResponseFail();
        }
        return result;
    }

    /**
     * 支付宝支付结果通知
     *
     * @param request 支付宝回调请求
     * @return
     */
    @Override
    public String aliPayNotify(HttpServletRequest request) {
        // 读取通知参数
        Map<String, String> params = AliPayManager.getNotifyParams(request.getParameterMap());
        if(MapUtil.isEmpty(params)){
            return aliPayConfig.getResponseFail();
        }
        try {
            // 签名校验
            if(!AlipaySignature.rsaCheckV1(params, aliPayConfig.getAlipayPublicKey(),
                    aliPayConfig.getCharset(), aliPayConfig.getSignType())){
                return aliPayConfig.getResponseFail();
            }
            String orderNo = params.get("out_trade_no");
            log.debug("支付宝回调,订单编号: {}",orderNo);
            // TODO 其他业务处理



        } catch (AlipayApiException e) {
            log.error("支付宝回调验证失败",e);
            return aliPayConfig.getResponseFail();
        }
        return aliPayConfig.getResponseSuccess();
    }

    /**
     * 支付宝支付同步通知返回地址
     * @param request
     * @return
     */
    @Override
    public String aliPayReturnUrl(HttpServletRequest request) {
        // 读取通知参数
        Map<String, String> params = AliPayManager.getNotifyParams(request.getParameterMap());
        if(MapUtil.isEmpty(params)){
            return "alipay_fail_url";
        }
        try {
            // 签名校验
            if(!AlipaySignature.rsaCheckV1(params, aliPayConfig.getAlipayPublicKey(),
                    aliPayConfig.getCharset(), aliPayConfig.getSignType())){
                return "alipay_fail_url";
            }

        } catch (AlipayApiException e) {
            log.error("支付宝回调验证失败",e);
            return aliPayConfig.getResponseFail();
        }
        return "alipay_success_url";
    }

}

3.3 Github项目源码

GitHub项目源码: https://github.com/Flying9001/pay

个人公众号:404Code,记录半个互联网人的技术与思考,感兴趣的可以关注.
404Code

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值