1.了解微信支付逻辑
说白了就是我们去调用微信官方提供的接口,但是这些接口有很多繁琐的参数,还需要计算签名等等一系列恶心的步骤,详情可以看微信官网,我们可以用微信官方提供的sdk,也可以用github上的三方sdk,本文以GitHub上大佬binaryWang的微信支付sdk演示微信支付的实现
前言
首先我们作为后端,需要像前端提供一个接口,前端通过调用这个接口唤起下单,在这个接口中,后端回去调用微信下单接口,这时候微信会给我们返一个预支付id,我们通过这个预支付的id,
这个预支付id是唤起下单的一个必填参数,咱们只需要计算出唤起支付所需要的参数,然后传给前端,前端就可以调用这个唤起支付接口,这样,输入密码的数字轮盘就出来了
注意事项:
sdk的版本可能会影响sign的计算,导致输入支付密码后,微信提示sign鉴权失败,这里的sdk大佬们都在维护的,只需要更新到最新版本即可
2.微信支付需要的参数
mchKey:新版本更新后叫做v2密钥,作用是处理签名信息解密,在微信支付商户平台申请,为32为密钥,自行设定,需要绑定商户的手机号进行短信验证和操作码验证
keyPath: classpath:wxcert/apiclient_cert.p12
#apiclient_key.pem证书文件的绝对路径或者以classpath:开头的类路径.
privateKeyPath: classpath:wxcert/apiclient_key.pem
#apiclient_cert.pem证书文件的绝对路径或者以classpath:开头的类路径.
privateCertPath: classpath:wxcert/apiclient_cert.pem
需要在api管理这里设置证书,在指引处可以下载证书
注:密钥,是开发者自己生成,生成后配置到商户平台即可
下载证书工具;下载后,双击“WXCertUtil.exe”文件,选择安装路径后,点击申请证书
也可通过以下链接下载证书工具:
windows版本 :https://wx.gtimg.com/mch/files/WXCertUtil.exe
mac版本 :https://wx.gtimg.com/mch/files/WXCertUtil.dmg
3.实现微信支付
导入依赖,并配置wx所需参数
<!--微信支付-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.6.0</version>
</dependency>
订单支付
/**
* 订单微信支付
* @param paymentForm
* @return
*/
@Override
public <T> T payOrder(OrderPaymentForm paymentForm,Long memberId) {
//获取订单号
String orderSn = paymentForm.getOrderSn();
//通过该订单号查询订单对象
Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderCode, orderSn));
log.info("appid---"+paymentForm.getAppId());
Assert.isTrue(order!=null,"该订单信息不存在");
Assert.isTrue(OrderStatusEnum.UNPAID.getValue().equals(order.getState()), "订单不可支付,请检查订单状态");
RLock lock = redissonClient.getLock(OrderConstants.ORDER_LOCK_PREFIX + order.getOrderCode());
try {
lock.lock();
T result;
result = (T) wxJsapiPay(paymentForm.getAppId(), order.getOrderCode(), order.getAmount(),memberId);
return result;
} finally {
//释放锁
if (lock.isLocked()) {
lock.unlock();
}
}
}
掉起订单支付
/**
* 微信支付调起
*
* @param appId 微信小程序ID
* @param orderSn 订单编号
* @param paymentAmount 支付金额
* @return 微信支付调起参数
*/
private WxPayUnifiedOrderV3Result.JsapiResult wxJsapiPay(String appId, String orderSn, BigDecimal paymentAmount,Long memberId) {
//价格转换为分
Long longValueAmount = BigDecimal2Long(paymentAmount);
//获取用户OpenId
String memberOpenId = memberService.getById(memberId).getOpenid();
WxPayUnifiedOrderV3Request wxRequest = new WxPayUnifiedOrderV3Request()
.setAppid(appId)//小程序appId
.setOutTradeNo(orderSn)//设置订单号
.setAmount(new WxPayUnifiedOrderV3Request
.Amount()
.setTotal(Math.toIntExact(longValueAmount)).
setCurrency(wxPayProperties.getCurrency())
)//设置金额
.setPayer(
new WxPayUnifiedOrderV3Request.Payer()
.setOpenid(memberOpenId)
)//设置付款人
.setDescription("赅买-订单编号:" + orderSn)
.setNotifyUrl(wxPayProperties.getPayNotifyUrl());
WxPayUnifiedOrderV3Result.JsapiResult jsapiResult;
try {
jsapiResult = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, wxRequest);
} catch (WxPayException e) {
log.error(e.getMessage(), e);
throw new BizException(e.getMessage()+"微信统一下单异常");
}
log.info("----------------------"+jsapiResult);
return jsapiResult;
}
返回值
实现效果
在用户输入密码成功后,这块微信就支付成功了,这时候我们需要暴露给微信一个开发的回调接口,微信这边会传入一些加密参数,我们拿到参数后调用api将这些参数解密,最后可以对订单进行一些信息的校验以及状态的更新等操作
4.实现微信退款
具体方式与支付差不多,但这块调用微信退款后,前端只需掉我们的接口,会自动完成退款,我们需要按照微信官方的返回规范,退款成功sucsess,更新订单信息
注:如果微信这块没有接收到返回成功的信息,还会按照一定策略(频繁度从高到底)不断调用我们提供的回调接口,具体调用方式请看微信小程序官方退款接口文档
退款--入参(订单编号,退款金额,退款原因)
/**
* 退款
*/
public <T> T refundOrder(RefundForm refundForm){
Assert.isTrue(refundForm.getReason()!=null,"退款备注不能为空");
//判断该订单是否未付款,或已取消
//获取商户订单号
String orderCode = refundForm.getOrderCode();
//转金额
Long orderRundAmount = BigDecimal2Long(refundForm.getRefundAmount());
//获取订单信息
Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderCode, orderCode));
//获取微信退款单号
String wxPayCode = order.getWxPayCode();
//订单金额
Long orderAmount = BigDecimal2Long(order.getAmount());
Assert.isTrue(order !=null,"查询不到该订单信息,无法退款");
Assert.isTrue(order.getState()==OrderStatusEnum.PAID.getValue() || order.getState()==OrderStatusEnum.REBATES.getValue(),"该订单尚未完成支付或已取消,无法退款");
Assert.isTrue(orderRundAmount.intValue()>0,"退款金额必须大于0");
Assert.isTrue(orderRundAmount<=orderAmount,"退款金额必须小于等于订单金额");
OrderRefundForm.Amount amount = OrderRefundForm.Amount.builder().refund(refundForm.getRefundAmount())//退款金额
.total(order.getAmount()).build(); //订单金额
OrderRefundForm orderRefundForm = OrderRefundForm.builder().amount(amount)
.out_refund_no(generateTradeNo(SecurityUtils.getUserId())) //退款单号
.reason(refundForm.getReason()==null?"":refundForm.getReason())
.transaction_id(wxPayCode)
.build();
T result = (T) wxJsapiRefund(orderRefundForm);
return result;
}
掉起退款
/**
* 订单退款掉起
* @param orderRefundForm 退款请求对象 memberId
* @return
*/
private WxPayRefundV3Result wxJsapiRefund(OrderRefundForm orderRefundForm) {
//退款金额
Long longValueRefundAmount = BigDecimal2Long(orderRefundForm.getAmount().getRefund());
//订单总金额
Long longValueOrderAmount = BigDecimal2Long(orderRefundForm.getAmount().getTotal());
//通过订单号查询商品id
Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getWxPayCode, orderRefundForm.getTransaction_id()));
appOrderMapper.updateOrderRefundReason(orderRefundForm.getReason(),order.getId()); // 更新退款原因
//如果是自动退款
WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request().
setTransactionId(orderRefundForm.getTransaction_id()).
setAmount(new WxPayRefundV3Request.Amount().
setTotal(Math.toIntExact(longValueOrderAmount))
.setRefund(Math.toIntExact(longValueRefundAmount))
.setCurrency(wxPayProperties.getCurrency())).
setOutRefundNo(orderRefundForm.getOut_refund_no()).
setNotifyUrl(wxPayProperties.getRefundNotifyUrl()).
setReason(orderRefundForm.getReason()==null?"":orderRefundForm.getReason());
WxPayRefundV3Result refundV3Result;
try {
refundV3Result = wxPayService.refundV3(wxPayRefundV3Request);
log.info("掉起退款接口成功");
} catch (WxPayException e) {
throw new BizException("退款失败"+e);
}
return refundV3Result;
}
退款也是一样的,需要暴露回调接口,退款成功后,微信会退款给商户,并且我们在回调接口处理订单状态等信息
实现效果:
总结:
微信支付的下单和调起需要前后端的配合与联调,我们需要根据业务需求灵活的处理更新订单的信息,再支付完成之后,在回调接口中校验支付金额与订单金额是否相同等信息,校验通过后才更新订单为已付款状态,,现在我们就实现类微信支付与退款功能了,本文没有写到回调接口的书写,如有需要可留言