Springboot 接入微信小程序支付 Api v3

官方文档:

微信小程序支付官方文档
在这里插入图片描述

基础知识介绍

  1. 微信公众平台(注册申请小程序),微信支付平台(开通微信支付商户),注意这里是分别两个平台
  2. 注册小程序需要有营业执照完成企业认证才可以有支付功能

准备条件:

  1. 申请注册微信小程序并完成企业认证;(请参阅官方文档)
    在这里插入图片描述
    在这里插入图片描述

  2. 注册开通微信支付商户;(请参阅官方文档)
    在这里插入图片描述

SpringBoot 集成接入 Api v3

支付场景流程-商品购买下单为例

  1. 小程序端选中商品调用后端创建商品订单接口,返回订单编号到小程序端;
  2. 小程序端再带着 订单编号 去调用后端小程序支付下单接口,接口会去调用微信支付服务后台生成预支付交易单,返回正确的 预支付参数
  3. 小程序端再拿到预支付参数 直接通过 微信支付官方提供的小程序方法调起小程序支付;
  4. 小程序端支付成功后 微信支付平台会下发一条支付通知 到配置好的支付回调接口;
  5. 支付回调接口收到通知后,再根据支付状态完成 后续的系统业务逻辑,如修改订单状态等等。

后端代码

  1. 引入maven 依赖
<!--微信支付APIv3-->
 <dependency>
     <groupId>com.github.wechatpay-apiv3</groupId>
     <artifactId>wechatpay-java</artifactId>
     <version>0.2.14</version>
 </dependency>
  1. 配置文件内添加支付配置
wx:
  # 微信小程序配置
  appid: 111xxxxx2222
  secret: 222xxxxxx1111
  pay:
    #应用id(小程序id)
    appId: wxxxxxxxxxxxxxxx0d
    #商户号
    merchantId: 1xxxxxxx00
    #商户API私钥
    privateKey: apiclient_key.pem
    #商户证书序列号
    merchantSerialNumber: 24xxxxx9D4xxx0xxxxxDCCCFDxxxxx87EFxxxxC1
    #商户APIv3密钥
    apiV3Key: qmxxxxxuiz71y2u3h45jlknbv89olzx
    #支付(回调)通知地址 必须是外网可访问的https域名地址
    payNotifyUrl: http://av2gta.natappfree.cc/client/payNotify
    #退款通知地址
    refundNotifyUrl: https://xxx.xxx.xxx.xxx/xxxx/xxxx/xxxx/openapi/wx/refundNotify
  1. 配置文件相关
    代理配置类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @Author:wwf
 * @Package:com.xxx.xxx.config
 * @Project:fishing-manage
 * @name:WxPayConfig
 * @Date:2024/10/28 14:50
 * @Filename:WxPayConfig
 */
@Data
@Component
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayConfig {

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

配置文件初始化商户配置

import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.util.PemUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import javax.annotation.Resource;
import java.io.IOException;

/**
 * @Author:wwf
 * @Package:com.xxx.xxx.config
 * @Project:fishing-manage
 * @name:WxPayAutoCertificateConfig
 * @Date:2024/10/28 14:52
 * @Filename:WxPayAutoCertificateConfig
 */
@Configuration
public class WxPayAutoCertificateConfig {

    @Resource
    private WxPayConfig wxPayConfig;

    /**
     * 初始化商户配置
     * @return
     */
    @Bean
    public RSAAutoCertificateConfig rsaAutoCertificateConfig() throws IOException {
        RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(wxPayConfig.getMerchantId())
                .privateKey(PemUtil.loadPrivateKeyFromPath(new ClassPathResource(wxPayConfig.getPrivateKey()).getURL().getPath()))
                .merchantSerialNumber(wxPayConfig.getMerchantSerialNumber())
                .apiV3Key(wxPayConfig.getApiV3Key())
                .build();
        return config;
    }
}
  1. 小程序支付下单
@Resource
private WxPayConfig wxPayConfig;

@Autowired
private RSAAutoCertificateConfig rsaAutoCertificateConfig;


/**
 * 小程序支付下单
 *
 * @param req
 * @return
 */
public PrepayWithRequestPaymentResponse createOrder(CreateOrderParam req) {
    String orderNo = req.getOutTradeNo();
    Integer totalFee = req.getTotalPrice();
    String openId = req.getOpenId();

    JsapiServiceExtension service =
            new JsapiServiceExtension.Builder()
                    .config(rsaAutoCertificateConfig)
                    // 不填默认为RSA
                    .signType("RSA")
                    .build();

    PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
    try {
        PrepayRequest request = new PrepayRequest();
        request.setAppid(wxPayConfig.getAppId());
        request.setMchid(wxPayConfig.getMerchantId());
        request.setDescription("描述");
        request.setOutTradeNo(orderNo);
        // 支付成功后的回调地址
        request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
        Amount amount = new Amount();
        amount.setTotal(totalFee);
        request.setAmount(amount);
        Payer payer = new Payer();
        payer.setOpenid(openId);
        request.setPayer(payer);
        log.info("请求预支付下单,请求参数:{}", JSONObject.toJSONString(request));
        // 调用预下单接口
        response = service.prepayWithRequestPayment(request);
        log.info("订单【{}】发起预支付成功,返回信息:{}", orderNo, response);
    } catch (HttpException e) { // 发送HTTP请求失败
        log.error("微信下单发送HTTP请求失败,错误信息:{}", e.getMessage());
    } catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
        log.error("微信下单服务状态错误,错误信息:{}", e.getMessage());
        throw new ServiceException("下单失败");
    } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
        log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
        throw new ServiceException("下单失败");
    }
    return response;
}
  1. 支付回调(通知)
@Resource
private OrdersMapper ordersMapper;

public String payNotify(HttpServletRequest request) {
    log.info("------收到支付通知------");
    // 请求头WeChat-Signature
    String signature = request.getHeader("Wechatpay-Signature");
    // 请求头WeChat-nonce
    String nonce = request.getHeader("Wechatpay-Nonce");
    // 请求头WeChat-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(ServletUtil.getBody(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", "失败");
    //todo 修改订单前,建议主动请求微信查询订单是否支付成功,防止恶意post  out_trade_no
    Orders orders = ordersMapper.selectOne(new LambdaQueryWrapper<Orders>().eq(Orders::getOrderNo, transaction.getOutTradeNo()));
    if (Objects.isNull(orders)) {
        log.error("订单不存在,非法调用");
        return JSONObject.toJSONString(returnMap);
    }

    //判断订单存在,但是不为待支付
    if (!orders.getOrderStatus().getKey().equals(UserOrderStatusEnum.PENDING.getKey())) {
        log.error("订单非待支付状态");
        return JSONObject.toJSONString(returnMap);
    }

    //获取支付状态
    Transaction.TradeStateEnum tradeState = transaction.getTradeState();

    //如果微信响应 非 支付成功,不做处理 直接返回
    if (!tradeState.equals(Transaction.TradeStateEnum.SUCCESS)) {
        log.error("订单未支付成功,状态为【{}】", tradeState);
        return JSONObject.toJSONString(returnMap);
    }

    // 如果微信响应支付成功,且当前状态为待支付,修改订单状态为已支付,并保存交易id TransactionId
    orders.setOrderStatus(UserOrderStatusEnum.PAID);
    orders.setPayTime(new Date());
    orders.setTransactionId(transaction.getTransactionId());

    //todo 需要去修改订单相关所有业务
    ordersMapper.updateById(orders);
    returnMap.put("code", "SUCCESS");
    returnMap.put("message", "成功");
    return JSONObject.toJSONString(returnMap);
}

小程序端代码(uniApp)

// 调用后端小程序下单接口获取支付参数
// 省略 ...

//调起支付
uni.requestPayment({
    "provider": "wxpay",//支付方式  
    "timeStamp": "1414561699",//时间戳
    "nonceStr":"5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
    "package":"prepay_id=wx201410272009395522657a690389285100",
    "signType":"RSA",//签名算法,需要与后台下单时一致
    "paySign":"oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==",签名
    success: function (resSuccess) {
        //支付成功后的操作
        //todo...                       
     },
                            
    fail: function (err) {
        //支付失败后的操作
        //todo...
    }
)

over …

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值