赞
踩
小程序接入(基于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#/
<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") } }); }
wx.login({
success(res) {
console.log(res.code)
if (res.code) {
// res.code为上方WxPayService.getOpenId()中的code
// 携带订单相关参数以及code进行相关请求
} else {
Util.showMyToast("系统有误,请联系管理员")
}
}
})
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。