微信支付#
框架为springboot,需要在小程序的后端服务中,使用接微信支付;
在网上选择了很多的技术选型,发现如果是自己封装,一个是太繁琐,还有就是不方便维护,发现一个比较好的springboot包。
官网地址:‘Document (notfound403.github.io)’
Spring boot依赖包#
pom引用#
<dependency>
<groupId>cn.felord</groupId>
<artifactId>payment-spring-boot-starter</artifactId>
<version>1.0.11.RELEASE</version>
</dependency>
导入之后,可以直接引用
但是建议直接用别的依赖,如果人家停止维护了,还得重新搞一遍,可以学习下人家的源码,自己封装了下,对接微信支付V3的大部分SDK了
源码地址#
GitHub:GitHub - NotFound403/payment-spring-boot: 微信支付V3,微信优惠券,代金券、公众号支付、微信小程序支付、分账、支付分、商家券
封装后引用与直接引用效果是一样的
实现:#
1. 预置条件#
1.申请商户号
2.绑定小程序与商户号
3.在商户号里,生成V3的密钥(V1,V2不要用了,太老了)
4.下载p12证书,并导入到项目中`
P12证书导入,编译可能不会成功,需要在pom里面加下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions> <execution> <id>copy-resources</id>
<phase>package</phase>
<goals> <goal>copy-resources</goal>
</goals> <configuration> <outputDirectory>${project.build.directory}/maven-archiver/resources</outputDirectory>
<resources> <resource> <directory>/src/main/resources/profile-${profile.active}</directory>
<include>application.yml</include>
<include>application-${profile.active}.yml</include>
<include>log4j2-${profile.active}.xml</include>
<include>bootstrap-${profile.active}.properties</include>
<filtering>true</filtering>
</resource> </resources> </configuration> </execution> </executions> <configuration> <encoding>UTF-8</encoding>
<!-- 过滤后缀为pem、pfx的证书文件 -->
<nonFilteredFileExtensions>
<nonFilteredFileExtension>pem</nonFilteredFileExtension>
<nonFilteredFileExtension>pfx</nonFilteredFileExtension>
<nonFilteredFileExtension>p12</nonFilteredFileExtension>
</nonFilteredFileExtensions> </configuration></plugin>
|
2. 添加到工程的yaml配置#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| wechat:
applets: # 小程序的配置项
openidUrl: https://api.weixin.qq.com/sns/jscode2session
appid: appid
secret: secret
pay: #微信支付配置项
v3:
# 微信小程序租户标识为 applets applets:
# 应用appId 必填
app-id: appid
# api v3 密钥 必填
app-v3-secret: v3secret
# 微信支付商户号 必填
mch-id: 1234567
# 商户服务器域名 用于回调 回调路径为 domain + notifyUrl 需要放开回调接口的安全策略 必填
domain: https://xxx/app
# 商户 api 证书路径 必填 填写classpath路径 位于 maven项目的resources文件下
cert-path: apiclient_cert.p12
|
3. 支付#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| private final WechatApiProvider wechatApiProvider;
OrderInfo orderInfo=XXx
String ipAddr = getLocalIp();
// 定义附加数据
Map<String, Object> attach = new HashMap<>();
attach.put("userId", orderInfo.getUserId());
attach.put("leaveComment", orderInfo.getLeaveComment());
PayParams payParams = new PayParams();
// 商品描述,必填
payParams.setDescription(DESC);
// 商户侧唯一订单号 建议为商户侧支付订单号 订单表主键 或者唯一标识字段
payParams.setOutTradeNo(orderInfo.getOrderId().toString());
payParams.setAttach(JSONObject.toJSONString(attach));
// 需要定义回调通知
payParams.setNotifyUrl(NOTIFY_URL);
Amount amount = new Amount();
//付款金额单位为“分”
amount.setTotal(orderInfo.getTotalPrice().intValue());
payParams.setAmount(amount);
// 此类支付Payer必传,且openid需要同appid有绑定关系 具体去看文档
Payer payer = new Payer();
payer.setOpenid(openId);
payParams.setPayer(payer);
//支付场景描述
SceneInfo sceneInfo = new SceneInfo();
//用户终端IP
sceneInfo.setPayerClientIp(ipAddr);
if (orderInfo.getChargePileId() != null) {
sceneInfo.setDeviceId(orderInfo.getChargePileId().toString());
}
payParams.setSceneInfo(sceneInfo);
wechatApiProvider.directPayApi(TENANT_ID).jsPay(payParams).getBody();
|
4.支付回调:#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| wechatApiProvider.callback(TENANT_ID).transactionCallback(params, data -> {
Long orderId = Long.valueOf(data.getOutTradeNo());
OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
if (orderInfo == null) {
log.error("订单不存在,回调异常");
}
if (UNPAID.getCode() != orderInfo.getStatus()) {
log.error("订单状态为[{}],非未支付,回调异常", orderInfo.getStatus());
}
orderService.updateOrderStatus(orderId, PAID, OrderInfoVO.builder().payMethod(WXPAY.getCode()).build());
log.info("订单状态刷新:orderId={}", orderId);
PayRecord payRecord = new PayRecord();
payRecord.setPayMerchantid(data.getMchid());
payRecord.setPayPrice(data.getAmount().getTotal());
payRecord.setOrderId(orderId);
payRecord.setType(1);
saveRecord(payRecord, 1);
log.info("支付记录保存:{}", payRecord);
});
|
5.退款:#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| RefundParams refundParams = new RefundParams();
RefundParams.RefundAmount amount = new RefundParams.RefundAmount();
amount.setRefund(paymentRefund.getRefundAmount());
amount.setTotal(paymentRefund.getActualAmount());
refundParams.setAmount(amount);
refundParams.setOutRefundNo(REFUND + paymentRefund.getOutTradeNo());
refundParams.setNotifyUrl(NOTIFY_REFUND_URL);
refundParams.setOutTradeNo(paymentRefund.getOutTradeNo());
refundParams.setReason(paymentRefund.getReason());
List<RefundGoodsDetail> goodsDetail = new ArrayList<>();
RefundGoodsDetail detail = new RefundGoodsDetail();
detail.setMerchantGoodsId(paymentRefund.getMerchantGoodsId());
detail.setGoodsName(paymentRefund.getGoodsName());
//数量写死1,商品单价金额等于总退款金额
detail.setUnitPrice(paymentRefund.getRefundAmount());
detail.setRefundQuantity(1);
detail.setRefundAmount(paymentRefund.getRefundAmount());
goodsDetail.add(detail);
refundParams.setGoodsDetail(goodsDetail);
wechatApiProvider.directPayApi(TENANT_ID).refund(refundParams).getBody();
|
6.退款回调:#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| wechatApiProvider.callback(TENANT_ID).refundCallback(params, data -> {
log.info("退款回调:data={}", data);
Long orderId = Long.valueOf(data.getOutTradeNo());
OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
if (orderInfo == null) {
log.error("订单[{}]不存在,退款回调异常", orderId);
}
if (PAID.getCode() != orderInfo.getStatus()) {
log.error("订单[{}]状态为[{}],非已支付,请检查订单状态是否准确", orderId, orderInfo.getStatus());
return; }
orderService.updateOrderStatus(orderId, Constant.OrderStatus.REFUNDED, OrderInfoVO.builder().payMethod(WXPAY.getCode()).build());
log.info("订单状态刷新:orderId={}", orderId);
PayRecord payRecord = new PayRecord();
payRecord.setPayMerchantid(data.getMchid());
payRecord.setPayPrice(data.getAmount().getPayerRefund());
payRecord.setOrderId(orderId);
payRecord.setType(2);
saveRecord(payRecord, 2);
log.info("支付记录保存:{}", payRecord);
});
|
如上包含了基本支付的一套流程了,还有其他如账单下载,订单查询,关闭订单,积分支付,合并支付等复杂的接口,可以自己研究,封装类里面都已经包含了