likes
comments
collection
share

小程序接入微信支付V3接口开发教程

作者站长头像
站长
· 阅读数 22

前言

最近做了一个小程序对接微信支付的需求,查看微信支付文档,还是感觉有点凌乱,所以做一个统一整理,供大家参考。

API参考官方文档:pay.weixin.qq.com/wiki/doc/ap…

支付流程

小程序接入微信支付V3接口开发教程

重点步骤说明:

开发步骤

1 接入前准备

1.1 微信支付配置申请

详细操作流程参考官方文档:pay.weixin.qq.com/wiki/doc/ap…

配置完成需要以下信息:

  • APPID
  • 商户号(mchid)
  • 商户API私钥(apiclient_key.pem)
  • 商户证书序列号
  • 商户APIv3密钥
1.2 引入开发库
Gradle
implementation 'com.github.wechatpay-apiv3:wechatpay-java:0.2.10'
Maven
<dependency>
  <groupId>com.github.wechatpay-apiv3</groupId>
  <artifactId>wechatpay-java</artifactId>
  <version>0.2.10</version>
</dependency>
1.3 创建配置
配置信息
#微信支付配置
wx:
  pay:
    appId: 
    merchantId: 
    privateKey: 
    merchantSerialNumber: 
    apiV3Key: 
    payNotifyUrl: 
配置类
package com.xh.server.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author yupf
 * @description 微信支付配置类
 * @date 2023/7/26 09:30
 */
@Data
@Component
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayConfig {

    //APPID
    private String appId;
    //mchid
    private String merchantId;
    //商户API私钥
    private String privateKey;
    //商户证书序列号
    private String merchantSerialNumber;
    //商户APIv3密钥
    private String apiV3Key;
    //支付通知地址
    private String payNotifyUrl;
}

1.4 初始化商户配置
package com.xh.server.config;

import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

/**
 * @author yupf
 * @description 微信支付证书自动更新配置
 * @date 2023/7/26 14:37
 */

@Configuration
public class WxPayAutoCertificateConfig {

    @Resource
    private WxPayConfig wxPayConfig;

    /**
     * 初始化商户配置
     * @return
     */
    @Bean
    public RSAAutoCertificateConfig rsaAutoCertificateConfig() {
        RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(wxPayConfig.getMerchantId())
                .privateKey(wxPayConfig.getPrivateKey())
                .merchantSerialNumber(wxPayConfig.getMerchantSerialNumber())
                .apiV3Key(wxPayConfig.getApiV3Key())
                .build();
        return config;
    }
}

2 JSAPI下单

文档地址:pay.weixin.qq.com/wiki/doc/ap…

    @ApiOperation(value = "预支付订单", notes = "预支付订单")
    @PostMapping("/create")
    public R<PrepayWithRequestPaymentResponse> createOrder(@Validated @RequestBody CreateOrderReq req) {
        //创建初始化订单
          ***
         
        //请求微信支付相关配置
        JsapiServiceExtension service =
                new JsapiServiceExtension.Builder()
                        .config(rsaAutoCertificateConfig)
                        .signType("RSA") // 不填默认为RSA
                        .build();
        PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
        try {
            PrepayRequest request = new PrepayRequest();
            request.setAppid(wxPayConfig.getAppId());
            request.setMchid(wxPayConfig.getMerchantId());
            request.setDescription(order.getDescription());
            request.setOutTradeNo(order.getOrderNo());
            request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
            Amount amount = new Amount();
            amount.setTotal(order.getAmount().multiply(new BigDecimal("100")).intValue());
            request.setAmount(amount);
            Payer payer = new Payer();
            payer.setOpenid(req.getWxOpenId());
            request.setPayer(payer);
            log.info("请求预支付下单,请求参数:{}", JSONObject.toJSONString(request));
            // 调用预下单接口
            response = service.prepayWithRequestPayment(request);
            log.info("订单【{}】发起预支付成功,返回信息:{}", order.getOrderNo(), response);
        } catch (HttpException e) { // 发送HTTP请求失败
            log.error("微信下单发送HTTP请求失败,错误信息:{}", e.getHttpRequest());
            return R.error().message("下单失败");
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
            log.error("微信下单服务状态错误,错误信息:{}", e.getErrorMessage());
            return R.error().message("下单失败");
        } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
            log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
            return R.error().message("下单失败");
        }
      
        //更新订单状态
          ***
        return R.ok().data(response);
    }

这里有几个注意点:

  1. 小程序预支付下单需要获取用户的openid;
  2. 预支付下单请求及返回类所在包为com.wechat.pay.java.service.payments.jsapi;
  3. PrepayWithRequestPaymentResponse对应的是wx.requestPayment接口中的请求参数,前端可直接使用;

3 查询订单

文档地址:pay.weixin.qq.com/wiki/doc/ap…

微信提供了两种查询订单方式:

  1. 微信支付订单号查询;
  2. 商户订单号查询
    @ApiOperation(value = "根据支付订单号查询订单", notes = "根据支付订单号查询订单")
    @PostMapping("/queryOrder")
    public R queryOrder(@Validated @RequestBody QueryOrderReq req) {
        QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
        queryRequest.setMchid(wxPayConfig.getMerchantId());
        queryRequest.setTransactionId(req.paymentNo);
        try {
            JsapiServiceExtension service =
                    new JsapiServiceExtension.Builder()
                            .config(rsaAutoCertificateConfig)
                            .signType("RSA") // 不填默认为RSA
                            .build();
            Transaction result = service.queryOrderById(queryRequest);
            if (Transaction.TradeStateEnum.SUCCESS != result.getTradeState()) {
                log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
            }
        } catch (ServiceException e) {
            log.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
            return R.error();
        }
        return R.ok();
    }
    @ApiOperation(value = "根据商户订单号查询订单", notes = "根据商户订单号查询订单")
    @PostMapping("/queryOrder")
    public R queryOrder(@Validated @RequestBody QueryOrderReq req) {
        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        queryRequest.setMchid(wxPayConfig.getMerchantId());
        queryRequest.setOutTradeNo(req.orderNo);
        try {
            JsapiServiceExtension service =
                    new JsapiServiceExtension.Builder()
                            .config(rsaAutoCertificateConfig)
                            .signType("RSA") // 不填默认为RSA
                            .build();
            Transaction result = service.queryOrderByOutTradeNo(queryRequest);
            if (Transaction.TradeStateEnum.SUCCESS != result.getTradeState()) {
                log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
            }
        } catch (ServiceException e) {
            log.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
            return R.error();
        }
        return R.ok();
    }

4 支付通知

文档地址:pay.weixin.qq.com/wiki/doc/ap…

回调地址notify_url设置规范:

  1. 异步接收微信结果通知回调地址,通知url必须为外网可访问的url;
  2. 不能携带任何参数;
  3. 公网域名必须为https,现使用http域名能正常接收回调的用户,建议更换https,避免后期出现回调通知无法接收的情况;
  4. 不支持携带端口号

其他注意事项详见文档:pay.weixin.qq.com/wiki/doc/ap…

    @ApiOperation(value = "预支付-回调")
    @PostMapping("/payNotify")
    public synchronized String payNotify(HttpServletRequest request) throws IOException {
        log.info("------收到支付通知------");
        // 请求头Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 微信支付证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");

        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signType)
                .body(HttpServletUtils.getRequestBody(request))
                .build();

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        // 以支付通知回调为例,验签、解密并转换成 Transaction
        log.info("验签参数:{}", requestParam);
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        log.info("验签成功!-支付回调结果:{}", transaction.toString());
        

        Map<String, String> returnMap = new HashMap<>(2);
        returnMap.put("code", "FAIL");
        returnMap.put("message", "失败");
        if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) {
            log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", transaction.getOutTradeNo(), transaction.getTransactionId());
            return JSONObject.toJSONString(returnMap);
        }

        //修改订单前,建议主动请求微信查询订单是否支付成功,防止恶意post
          ***

        //修改订单信息
          ***

        returnMap.put("code", "SUCCESS");
        returnMap.put("message", "成功");
        return JSONObject.toJSONString(returnMap);
    }
package com.xh.server.util;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author yupf
 * @date 2023/07/26 上午9:17
 **/
public class HttpServletUtils {

    /**
     * 获取请求体
     *
     * @param request
     * @return
     * @throws IOException
     */
    public static String getRequestBody(HttpServletRequest request) throws IOException {
        ServletInputStream stream = null;
        BufferedReader reader = null;
        StringBuffer sb = new StringBuffer();
        try {
            stream = request.getInputStream();
            // 获取响应
            reader = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw new IOException("读取返回支付接口数据流出现异常!");
        } finally {
            reader.close();
        }
        return sb.toString();
    }

}

总结

以上便是小程序对接微信支付的全部流程,其他如APP或者H5对接流程大同小异,只不过引用的包有所不同,建议下载开发库wechatpay-java详细阅读以下源码。

转载自:https://juejin.cn/post/7262900590937407546
评论
请登录