背景介绍
最近第一次接触到微信支付,踩了很多坑,赶紧写下来,全是心酸和眼泪。话不多说。开始展示。
项目中使用的接入方式是JSAPI(参考 产品中心 - 微信支付商户平台),当然也可以使用其他接入方式,根据需求来。
微信支付提供了两种对接方式(SDK,工具 | 微信支付商户平台文档中心)。一种是SDK,一种是Client,我个人偏向于SDK,因为它更简单,基本都封装好了。
项目中我们选择的非官方SDK binarywang,没什么别的原因,主要是更方便。
官网:Binary Wang;
GitHub:GitHub - binarywang/weixin-java-pay-demo: 基于Spring Boot 和 WxJava 实现的微信支付Java后端Demo
一、接入前准备
接入前需要创建账号等步骤,可以参考官方文档:JSAPI支付-接入前准备 | 微信支付商户平台文档中心x
二、引入依赖
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
</dependency>
三、编写配置文件
开发环境下可以把秘钥存到本地,线上建议放到服务器。
因为支付会有回调和退款等操作,本地接口不能被访问。建议使用内网穿透工具进行测试(Sunny-Ngrok内网转发内网穿透 - 国内内网映射服务器)
wechat-pay:
#商户id
mchId: 111111
#公众号appid
appid: wxce9662d47d419qwvw
#APIv3密钥
apiV3key: oLV98ZUF1Kdsfwefgwq7nCazqpew
#商户证书序列号
serialNo: 234235r598C803FersvdvB380A4F9CA77C8538AE7F
#商户私钥
privateKeyPath: E:/Project/devOps/key/apiclient_key.pem
privateCertPath: E:/Project/devOps/key/apiclient_cert.pem
#接收结果通知地址
notifyUrl: http://howfhu.frfe.idcwfwgye.com/notify/pay/wx
#退款通知地址
refundNotifyUrl: http://hu.free.idefengye.com/notify/refund/wx
四、数据库设计
订单表
退款表 支付单表
订单与支付单和退款单关系为一对多 不同用户扫码会产生多个支付记录,同样退款有多个退款记录。业务代码需要做支付幂等,同一笔不允许重复支付,重复支付需要调用退款接口。
五、业务代码
1.控制层
@Api(tags = "订单管理")
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@Operation(summary = "提交订单")
@PostMapping("/order/create")
public BizResult<String> createOrder(@Valid @RequestBody OrderCreateReq req) {
String orderNO = orderService.createOrder(req);
return BizResult.create(orderNO);
}
@Operation(summary = "订单详情")
@GetMapping("/order/detail")
public BizResult<OrderRsp> orderDetail(@RequestParam String orderNo) {
OrderRsp orderRsp = orderService.orderDetail(orderNo);
return BizResult.create(orderRsp);
}
@Operation(summary = "轮询订单")
@GetMapping("/order/polling")
public BizResult<Boolean> pollingOrder(@RequestParam String orderNo) {
Boolean result = orderService.pollingOrder(orderNo);
return BizResult.create(result);
}
@Operation(summary = "订单列表")
@PostMapping("/order/list")
public BizResult<PageData<OrderListRsp>> queryOrderList(@RequestBody OrderListReq req) {
req.check();
PageData<OrderListRsp> pageData = orderService.queryOrderList(req);
return BizResult.create(pageData);
}
}
2.业务层
@Slf4j
@Service
public class OrderService {
/**
* 创建订单
*/
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public String createOrder(OrderCreateReq req)
//判断用户是否短时间多次提交
// 加锁 RLock rLock
try {
//查询商品是否存在 不存在处理异常
OrderDO orderDO = OrderConverter.init(req, traceCodeGoodsDO, merchantDO, orderTypeEnum);
orderMapper.insert(orderDO);
return orderDO.getOrderNo();
} finally {
if (rLock.isHeldByCurrentThread()) {
rLock.unlock();
}
}
}
/**
* 查询订单详情
*/
public OrderRsp orderDetail(String orderNo) {
if (StringUtils.isBlank(orderNo)) {
throw BizException.create(BaseErrorCodeEnum.PARAMS_ERROR, "订单号不存在");
}
//查询订单
OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
if (Objects.isNull(orderDO)) {
throw BizException.create(BizErrorCodeEnum.ORDER_NOT_EXITS);
}
return OrderConverter.toOrderRsp(orderDO);
}
/**
* 订单轮询 前端轮询这个接口 判断当前订单状态 进行响应状态修改
*/
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public Boolean pollingOrder(String orderNo) {
if (StringUtils.isBlank(orderNo)) {
throw BizException.create(BaseErrorCodeEnum.PARAMS_ERROR, "订单号不存在");
}
OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
if (Objects.isNull(orderDO)) {
throw BizException.create(BizErrorCodeEnum.ORDER_NOT_EXITS);
}
//状态支付中 将状态改为未支付
if (Objects.equals(orderDO.getStatus(), OrderStatusEnum.PAYING.getCode())) {
orderMapper.updateStatus(orderNo, OrderStatusEnum.UNPAID.getCode(), orderDO.getStatus());
return false;
}
//未支付
if (Objects.equals(orderDO.getStatus(), OrderStatusEnum.UNPAID.getCode())) {
return false;
}
String payNo = orderDO.getPayNo();
if (StringUtils.isBlank(payNo)) {
return false;
}
//查询支付单是否存在
PaymentDO paymentDO = paymentMapper.findByPayNo(payNo);
if (Objects.isNull(paymentDO)) {
log.error("轮询订单,支付单不存在。orderNo:{},payNo:{}", orderDO, payNo);
return false;
}
if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.UNPAID.getCode()) && !Objects.equals(orderDO.getStatus(), OrderStatusEnum.PAYING.getCode())) {
// 查询需要取消支付单记录
List<PaymentDO> paymentList = paymentMapper.queryPaymentList(orderDO.getOrderNo(), PayStatusEnum.UNPAID.getCode(), paymentDO.getPayNo());
if (ObjectUtil.isNotEmpty(paymentList)) {
// 修改支付单取消状态
paymentMapper.cancelPay(orderDO.getOrderNo(), PayStatusEnum.CANCEL.getCode(), paymentDO.getPayNo(), PayStatusEnum.UNPAID.getCode());
}
return true;
}
PayStatusEnum payStatusEnum = LocalEnumUtils.findByCodeWithoutDefault(PayStatusEnum.class, paymentDO.getStatus());
if (Objects.equals(payStatusEnum, PayStatusEnum.CANCEL)
|| Objects.equals(payStatusEnum, PayStatusEnum.FAIL)) {
//取消支付或支付失败,可重新扫码创建支付单
return false;
} else if (Objects.equals(payStatusEnum, PayStatusEnum.UNPAID)) {
//支付单待支付,查询第三方渠道
WxPayOrderQueryV3Result wxPayOrderQueryV3Result = null;
try {
WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request();
request.setOutTradeNo(paymentDO.getPayNo());
wxPayOrderQueryV3Result = wxManager.getWxPayService().queryOrderV3(request);
} catch (WxPayException e) {
log.error("查询微信支付订单失败。payNo:{}", paymentDO.getPayNo(), e);
return false;
}
//查询第三方支付信息并更新订单
if (orderManager.queryOrderByWx(wxPayOrderQueryV3Result, orderDO, paymentDO, payStatusEnum)) {
producerHelper.sendOrderMsg(orderDO);
return true;
}
} else {
//其他状态,更新订单状态。
int orgOrderStatus = orderDO.getStatus();
OrderStatusEnum tarOrderStatus = OrderStatusEnum.PAID;
if (Objects.equals(payStatusEnum, PayStatusEnum.REFUND)) {
tarOrderStatus = OrderStatusEnum.REFUND;
orderDO.setRefundTime(paymentDO.getRefundTime());
orderDO.setRefundAmount(paymentDO.getRefundAmount());
}
orderDO.setFinishTime(paymentDO.getPayTime());
orderDO.setStatus(tarOrderStatus.getCode());
orderDO.setUpdateTime(new Date());
int i = orderMapper.updateStatusByOrderNo(orderDO, orgOrderStatus);
if (i > 0) {
producerHelper.sendOrderMsg(orderDO);
}
return true;
}
return false;
}
/**
* jsapi下单并返回前端唤起参数
*/
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public OrderJsApiRsq jsapiPay(JsAPIReq req) {
String orderNo = req.getOrderNo();
String openId = req.getOpenid();
// 加锁 RLock lock
if (lock == null) {
throw BizException.create(CommonErrorCodeEnum.RESUBMIT_ERROR, "订单支付中");
}
//查询订单
OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
if (Objects.isNull(orderDO)) {
throw BizException.create(BizErrorCodeEnum.ORDER_NOT_EXITS);
}
Integer orgStatus = orderDO.getStatus();
//校验重复支付
if (Objects.equals(orgStatus, OrderStatusEnum.PAID.getCode())) {
throw BizException.create(BizErrorCodeEnum.ORDER_PAY, "订单已支付,请勿重复支付");
}
// 查询支付单,没有则新增
// 对比openid,如果一样直接发起支付,不一样新增支付单
PaymentDO paymentDO = paymentMapper.findByPayNo(orderDO.getPayNo());
if (Objects.isNull(paymentDO) || !Objects.equals(paymentDO.getOpenid(), req.getOpenid())) {
//插入支付单数据
paymentMapper.insert(paymentDO);
//更新订单号中支付单号
// 幂等更新
int i = orderMapper.updateStatusByOrderNo(orderDO, orgStatus);
if (i == 0) {
throw BizException.create(BizErrorCodeEnum.ORDER_PAY, "订单已支付");
}
}
WxPayUnifiedOrderV3Request request = WxPayConverter.toWxPayUnifiedOrderV3Request(orderDO, paymentDO.getPayNo(), openId);
WxPayUnifiedOrderV3Result.JsapiResult result = null;
try {
result = wxManager.getWxPayService().createOrderV3(TradeTypeEnum.JSAPI, request);
} catch (WxPayException e) {
log.error("JSAPI下单支付异常,orderNo:{}", orderNo, e);
throw BizException.create(BizErrorCodeEnum.ORDER_UNIFIED_ERROR, "支付唤起失败");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return WxPayConverter.toOrderJsApiRsq(result);
}
/**
* 支付通知
*/
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public OrderPayNotify payNotify(JSONObject jsonObject, HttpServletRequest request, HttpServletResponse response) {
log.info("微信支付结果通知:{}", jsonObject);
//获取请求头
SignatureHeader header = PayUtil.getWXRequestHeader(request);
//请求体
//解析回调通知
WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = getWxPayOrderNotifyV3Result(jsonObject, header);
WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
WxPayTradeStateEnum wxPayStatus = WxPayTradeStateEnum.findByCode(result.getTradeState());
//中间态,直接返回,继续通知
if (Objects.equals(wxPayStatus, WxPayTradeStateEnum.NOT_PAY)
|| Objects.equals(wxPayStatus, WxPayTradeStateEnum.ACCEPT)
|| Objects.equals(wxPayStatus, WxPayTradeStateEnum.USER_PAYING)) {
log.error("微信支付回调,中间态继续通知,payNo:{}", wxPayOrderNotifyV3Result.getResult().getOutTradeNo());
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
return OrderPayNotify.fail();
}
//加锁RLock lock
if (lock == null) {
throw BizException.create(BizErrorCodeEnum.PAY_FAIL);
}
try {
//查询支付单
PaymentDO paymentDO
log.info("支付单结果解析:{},支付单号{}", paymentDO, paymentDO.getPayNo());
//查询订单
OrderDO orderDO
log.info("订单结果解析:{},支付单号{}", orderDO, orderDO.getOrderNo());
Integer status = paymentDO.getStatus();
//订单已支付 订单状态不等于未支付和支付中
if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.UNPAID.getCode())) {
if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.PAYING.getCode())) {
//订单已支付 判断是否同一支付单, 是则表示同一笔订单重复支付
if (!Objects.equals(orderDO.getPayNo(), paymentDO.getPayNo())) {
//支付通知状态
RefundDO refundDO = null;
if (Objects.equals(wxPayStatus, WxPayTradeStateEnum.SUCCESS)) {
//重复支付,新增退款记录
refundDO = RefundConverter.init(paymentDO);
refundMapper.insert(refundDO);
paymentDO.setRefundTime(refundDO.getRefundTime());
paymentDO.setRefundAmount(refundDO.getRefundAmount());
//申请退款
WxPayRefundV3Request wxPayRefundV3Request = WxPayConverter.toWxPayRefundV3(refundDO);
wxPayRefundV3Request.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());
WxPayRefundV3Result wxPayRefundV3Result = wxManager.getWxPayRefundService().refundV3(wxPayRefundV3Request);
log.info("申请退款结果解析:{}", wxPayRefundV3Result);
}
//更新支付单
paymentDO.setStatus(PayStatusEnum.REFUNDING.getCode());
paymentMapper.updatePayStatus(paymentDO, status);
response.setStatus(HttpStatus.SC_OK);
return OrderPayNotify.success();
}
response.setStatus(HttpStatus.SC_OK);
return OrderPayNotify.success();
}
}
//未支付订单
//更新订单和支付单状态
if (orderManager.payNotify(result, orderDO, paymentDO, wxPayStatus)) {
//生成码包
producerHelper.sendOrderMsg(orderDO);
}
response.setStatus(HttpStatus.SC_OK);
return OrderPayNotify.success();
} catch (Exception e) {
log.error("微信回调结果异常,第三方流水号{},异常支付单号{}", result.getTransactionId(), wxPayOrderNotifyV3Result.getResult().getOutTradeNo(), e);
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
return OrderPayNotify.fail();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 退款通知
*/
@Transactional(rollbackFor = Exception.class)
public OrderPayNotify payRefundNotify(JSONObject jsonObject, HttpServletRequest request, HttpServletResponse response) {
//获取请求头
SignatureHeader header = PayUtil.getWXRequestHeader(request);
//解析回调
WxPayRefundNotifyV3Result wxPayRefundNotifyV3Result = getWxPayRefundNotifyV3Result(jsonObject, header);
WxPayRefundNotifyV3Result.DecryptNotifyResult result = wxPayRefundNotifyV3Result.getResult();
log.info("微信退款结果通知:{}", result);
WxPayRefundStateEnum wxPayRefundStateEnum = WxPayRefundStateEnum.findByCode(result.getRefundStatus());
//退款关闭或者退款异常,直接返回,继续通知
if (Objects.equals(wxPayRefundStateEnum, WxPayRefundStateEnum.CLOSED) || Objects.equals(wxPayRefundStateEnum, WxPayRefundStateEnum.ABNORMAL)
) {
log.error("微信退款异常回调,退款单号,refundNo:{}", result.getOutRefundNo());
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
return OrderPayNotify.fail();
}
//加锁 RLock lock
if (lock == null) {
throw BizException.create(BizErrorCodeEnum.REFUND_NOTIFY_FAIL);
}
try {
//查询退款单
RefundDO refundDo
log.info("微信退款单解析:{},退款单号", refundDo, refundDo.getRefundNo());
//查询支付单
PaymentDO payment
log.info("支付单结果解析:{},支付单号{}", payment, payment.getPayNo());
//查询订单
OrderDO order
log.info("订单结果解析:{},订单号{}", order, order.getOrderNo());
Integer paymentStatus = payment.getStatus();
Integer orderStatus = order.getStatus();
Integer refundStatus = refundDo.getStatus();
//退款单状态退款中 并且退款通知成功 更新支付单和退款单
if (Objects.equals(refundStatus, RefundStateEnum.REFUNDING.getCode())) {
//更新退款单
if (!Objects.equals(refundStatus, RefundStateEnum.SUCCESS.getCode())) {
refundDo = RefundConverter.toRefund(wxPayRefundNotifyV3Result, refundDo);
int r = refundMapper.updateStatus(refundDo, refundStatus);
if (r == 0) {
throw BizException.create(BizErrorCodeEnum.REFUND_UPDATE_FAIL, "退款单更新失败");
}
}
//更新支付单
payment.setStatus(PayStatusEnum.REFUND.getCode());
payment.setRefundTime(DateUtil.parse(result.getSuccessTime()));
BigDecimal refundAmount = LocalMoneyUtils.fen2yuan(Long.valueOf(result.getAmount().getRefund()));
payment.setPayAmount(refundAmount);
int p = paymentMapper.updatePayStatus(payment, paymentStatus);
if (p == 0) {
throw BizException.create(BizErrorCodeEnum.PAYMENT_UPDATE_FAIL, "支付单状态更新失败");
}
response.setStatus(HttpStatus.SC_OK);
return OrderPayNotify.success();
}
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
return OrderPayNotify.fail();
} catch (Exception e) {
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
return OrderPayNotify.fail();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
/**
* 订单列表
*/
public PageData<OrderListRsp> queryOrderList(OrderListReq req, MerchantContext loginUser) {
Long count = orderMapper.orderTotal(req, loginUser.getMerchantId());
if (count <= 0) {
return PageData.create(Lists.newArrayList(), count, req.getPage(), req.getSize());
}
List<OrderDO> orderList = orderMapper.queryOrderList(req, loginUser.getMerchantId());
List<OrderListRsp> OrderRsp = OrderConverter.toOrderListReq(orderList);
return PageData.create(OrderRsp, count, req.getPage(), req.getSize());
}
/**
* 关闭订单
*/
@Transactional(rollbackFor = Exception.class)
public boolean closeOrder(String orderNo) throws WxPayException {
//查询订单是否已支付
OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
if (!OrderStatusEnum.UNPAID.getCode().equals(orderDO.getStatus())) {
return false;
}
Date expireTime = orderDO.getExpireTime();
Date date = new Date();
if (expireTime.compareTo(date) > 0) {
//时间未到
return false;
}
log.info("订单超期关闭,单号为{}", orderDO.getOrderNo());
orderDO.setCloseTime(date);
orderDO.setUpdateTime(date);
orderDO.setStatus(OrderStatusEnum.CLOSED.getCode());
int i = orderMapper.updateStatusByOrderNo(orderDO, OrderStatusEnum.UNPAID.getCode());
if (i == 0) {
//订单状态以改变
return false;
}
//关闭支付单
PaymentDO paymentDO = paymentMapper.findByPayNo(orderDO.getPayNo());
if (Objects.isNull(paymentDO)) {
return false;
}
//更新支付单
Integer payStatus = paymentDO.getStatus();
paymentDO.setStatus(PayStatusEnum.CANCEL.getCode());
paymentDO.setUpdateTime(date);
paymentMapper.updatePayStatus(paymentDO, payStatus);
//第三方渠道关单
WxPayOrderCloseV3Request request = new WxPayOrderCloseV3Request();
request.setOutTradeNo(paymentDO.getPayNo());
wxManager.getWxPayService().closeOrderV3(request);
return true;
}
/**
* 解析微信支付回调通知
*/
private WxPayOrderNotifyV3Result getWxPayOrderNotifyV3Result(JSONObject jsonObject, SignatureHeader header) {
WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = null;
try {
wxPayOrderNotifyV3Result = wxManager.getWxPayService().parseOrderNotifyV3Result(jsonObject.toJSONString(), header);
log.info("微信支付结果解析:{}", wxPayOrderNotifyV3Result.getResult());
} catch (Exception e) {
log.info("微信支付结果异常解析:{},异常信息{}", wxPayOrderNotifyV3Result.getResult(), e);
}
return wxPayOrderNotifyV3Result;
}
/**
* 解析微信退款回调通知
*/
private WxPayRefundNotifyV3Result getWxPayRefundNotifyV3Result(JSONObject jsonObject, SignatureHeader header) {
WxPayRefundNotifyV3Result wxPayRefundNotifyV3Result = null;
try {
wxPayRefundNotifyV3Result = wxManager.getWxPayRefundService().parseRefundNotifyV3Result(jsonObject.toJSONString(), header);
log.info("微信退款结果解析:{}", wxPayRefundNotifyV3Result.getResult());
} catch (WxPayException e) {
log.info("微信退款结果异常解析:{},异常信息{}", wxPayRefundNotifyV3Result.getResult(), e);
}
return wxPayRefundNotifyV3Result;
}
}
3.manager
@Slf4j
@Component
public class WxManager {
@Autowired
WxPayProperties wxPayProperties;
@Resource
private RedissonClient redissonClient;
@Autowired
private WxMpProperties wxMpProperties;
public WxMpService getWxMpService() {
WxMpRedissonConfigImpl config = new WxMpRedissonConfigImpl(redissonClient, CommonConstants.NAMESPACE);
config.setAppId(wxMpProperties.getAppId());
config.setSecret(wxMpProperties.getSecret());
WxMpService service = new WxMpServiceImpl();
service.setWxMpConfigStorage(config);
return service;
}
public WxPayService getWxPayService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(wxPayProperties.getAppid());
payConfig.setMchId(wxPayProperties.getMchId());
payConfig.setPrivateKeyPath(wxPayProperties.getPrivateKeyPath());
payConfig.setNotifyUrl(wxPayProperties.getNotifyUrl());
payConfig.setApiV3Key(wxPayProperties.getApiV3key());
payConfig.setPrivateCertPath(wxPayProperties.getPrivateCertPath());
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
public WxPayService getWxPayRefundService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(wxPayProperties.getAppid());
payConfig.setMchId(wxPayProperties.getMchId());
payConfig.setPrivateKeyPath(wxPayProperties.getPrivateKeyPath());
payConfig.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());
payConfig.setApiV3Key(wxPayProperties.getApiV3key());
payConfig.setPrivateCertPath(wxPayProperties.getPrivateCertPath());
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
@Slf4j
@Component
public class OrderManager {
@Autowired
OrderMapper orderMapper;
@Autowired
PaymentMapper paymentMapper;
@Autowired
WxManager wxManager;
/**
* 查询第三方支付,更新订单
*
* @param orderDO
* @param paymentDO
*/
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public Boolean queryOrderByWx(WxPayOrderQueryV3Result wxPayOrderQueryV3Result, OrderDO orderDO, PaymentDO paymentDO, PayStatusEnum payStatusEnum) {
//未支付 支付中 等待扣款 直接返回
WxPayTradeStateEnum wxPayTradeStateEnum = WxPayTradeStateEnum.findByCode(wxPayOrderQueryV3Result.getTradeState());
if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.NOT_PAY)
|| Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.ACCEPT)
|| Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.USER_PAYING)) {
return false;
}
if (!wxPayOrderQueryV3Result.getPayer().getOpenid().equals(paymentDO.getOpenid())) {
//记录日志,更新openid
log.error("查询微信支付,openid与本地不一致,paymentDO:{},payer:{}", paymentDO, wxPayOrderQueryV3Result.getPayer());
}
//更新支付单状态
paymentDO = PaymentConverter.toPaymentByWxPayOrderQueryV3Result(wxPayOrderQueryV3Result, paymentDO);
int p = paymentMapper.updatePayStatus(paymentDO, payStatusEnum.getCode());
if (p == 0) {
throw BizException.create(BizErrorCodeEnum.PAYMENT_UPDATE_FAIL, "支付单状态更新失败");
}
//已关闭 已撤销 支付失败 不更新订单状态 可重新支付
if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.CLOSED)
|| Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.REVOKED)
|| Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.PAY_ERROR)) {
return false;
}
//更新订单状态 幂等
int orgOrderStatus = orderDO.getStatus();
orderDO = OrderConverter.toOrderByPayment(paymentDO, orderDO, payStatusEnum);
int o = orderMapper.updateStatusByOrgStatus(orderDO, orgOrderStatus);
if (o == 0) {
throw BizException.create(BizErrorCodeEnum.ORDER_UPDATE_FAIL, "订单状态更新失败");
}
return true;
}
/**
* 支付回调 更新订单
*/
public Boolean payNotify(WxPayOrderNotifyV3Result.DecryptNotifyResult result, OrderDO order, PaymentDO payment, WxPayTradeStateEnum wxPayTradeStateEnum) {
if (!result.getPayer().getOpenid().equals(payment.getOpenid())) {
//记录日志
log.error("查询微信支付,openid与本地不一致,paymentDO:{},payer:{}", payment, result.getPayer());
}
PayStatusEnum payStatusEnum = LocalEnumUtils.findByCodeWithoutDefault(PayStatusEnum.class, payment.getStatus());
Integer orderStatus = order.getStatus();
//非 未支付 代扣款 支付中 更新支付单信息
if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.NOT_PAY)
|| Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.ACCEPT)
|| Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.USER_PAYING)) {
return false;
}
//更新支付单
payment = PaymentConverter.fromWxPayOrderNotifyV3Result(result, payment);
int p = paymentMapper.updatePayStatus(payment, payStatusEnum.getCode());
if (p == 0) {
throw BizException.create(BizErrorCodeEnum.PAYMENT_UPDATE_FAIL, "支付单状态更新失败");
}
//已关闭 已撤销 支付失败 不更新订单状态 可重新支付
if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.CLOSED)
|| Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.REVOKED)
|| Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.PAY_ERROR)) {
return false;
}
PayStatusEnum statusEnum = LocalEnumUtils.findByCodeWithoutDefault(PayStatusEnum.class, payment.getStatus());
//更新订单状态 幂等
order = OrderConverter.toOrderByPayment(payment, order, statusEnum);
int o = orderMapper.updateStatusByOrgStatus(order, orderStatus);
if (o == 0) {
throw BizException.create(BizErrorCodeEnum.ORDER_UPDATE_FAIL, "订单状态更新失败");
}
return true;
}
}
4.Properties
@Data
@Component
@Configuration
@ConfigurationProperties(prefix = "wechat-pay")
public class WxPayProperties {
/**
* 商户号
*/
private String mchId;
/**
* 证书解密的密钥
*/
private String apiV3key;
/**
* 商户私钥文件
*/
private String privateKeyPath;
/**
* apiclient_cert.pem证书文件
*/
private String privateCertPath;
/**
* 商户证书序列号
*/
private String serialNo;
/**
* 赋选供应链 服务号appid
*/
private String appid;
/**
* 支付回调通知地址
*/
private String notifyUrl;
/**
* 退款回调通知地址
*/
private String refundNotifyUrl;
}
总结
以上就是核心业务代码,涉及到具体业务mapper没有放上来,但是核心的处理步骤已经体现出来了。主要是对订单状态需要仔细判断,不同操作会引起状态的变更。业务中,当多个人扫同一个码,后续扫码成功会调用退款接口。