Java微信支付-统一下单API

我们项目中的支付交互流程:用户点击购买时后台首先创建订单,然后调用微信支付统一下单API返回给前台相应的参数,如果为NATIVE支付方式,前台将参数中的支付连接生成二维码展示给用户,如果为JSAPI支付方式,前台(微信客户端)调用JSAPI支付拉起柜台进行支付。

在接入微信支付之前阅读对应的官方文档(开发步骤、业务流程、API列表)非常有必要。不过微信文档写的写很坑,坑在哪?在API列表与加解密相关的写的比较坑。不过开发步骤和业务流程写的还是很不错的。 微信支付官方文档

话不多说,直接上码(这是我们项目阉割版的支付主体代码,但是调起支付是没问题的,如果有小伙伴在使用过程中遇到什么问题,可以私信我。)

# 项目中加入微信支付的SDK
<dependency>
	<groupId>com.github.wxpay</groupId>
	<artifactId>wxpay-sdk</artifactId>
	<version>0.0.3</version>  
</dependency>        
# application.properties配置文件中加入如下配置
#微信相关配置
wxconfig.appId = appId
# 微信公众平台中开发设置中的AppSecret
wxconfig.secret = secret

# 微信商户平台商户号
wxconfig.mchId = mchId
# 微信商户平台支付API秘钥
wxconfig.key = key
wxconfig.pay.notify.url = 支付成功回调地址
wxconfig.refund.notify.url = 退款成功回调地址
wxconfig.certPath = 证书apiclient_cert.p12路径
/**
 * 微信支付接口
 * 
 * @create: 2019-10-10 15:25
 * @author: Sun
 */
public interface WxPayService {
	/**
     * 统一下单(JSAPI-NATIVE支付方式该接口都支持)
     *
     * @param uid            用户id
     * @param openid         用户openid 公众号/小程序 JSAPI支付必传参数
     * @param desc           商品描述
     * @param orderNo        订单号
     * @param totalFee       订单总金额,单位为分
     * @param spbillCreateIp 下单终端ip
     * @param startTime      交易起始时间
     * @param expireTime     交易结束时间
     * @param tradeType      交易类型
     * @param productId      商品id。trade_type=NATIVE时,此参数必传。
     * @return return_code 和result_code都为SUCCESS的时候,将响应结果,存放到map中返回,否则throw WxPayException
     */
    Map<String, String> unifiedOrder(String uid, String openid, String desc, String orderNo, int totalFee,
                                     String spbillCreateIp, String startTime, String expireTime, String tradeType,String productId) throws WxPayException;

	/**
     * JSAPI支付方式-生成支付参数和支付签名
     *
     * @param prepayId 统一下单返回的预支付单id
     * @return
     */
    Map<String, String> generatePaySign(String prepayId);
}
/**
 * 微信支付接口实现
 * 
 * @create: 2019-10-10 15:40
 * @author: Sun
 */
@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Autowired
    private MyWxPayConfig myWxPayConfig;

    /**
     * 支付、退款 异步通知地址
     */
    @Value("${wxconfig.pay.notify.url}")
    private String payAsyncNotifyUrl;
    @Value("${wxconfig.refund.notify.url}")
    private String refundAsyncNotifyUrl;

    /**
     * 微信服务器响应字段
     */
    private static final String RETURN_CODE = "return_code";
    private static final String RESULT_CODE = "result_code";
    private static final String TRADE_STATE = "trade_state";
    private static final String RETURN_MSG = "return_msg";
    private static final String ERROR_CODE = "err_code";
    private static final String ERROR_CODE_DES = "err_code_des";
    private static final String PREPAY_ID = "prepay_id";


    @Override
    public Map<String, String> unifiedOrder(String uid, String openid, String desc,
                                            String orderNo, int totalFee, String spbillCreateIp,
                                            String startTime, String expireTime, String tradeType, String productId) {
        log.info("[unifiedOrder] [入参] [uid:{}] [openid:{}] [desc:{}] [orderNo:{}] [totalFee:{}] [spbillCreateIp:{}] " +
                "[startTime:{}] [expireTime:{}] [tradeType:{}] [productId:{}]", uid, openid, desc, orderNo, totalFee, spbillCreateIp, startTime, expireTime, tradeType, productId);

        WXPay wxPay = new WXPay(myWxPayConfig);

        Map<String, String> paramMap = new HashMap<>();
        // 如果传递openid为JSAPI支付方式,否则视为NATIVE支付方式
		if (!StringUtils.isBlank(openid)) {
			// JSAPI支付方式时 openid为必传参数
            paramMap.put("openid", openid);
        } else {
            // NATIVE支付方式时 product_id为必传参数
          	paramMap.put("product_id", productId);
        }
        paramMap.put("body", desc);
        paramMap.put("out_trade_no", orderNo);
        paramMap.put("total_fee", totalFee + "");
        paramMap.put("spbill_create_ip", spbillCreateIp);
        paramMap.put("notify_url", payAsyncNotifyUrl);
        paramMap.put("time_start", startTime);
        paramMap.put("time_expire", expireTime);
        paramMap.put("trade_type", tradeType);

        // 调用微信统一下单API返回参数存放集合
        Map<String, String> resultMap = null;
        try {
            resultMap = wxPay.unifiedOrder(paramMap);
            log.info("[unifiedOrder] [微信统一下单API响应参数] [uid:{}] [out_trade_no:{}] [resultMap:{}]", uid, orderNo, resultMap);
			
			// NATIVE 支付方式返回参数
			if (StringUtils.isBlank(openid)) {
                return resultMap;
            }
			
			// JSAPI 支付方式返回参数
            return generatePaySign(resultMap.get(PREPAY_ID));
            
        } catch (Exception e) {
            log.error("[unifiedOrder] [uid:{}] [out_trade_no:{}] [调用微信SDK统一下单API过程中出现异常:{}]", uid, orderNo, ExceptionUtils.getStackTrace(e));
            throw new WxPayException(4504, "调用统一下单API异常,请重新尝试!");
        }
    }
    
	@Override
    public Map<String, String> generatePaySign(String prepayId) {
        Map<String, String> payMap = new HashMap<>();
        try {
            payMap.put("appId", myWxPayConfig.getAppID());
            payMap.put("timeStamp", getCurrentTimestamp() + "");
            payMap.put("nonceStr", WXPayUtil.generateNonceStr());
            payMap.put("signType", WXPayConstants.MD5);
            payMap.put("package", "prepay_id=" + prepayId);
            // 生成paySign参数,paySign参数需要重新进行签名计算,参与签名的参数为:appId、timeStamp、nonceStr、package、signType,参数区分大小写。
            String paySign = WXPayUtil.generateSignature(payMap, myWxPayConfig.getKey());
            payMap.put("paySign", paySign);
        } catch (Exception e) {
            log.error("[generatePaySign] [生成返回前台JSAPI支付请求参数时异常:{}]", ExceptionUtils.getStackTrace(e));
            throw new WxPayException(4504, "调用统一下单API异常,请重新尝试!");
        }

        log.info("[generatePaySign] [success] [payMap:{}]", payMap);
        
        return payMap;
    }
	
	/**
     * 获取当前时间戳,单位秒
     *
     * @return
     */
    private long getCurrentTimestamp() {
        return System.currentTimeMillis() / 1000;
    }
}    
/**
 * 微信支付相关配置
 * 
 * @author: Sun
 * @create: 2019-10-10 15:59
 */
@Component
public class MyWxPayConfig implements WXPayConfig {

    @Value("${wxconfig.appId}")
    private String appId;
    @Value("${wxconfig.mchId}")
    private String mchId;
    @Value("${wxconfig.key}")
    private String key;

    private byte[] certData;
 
    public MyWxPayConfig(@Value("${wxconfig.certPath}") String certPath) throws Exception {
        File file = new File(certPath);
        InputStream certStream = new FileInputStream(file);
        this.certData = new byte[(int) file.length()];
        certStream.read(this.certData);
        certStream.close();
    }

    @Override
    public String getAppID() {
        return appId;
    }

    @Override
    public String getMchID() {
        return mchId;
    }

    @Override
    public String getKey() {
        return key;
    }

    @Override
    public InputStream getCertStream() {
        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }

    @Override
    public int getHttpConnectTimeoutMs() {
        return 10000;
    }

    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }
}
/**
 * WxPayException class.
 * 
 * @create: 2019-10-10 15:12
 * @author: Sun
 */
public class WxPayException extends RuntimeException {

	/**
     * 状态码
     */
    private int code;

    public WxPayException(int code, String message) {
        super(code, message);
    }

    public WxPayException(int code, String message, Throwable cause) {
        super(code, message, cause);
    }
	
	public int getCode() {
        return code;
    }
}

大家使用自己的Controller接收请求调用该Service接口就可测试是否可以调起微信支付了。建议使用NATIVE支付方式进行测试,因为不需要获取openid,并且在调用统一下单API之后拿到微信服务器返回的code_url然后随便找一个二维码生成网站生成二维码,就可以使用微信进行扫描支付了。

发布了40 篇原创文章 · 获赞 10 · 访问量 4048

猜你喜欢

转载自blog.csdn.net/qq_41693150/article/details/102503595