基于JAVA+Uniapp的微信小程序接入微信小程序支付案例(基于APIV3)

发布时间:2024年01月22日

微信支付文档

小程序接入(基于APIv3进行支付)

一:参数准备

需要的参数如下所示

wx:
  # 微信小程序appid
  app_id: xxx 
  # 微信小程序app_secret
  app_secret: xxx
  # 微信小程序获取openid地址
  session_key_url: https://api.weixin.qq.com/sns/jscode2session?appid=${wx.app_id}&secret=${wx.app_secret}&js_code=%s&grant_type=authorization_code
  pay:
    # 商户ID
    mch_id: xxx
    # API v3密钥
    api_V3_key: xxx
    # 证书序列号
    serial_number: xxx
    # 商户证书保存路径
    private_key_path: /xxx/xxx/apiclient_key.pem
    # 支付成功之后我方接受微信服务器通知地址,必须是Https
    notify_url: https://xxx/smart/wx/notify

参数获取地址:

小程序相关:https://mp.weixin.qq.com/cgi-bin/loginpage?t=wxm2-login&lang=zh_CN

商户参数相关:

文档地址

https://pay.weixin.qq.com/docs/merchant/development/development-preparation/parameter-application.html

证书获取地址

https://pay.weixin.qq.com/index.php/core/cert/api_cert#/

二:Java代码接入
引入依赖
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.2.12</version>
</dependency>

文档地址:

https://github.com/wechatpay-apiv3/wechatpay-java

微信支付相关代码
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 微信支付
 * @author: azhou
 * @create: 2024/1/20 14:28
 **/
@Component
@Slf4j
public class WxPayService {


    private RSAAutoCertificateConfig notificationConfig;

    @Value("${wx.session_key_url}")
    private String sessionKeyUrl;

    /**
     * 商户号
     */
    @Value("${wx.pay.mch_id}")
    public String merchantId;

    /**
     * 商户API私钥路径
     */
    @Value("${wx.pay.private_key_path}")
    public String privateKeyPath;

    /**
     * 商户证书序列号
     */
    @Value("${wx.pay.serial_number}")
    public String merchantSerialNumber;

    /**
     * 商户APIV3密钥
     */
    @Value("${wx.pay.api_V3_key}")
    public String apiV3Key;

    /**
     * 通知地址
     */
    @Value("${wx.pay.notify_url}")
    public String notifyUrl;

    /**
     * 小程序ID
     */
    @Value("${wx.app_id}")
    public String wxAppid;


    /**
     * 拉起微信小程序预付款信息
     *
     * @param orderNo    订单编号
     * @param totalPrice 支付的价格(字符串例如:25.66)
     * @param openId     微信openId
     * @return 预支付ID和openId的map
     */
    public Map<String, Object> wxSmartPay(String orderNo, String totalPrice, String openId) {
        Map<String, Object> map = new HashMap<>();
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(notificationConfig).build();
        PrepayRequest request = new PrepayRequest();
        request.setAppid(wxAppid);
        request.setMchid(merchantId);
        request.setDescription("商品描述");
        request.setOutTradeNo(orderNo);
        request.setNotifyUrl(notifyUrl);
        Amount amount = new Amount();
        amount.setTotal(convertToCents(totalPrice));
        amount.setCurrency("CNY");
        request.setAmount(amount);
        Payer payer = new Payer();
        payer.setOpenid(openId);
        request.setPayer(payer);
        log.info("小程序调用微信支付请求参数:{}", JSON.toJSONString(request));
        PrepayWithRequestPaymentResponse requestPaymentResponse = service.prepayWithRequestPayment(request);
        log.info("小程序调用微信支付响应结果:{}", JSON.toJSONString(requestPaymentResponse));
        // requestPaymentResponse对象中包含小程序中拉起微信支付的参数
        map.put("data", requestPaymentResponse);
        map.put("openId", openId);
        return map;
    }

 	/**
     * 接受微信通知
     *
     * @param requestParam 微信服务器请求过来的参数
     * @return 解密之后的参数对象
     */
    public Transaction wxNotify(RequestParam requestParam) {
        NotificationParser parser = new NotificationParser(notificationConfig);
        Transaction transaction;
        try {
            // 以支付通知回调为例,验签、解密并转换成 Transaction
            transaction = parser.parse(requestParam, Transaction.class);
        } catch (Exception e) {
            // 签名验证失败,返回 401 UNAUTHORIZED 状态码
            log.info("微信通知,签名校验失败", e);
            return null;
        }
        log.info("微信通知,解密结果:{}", JSON.toJSONString(transaction));
        return transaction;
    }


    /**
     * 转换价格
     *
     * @param totalPrice
     * @return
     */
    public static Integer convertToCents(String totalPrice) {
        BigDecimal bigDecimal = new BigDecimal(totalPrice);
        BigDecimal multipliedBy100 = bigDecimal.multiply(new BigDecimal(100));
        return multipliedBy100.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
    }

    /**
     * 获取微信openId
     *
     * @param code
     * @return
     */
    public String getOpenId(String code) {
        try {
            String res = HttpUtil.get(String.format(sessionKeyUrl, code));
            log.debug("获取WX_Session_Key结束,结果:{}", res);
            JSONObject jsonObject = JSON.parseObject(res);
            if (jsonObject.containsKey("openid")) return jsonObject.get("openid").toString();
            else return "";
        } catch (Exception e) {
            log.error("wxCode解密失败:code->{}", code);
            return "";
        }
    }


    /**
     * 初始化微信支付配置
     */
    @PostConstruct
    public void init() {
        // RSAAutoCertificateConfig对象为自动更新微信平台证书的对象,可发起微信支付调用,也可以接受通知进行解析
        notificationConfig = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
    }

}

注意:微信支付里面有两个证书,一个是商户证书,就是在商户平台中我们自己获取下载的证书,另外一个是平台证书,用来验证微信服务器通知过来的签名是否正确,两者不同,而平台证书只能通过调用微信官方的接口进行获取,因为我们用的是 wechatpay-java 微信官方推出的sdk,所以里面内置了自动获取平台证书,以及对平台证书的更新操作,所以我们不需要管平台证书。

核心业务处理代码

@Override
public boolean wxNotify(RequestParam requestParam) {
    // 获取微信通知之后的参数对象
    Transaction transaction = wxPayService.wxNotify(requestParam);
    // 是否成功支付
    boolean isPay = Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState());
    // 订单号
    String outTradeNo = transaction.getOutTradeNo();
    if (!isPay) log.info("微信通知,订单号:{},尚未支付!", outTradeNo);
    else {
        // 支付金额
        Integer payerTotal = transaction.getAmount().getPayerTotal();
        Order order = orderService.getByOrderNo(outTradeNo);
        if (order == null) log.info("微信通知,订单号:{} 查询结果为空!", outTradeNo);
        String payPrice = order.getPayPrice();
        Integer totalPrice = StringUtils.convertToCents(payPrice);
        if (!totalPrice.equals(payerTotal)) {
            log.info("微信通知,订单号:{} 支付金额不一致!", outTradeNo);
        } else {
            // 支付成功之后进行的操作
        }
    }
    return isPay;
}

接收微信通知接口

/**
 * 接受微信通知接口(只能是post请求)
 * @param request
 * @return
 */
@PostMapping("/wx/notify")
public ResponseEntity.BodyBuilder wxNotify(HttpServletRequest request) {
    String signature = request.getHeader("Wechatpay-Signature");
    String serial = request.getHeader("Wechatpay-Serial");
    String nonce = request.getHeader("Wechatpay-Nonce");
    String timestamp = request.getHeader("Wechatpay-Timestamp");
    String body = HttpUtils.readData(request);
    RequestParam requestParam = new RequestParam.Builder().serialNumber(serial)
        .nonce(nonce)
        .signature(signature)
        .timestamp(timestamp)
        .body(body)
        .build();
    boolean wxNotify = goodsCartService.wxNotify(requestParam);
    return ResponseEntity.status(wxNotify ? HttpStatus.OK : HttpStatus.INTERNAL_SERVER_ERROR);
}

其他

public static String readData(HttpServletRequest request) {
    BufferedReader br = null;
    try {
        StringBuilder result = new StringBuilder();
        br = request.getReader();
        for (String line; (line = br.readLine()) != null; ) {
            if (result.length() > 0) {
                result.append("\n");
            }
            result.append(line);
        }
        return result.toString();
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
三:微信小程序接入
小程序拉起微信支付
wxPay(param) {
    var that = this;
    //小程序发起微信支付
    wx.requestPayment({
        timeStamp: param.timeStamp, //记住,这边的timeStamp一定要是字符串类型的,不然会报错
        nonceStr: param.nonceStr,
        package: param.packageVal,
        signType: param.signType,
        paySign: param.paySign,
        success: function(event) {
            wx.showToast({
                title: '支付成功',
                icon: 'success',
                duration: 2000
            });
        },
        fail: function(error) {
            // fail
            console.log("支付失败")
            console.log(error)
        },

        complete: function() {
            // complete
            console.log("pay complete")
        }

    });
}
openid获取
wx.login({
    success(res) {
        console.log(res.code)
        if (res.code) {
            // res.code为上方WxPayService.getOpenId()中的code
            // 携带订单相关参数以及code进行相关请求
        } else {
            Util.showMyToast("系统有误,请联系管理员")
        }
    }
})
文章来源:https://blog.csdn.net/weixin_46228112/article/details/135748025
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。