微信支付
场景:需要微信支付完成业务流程,首先商户申请了微信支付商户端的认证,然后我们就可以拿到关键的参数:mchId,mchKey,appid。
商户使用微信支付的条件
首先看一下微信的官方文档:微信支付文档
这里使用的是JSAPI支付方式。
JSAPI支付是指商户通过调用微信支付提供的JSAPI接口,在支付场景中调起微信支付模块完成收款。
商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。
1.配置项
首先是小程序的配置(yml文件中)
wx:
miniapp:
app-id: 小程序appid
app-secret: 小程序密钥
pay:
appId: id
mchId: mchid
mchKey: mchkey
notifyUrl: http://xxx.com/pay/notify 微信支付回调地址
接着把这些写成配置类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* <p>
*
* </p>
* 小程序配置类
* @since 2022/4/19
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMiniappProperties {
/**
* 设置微信小程序的appid.
*/
private String appId;
/**
* 设置微信小程序的Secret.
*/
private String appSecret;
}
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* Description:
*
* 微信支付配置类
* @date : 2022/4/14
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties {
/**
* 设置微信公众号或者小程序等的appid
*/
private String appId;
/**
* 微信支付商户号
*/
private String mchId;
/**
* 微信支付商户密钥
*/
private String mchKey;
/**
* 创建订单ip
*/
private String createIp;
/**
* 订单回调
*/
private String notifyUrl;
/**
* 支付类型
*/
private String tradeType = "JSAPI";
}
接着就是这些参数的配置
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* <p>
* 微信相关配置
* </p>
*
* @since 2021/4/19
*/
@Configuration
@AllArgsConstructor
public class WxConfig {
private final WxMiniappProperties miniappProperties;//这个是上面写的参数类
private final WxPayProperties wxPayProperties;//这个是上面写的参数类
/**
* 小程序service配置
*/
@Bean
public WxMaService wxMaService() {
WxMaDefaultConfigImpl wxMaConfig = new WxMaDefaultConfigImpl();
wxMaConfig.setAppid(this.miniappProperties.getAppId());
wxMaConfig.setSecret(this.miniappProperties.getAppSecret());
WxMaService wxMaService = new WxMaServiceImpl();
wxMaService.setWxMaConfig(wxMaConfig);
return wxMaService;
}
@Bean
public WxPayService wxService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.wxPayProperties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.wxPayProperties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.wxPayProperties.getMchKey()));
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
到此我们的微信配置就完成了。
2.微信支付核心逻辑
首先要弄清楚支付的逻辑
- 首先前端调用我们预提交接口:这个接口就是为前端准备一些页面数据,还调用微信
- 然后前端再调用我们的微信支付接口,拿到我们接口返回的数据之后,前端掉起支付页面。
- 至此用户支付,前端不需要进行其他操作。
- 然后微信会自动调用我们写的微信支付回调接口,去告诉微信是否成功。
- 这样就形成了一个闭环。
具体的流程图在下面。左边是用户操作流程,右边是对应的代码逻辑
从上面看到,我们需要一个订单预提交接口:这个接口的主要目的就是展示给用户看,他这个订单需要花多少钱,需要他填写一些电话,物流地址等信息;
主要是掉起微信支付的接口,在这个接口里面就是拿到参数,调用微信的jspa下单接口。
这只是一个核心方法,具体的业务逻辑需要自己写,自己根据需求设计
/**
* 执行微信统一下单
*
* @param orderNo 订单编号
* @param payPrice 支付金额
* @param openid 支付用户的openid
*/
private WxPayResultVO doWxPay(String orderNo, BigDecimal payPrice,String openid) {
Date now = new Date();
//订单开始时间
String startTime = DateUtil.format(now, DatePattern.PURE_DATETIME_PATTERN);
//订单关闭时间(1小时或者根据实际需求)
String expireTime = DateUtil.format(DateUtil.offsetHour(now, 1), DatePattern.PURE_DATETIME_PATTERN);
//这个接口只支持传分
Integer totalFee = BaseWxPayRequest.yuanToFen(payPrice.toString());
//支付用户的ip地址
String ip = InetAddress.getLoopbackAddress().getHostAddress();
//构建参数(这里的参数可以看接口文档对应的新增和删减,目前这个是都需要的几个字段)
WxPayUnifiedOrderRequest orderRequest = WxPayUnifiedOrderRequest
.newBuilder()
.outTradeNo(orderNo)
.body("测试商品")
.totalFee(totalFee)
.openid(user.getOpenid())
.spbillCreateIp(ip)
.tradeType(wxPayProperties.getTradeType())
.notifyUrl(wxPayProperties.getNotifyUrl())
.timeStart(startTime)
.timeExpire(expireTime)
.build();
// 调用微信jspa下单统一接口
try {
WxPayMpOrderResult result = this.wxPayService.createOrder(orderRequest);
log.info("微信支付返回参数: {}", result);
WxPayResultVO payResult = new WxPayResultVO();
BeanUtil.copyProperties(result, payResult);
return payResult;
} catch (WxPayException e) {
log.error("微信支付失败!订单号:{}, 错误码:{}, 原因:{}",
orderNo, e.getErrCode(), e.getMessage());
//这里需要抛出异常(抛出你的自定义异常)
throw new ApiException(2000,"微信支付失败");
}
}
微信支付的返回参数(前端拿到这些参数去吊起微信的支付页面)
import lombok.Data;
@Data
public class WxPayResultVO {
/**
* 时间戳
*/
private String timeStamp;
/**
* 随机字符串
*/
private String nonceStr;
/**
* prepay_id
*/
private String packageValue;
/**
* 签名加密类型
*/
private String signType;
/**
* 签名
*/
private String paySign;
}
下面就是微信的支付回调
/**
* 平台商品微信支付回调
*/
@PostMapping("/notify")
public String notify(@RequestBody String xmlData) throws WxPayException {
final WxPayOrderNotifyResult notifyResult = this.wxPayService.parseOrderNotifyResult(xmlData);
String orderNo = notifyResult.getOutTradeNo();
String totalFee = BaseWxPayResult.fenToYuan(notifyResult.getTotalFee());
log.error("===支付回调=== 订单:{}, 金额: {}", orderNo, totalFee);
//这里就是你的业务逻辑了
return this.paySerivice.notify(orderNo,totalFee);
}
具体方法
@Override
@Transactional(rollbackFor = Exception.class)
public String notify(String orderNo, String totalFee) {
//业务逻辑1
return WxPayNotifyResponse.fail("失败");
//业务逻辑2
return WxPayNotifyResponse.fail("失败");
//业务逻辑3
return WxPayNotifyResponse.success("成功");
}
这里面的业务逻辑就是你进行判断这个订单是否存在啊,这个订单是否出现其他问题,如果有问题返给微信失败;如果没有问题,就处理一下你的业务逻辑,返回成功给微信。
至此微信支付就结束了。
从上可以看到其实没有什么难的地方,主要是你的业务逻辑必须理清楚。因为涉及到支付,也就是涉及到钱,所以会给人一种畏惧感,但是这些是必须要学习的,必须要尝试的。