完成支付请求、重新发起请求、实际支付(通知回调为测试),需要跟踪状态轮询时是否更新数据库中的状态;退款申请已经完成,还需要根据业务系统增加审核相关字段

This commit is contained in:
jkcdev 2025-05-14 17:47:36 +08:00
parent e2985dd256
commit 5a94e8da43
14 changed files with 196 additions and 122 deletions

View File

@ -32,12 +32,12 @@
<artifactId>ebike-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 微信支付SDK -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.17</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${boot.version}</version>
</dependency>
<!-- mybatis-flex -->
<dependency>
<groupId>com.mybatis-flex</groupId>
@ -50,6 +50,33 @@
<version>${mybatis-flex.version}</version>
<scope>provided</scope>
</dependency>
<!-- nacos客户端依赖包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${nacos.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 微信支付SDK -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.17</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
@ -101,28 +128,6 @@
<artifactId>fastjson2</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- nacos客户端依赖包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${nacos.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<!-- for test only -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -3,6 +3,7 @@ package com.cdzy.payment;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
@ -14,6 +15,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
*/
@SpringBootApplication
@MapperScan("com.cdzy.payment.mapper")
@EnableFeignClients(basePackages = "com.ebike.feign")
public class EbikePaymentApplication {
public static void main(String[] args) {
SpringApplication.run(EbikePaymentApplication.class, args);

View File

@ -3,6 +3,7 @@ package com.cdzy.payment.config;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.cipher.Signer;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.refund.RefundService;
@ -62,9 +63,14 @@ public class WxPayConfig {
private String refundNotifyUrl;
/**
* 支付退款过期时间分钟
* 默认5分钟超过5分钟订单会自动关闭
* 默认24小时超过24小时订单会自动关闭
*/
private Integer expireMinute = 5;
private Integer expireMinute = 1440;
/**
* 定时任务执行表达式
* 默认每12小时执行1次
*/
private String cronExpression = "0 0 0,12 * * ?";
@Bean
public Config certificateConfig() {
@ -90,9 +96,8 @@ public class WxPayConfig {
}
@Bean
public RSASigner wxRsaSigner(){
PrivateKey privateKey = PemUtil.loadPrivateKeyFromPath(privateKeyPath);
return new RSASigner(merchantSerialNumber, privateKey);
public Signer wxRsaSigner(Config certificateConfig){
return certificateConfig.createSigner();
}
}

View File

@ -31,7 +31,7 @@ public class EbikeWxPaymentController {
* @param orderId 骑行订单id
* @return 下单成功返回true否则返回false
*/
@PostMapping("/prepay")
@GetMapping("/prepay")
public JsonResult<?> prepay(@RequestParam(name = "orderId") String orderId) {
JSONObject r = wxPayService.prepay(orderId);
return r.containsKey("error")?JsonResult.failed("下单失败: "+ r.getString("error")):JsonResult.success(r);
@ -44,8 +44,8 @@ public class EbikeWxPaymentController {
* @return 支付订单信息
*/
@GetMapping("/queryOrder/{orderId}")
public JsonResult<?> queryOrderByOutTradeNo(@PathVariable String orderId) {
Transaction r = wxPayService.queryOrderByOutTradeNo(orderId);
public JsonResult<?> queryOrderByOrderId(@PathVariable(name = "orderId") String orderId) {
Transaction r = wxPayService.queryOrderByOrderId(orderId);
return r == null?JsonResult.failed(String.format("骑行订单号{%s}查询支付订单失败", orderId)):JsonResult.success(r);
}
@ -56,8 +56,8 @@ public class EbikeWxPaymentController {
* @return 订单信息支付状态
*/
@GetMapping("/queryOrderStatus/{orderId}")
public JsonResult<?> queryOrderStatusById(@PathVariable String orderId) {
HandleNotifyResult r = wxPayService.queryOrderStatusByOutTradeNo(orderId);
public JsonResult<?> queryOrderStatusById(@PathVariable(name = "orderId") String orderId) {
HandleNotifyResult r = wxPayService.queryOrderStatusByOrderId(orderId);
return JsonResult.success(r);
}
@ -93,7 +93,7 @@ public class EbikeWxPaymentController {
* @return 退款信息
*/
@GetMapping("/queryRefund/{refundId}")
public JsonResult<?> refundQuery(@PathVariable String refundId) {
public JsonResult<?> refundQuery(@PathVariable(name = "refundId") String refundId) {
Refund r = wxPayService.queryRefundByOutNo(refundId);
return r == null?JsonResult.failed(String.format("退款单号{%s}查询退款失败", refundId)):JsonResult.success(r);
}

View File

@ -2,8 +2,8 @@ package com.cdzy.payment.model.dto;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.cipher.SignatureResult;
import com.wechat.pay.java.core.cipher.Signer;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -49,15 +49,11 @@ public class WxJsapiPromptDto {
*/
private String paySign;
public JSONObject toJson(RSASigner rsaSigner) {
public JSONObject toJson(Signer rsaSigner) {
try{
// 计算签名
StringJoiner signMsg = new StringJoiner("\n");
signMsg.add(appId);
signMsg.add(timeStamp);
signMsg.add(nonceStr);
signMsg.add(extension);
SignatureResult sign = rsaSigner.sign(signMsg.toString());
String signMsg = appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + extension + "\n";
SignatureResult sign = rsaSigner.sign(signMsg);
this.paySign = sign.getSign();
//result.put("package", result.remove("extension"));
return JSONObject.parseObject(JSONObject.toJSONString(this));

View File

@ -40,7 +40,7 @@ public class EbikePayment implements Serializable {
private String orderId;
/**
* 预支付交易会话标识;有效期为2小时
* 支付交易用订单编号重新请求时需要更新
*/
private String paymentId;

View File

@ -39,10 +39,6 @@ public class EbikeRefund implements Serializable {
*/
private String orderId;
/**
* 支付记录id
*/
private String recordId;
/**
* 微信支付订单号
@ -76,7 +72,7 @@ public class EbikeRefund implements Serializable {
private Double refund;
/**
* 退款总金额
* 原订单金额
*/
private Double total;

View File

@ -27,27 +27,36 @@ public interface WxPayService {
/**
* JSAPI支付下单
*
* @param outTradeNo 订单id
* @param orderId 订单id
* @return 下单成功返回wx支付请求参数否则返回null
*/
JSONObject prepay(String outTradeNo);
JSONObject prepay(String orderId);
/**
* 通过支付订单号查询支付订单
*
* @param outTradeNo 支付订单号
* @return 支付订单信息
*/
Transaction queryOrderByOutTradeNo(String outTradeNo);
/**
* 通过商户(骑行)订单号查询支付订单
*
* @param outTradeNo 商户(骑行)订单号
* @param orderId 商户(骑行)订单号
* @return 支付订单信息
*/
Transaction queryOrderByOutTradeNo(String outTradeNo);
Transaction queryOrderByOrderId(String orderId);
/**
* 通过订单号查询支付订单状态
*
* @param outTradeNo 商户(骑行)订单号
* @param orderId 商户(骑行)订单号
* @return 支付订单信息
*/
HandleNotifyResult queryOrderStatusByOutTradeNo(String outTradeNo);
HandleNotifyResult queryOrderStatusByOrderId(String orderId);
/**
* 处理支付回调

View File

@ -4,8 +4,6 @@ import com.cdzy.payment.model.enums.PayMethod;
import com.cdzy.payment.utils.StringUtils;
import com.ebike.feign.clients.OrdersFeignClient;
import com.ebike.feign.model.res.ResFeignOrderPaymentDto;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.cdzy.payment.model.entity.EbikePayment;
@ -18,6 +16,7 @@ import org.springframework.stereotype.Service;
import java.util.List;
import static com.cdzy.payment.model.entity.table.EbikePaymentTableDef.EBIKE_PAYMENT;
import static com.mybatisflex.core.constant.FuncName.*;
/**
* 用户订单支付记录 服务层实现
@ -33,27 +32,29 @@ public class EbikePaymentServiceImpl extends ServiceImpl<EbikePaymentMapper, Ebi
@Override
public List<EbikePayment> getNoPayOrderByDuration(int duration) {
QueryColumn time = QueryMethods.secToTime(String.valueOf(duration * 60));
// trade_state 等于 NOTPAY未支付的 并且创建时间未超过duration分钟的订单
String timeFilter = String.format("%s(%s, %s(%s))<%s()", ADDTIME, EBIKE_PAYMENT.CREATE_TIME.getName(), SEC_TO_TIME
, String.valueOf(duration * 60), NOW);
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_PAYMENT.TRADE_STATE.eq(Transaction.TradeStateEnum.NOTPAY.ordinal()))
.and(QueryMethods.addTime(EBIKE_PAYMENT.CREATE_TIME, time).lt(QueryMethods.now()));
.and(timeFilter);
return list(query);
}
@Override
public List<EbikePayment> getExpireOrderByDuration(int duration) {
QueryColumn time = QueryMethods.secToTime(String.valueOf(duration * 60));
// trade_state 等于 NOTPAY未支付的 并且创建时间超过duration分钟的订单
String timeFilter = String.format("%s(%s, %s(%s))>=%s()", ADDTIME, EBIKE_PAYMENT.CREATE_TIME.getName(), SEC_TO_TIME
, String.valueOf(duration * 60), NOW);
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_PAYMENT.TRADE_STATE.eq(Transaction.TradeStateEnum.NOTPAY.ordinal()))
.and(QueryMethods.addTime(EBIKE_PAYMENT.CREATE_TIME, time).ge(QueryMethods.now()));
.and(timeFilter);
return list(query);
}
@Override
public Boolean updatePaymentStatus(Transaction transaction) {
EbikePayment ebikePayment = new EbikePayment();
EbikePayment ebikePayment = getByPaymentId(transaction.getOutTradeNo());
//ebikePayment.setRecordId(recordId);
ebikePayment.setTradeState(String.valueOf(transaction.getTradeState().ordinal()));
if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
@ -65,14 +66,12 @@ public class EbikePaymentServiceImpl extends ServiceImpl<EbikePaymentMapper, Ebi
ebikePayment.setTransactionId(transaction.getTransactionId());
// 同步支付状态
ResFeignOrderPaymentDto paymentParam = new ResFeignOrderPaymentDto();
paymentParam.setOrderId(Long.valueOf(transaction.getOutTradeNo()));
paymentParam.setOrderId(Long.valueOf(ebikePayment.getOrderId()));
paymentParam.setPaymentTime(ebikePayment.getPaymentTime());
paymentParam.setPaymentMethod(PayMethod.wechat.name());
ordersFeignClient.payment(paymentParam);
}
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_PAYMENT.ORDER_ID.eq(transaction.getOutTradeNo()));
return update(ebikePayment, query);
return updateById(ebikePayment);
}
@Override
@ -81,4 +80,16 @@ public class EbikePaymentServiceImpl extends ServiceImpl<EbikePaymentMapper, Ebi
.where(EBIKE_PAYMENT.ORDER_ID.eq(orderId));
return this.getOne(query);
}
/**
* 根据支付id查询支付记录
*
* @param paymentId 支付id
* @return
*/
private EbikePayment getByPaymentId(String paymentId) {
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_PAYMENT.PAYMENT_ID.eq(paymentId));
return this.getOne(query);
}
}

View File

@ -1,13 +1,11 @@
package com.cdzy.payment.service.impl;
import com.ebike.feign.clients.OrdersFeignClient;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.cdzy.payment.model.entity.EbikeRefund;
import com.cdzy.payment.mapper.EbikeRefundMapper;
import com.cdzy.payment.service.EbikeRefundService;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.Refund;
import com.wechat.pay.java.service.refund.model.Status;
import jakarta.annotation.Resource;
@ -17,6 +15,7 @@ import java.time.LocalDateTime;
import java.util.List;
import static com.cdzy.payment.model.entity.table.EbikeRefundTableDef.EBIKE_REFUND;
import static com.mybatisflex.core.constant.FuncName.*;
/**
* 用户订单退款记录 服务层实现
@ -41,10 +40,12 @@ public class EbikeRefundServiceImpl extends ServiceImpl<EbikeRefundMapper, Ebike
@Override
public List<EbikeRefund> getNoSuccessRefundOrderByDuration(int duration) {
// trade_state 不等于 SUCCESS退款成功的 并且创建时间超过duration分钟的订单
// trade_state 等于 PROCESSING正在退款中的 并且创建时间超过duration分钟的订单
String timeFilter = String.format("%s(%s, %s(%s))>=%s()", ADDTIME, EBIKE_REFUND.CREATE_TIME.getName(), SEC_TO_TIME
, String.valueOf(duration * 60), NOW);
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_REFUND.STATUS.eq(Status.PROCESSING.ordinal()))
.and(QueryMethods.addTime(EBIKE_REFUND.CREATE_TIME, QueryMethods.secToTime(String.valueOf(duration * 60))).ge(QueryMethods.now()));
.and(timeFilter);
return list(query);
}

View File

@ -18,7 +18,7 @@ import com.ebike.feign.model.rsp.AmountDto;
import com.ebike.feign.model.rsp.DetailDto;
import com.ebike.feign.model.rsp.EbikePaymentDto;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.cipher.Signer;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
@ -41,6 +41,8 @@ import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.*;
import java.util.ArrayList;
import java.util.List;
import static com.wechat.pay.java.core.http.Constant.*;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_SERIAL;
@ -63,7 +65,7 @@ public class WxPayServiceImpl implements WxPayService {
@Resource
private WxPayConfig wxPayConfig;
@Resource
private RSASigner wxRsaSigner;
private Signer wxRsaSigner;
@Resource
private Config certificateConfig;
@Resource
@ -71,13 +73,14 @@ public class WxPayServiceImpl implements WxPayService {
@Resource
private EbikeRefundService ebikeRefundService;
@Resource
private OrdersFeignClient orderFeingClient;
private OrdersFeignClient ordersFeignClient;
@Override
public boolean closeOrder(String outTradeNo) {
try {
CloseOrderRequest request = new CloseOrderRequest();
request.setOutTradeNo(outTradeNo);
request.setMchid(wxPayConfig.getMerchantId());
wxJsapiService.closeOrder(request);
log.info("关闭订单成功,订单号:{}", outTradeNo);
// ebikePaymentService.updatePaymentStatus(outTradeNo, 2);
@ -90,27 +93,42 @@ public class WxPayServiceImpl implements WxPayService {
@Override
public JSONObject prepay(String outTradeNo){
public JSONObject prepay(String orderId){
JSONObject r = new JSONObject();
// 首先检查订单是否正在付中或者已经支付成功
EbikePayment ebikePayment = ebikePaymentService.getByOrderId(outTradeNo);
// 关闭了的订单重新发起请求
EbikePayment ebikePayment = ebikePaymentService.getByOrderId(orderId);
if (ebikePayment != null) {
if (String.valueOf(Transaction.TradeStateEnum.SUCCESS.ordinal()).equals(ebikePayment.getTradeState())) {
log.info("订单已经支付成功,订单号:{}", outTradeNo);
String tradeState = ebikePayment.getTradeState();
if (String.valueOf(Transaction.TradeStateEnum.SUCCESS.ordinal()).equals(tradeState)) {
log.info("订单已经支付成功,订单号:{}", orderId);
r.put("error", "订单已经支付成功");
return r;
}
if (String.valueOf(Transaction.TradeStateEnum.NOTPAY.ordinal()).equals(ebikePayment.getTradeState())
||String.valueOf(Transaction.TradeStateEnum.USERPAYING.ordinal()).equals(ebikePayment.getTradeState())) {
log.info("订单正在支付中,订单号:{}", outTradeNo);
r.put("error", "订单正在支付中");
return null;
if (String.valueOf(Transaction.TradeStateEnum.NOTPAY.ordinal()).equals(tradeState)
||String.valueOf(Transaction.TradeStateEnum.USERPAYING.ordinal()).equals(tradeState)) {
// 检查订单是否关闭
Transaction t = queryOrderByOutTradeNo(ebikePayment.getPaymentId());
if (t != null && Transaction.TradeStateEnum.CLOSED.equals(t.getTradeState())) {
log.info("订单已经关闭,订单号:{}, 重新发起请求", orderId);
ebikePayment.setTradeState(String.valueOf(Transaction.TradeStateEnum.CLOSED.ordinal()));
ebikePayment.setPaymentId(StringUtils.generateSnowflakeId("paymentId"));
}else{
log.info("订单正在支付中,订单号:{}", orderId);
}
} else if (String.valueOf(Transaction.TradeStateEnum.CLOSED.ordinal()).equals(tradeState)) {
log.info("订单已经关闭,订单号:{}, 重新发起请求", orderId);
ebikePayment.setPaymentId(StringUtils.generateSnowflakeId("paymentId"));
}else {
log.info("订单状态异常,订单号:{}", orderId);
r.put("error", "订单状态异常: ");
return r;
}
}
//查询订单, orderFeingClient.getOrderById(orderId)
JsonResult<?> result = orderFeingClient.getPaymentDetails(Long.valueOf(outTradeNo));
JsonResult<?> result = ordersFeignClient.getPaymentDetails(Long.valueOf(orderId));
if (result.getCode() != 200) {
log.error("查询订单 {} 失败, {}", outTradeNo, result.getMessage());
log.error("查询订单 {} 失败, {}", orderId, result.getMessage());
r.put("error", "查询订单失败, "+result.getMessage());
return r;
}
@ -127,18 +145,20 @@ public class WxPayServiceImpl implements WxPayService {
* @return 下单成功返回wx支付请求参数否则返回null
*/
private JSONObject prepay(EbikePaymentDto paymentDto, EbikePayment ebikePayment) {
JSONObject r = new JSONObject();
String outTradeNo = paymentDto.getOrderId();
String description = paymentDto.getDescription();
String goodsTag = paymentDto.getGoodsTag();
String openId = paymentDto.getOpenId();
AmountDto amount = paymentDto.getAmount();
DetailDto detail = paymentDto.getDetail();
String payId = ebikePayment==null?outTradeNo : ebikePayment.getPaymentId();
try {
PrepayRequest request = new PrepayRequest();
request.setAppid(wxPayConfig.getAppId());
request.setMchid(wxPayConfig.getMerchantId());
request.setDescription(description);
request.setOutTradeNo(outTradeNo);
request.setOutTradeNo(payId);
request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
LocalDateTime expireTime = LocalDateTime.now().plusMinutes(wxPayConfig.getExpireMinute());
request.setTimeExpire(StringUtils.formatLocalDatetime(expireTime));
@ -151,22 +171,36 @@ public class WxPayServiceImpl implements WxPayService {
payer.setOpenid(openId);
request.setPayer(payer);
Detail detailReq = new Detail();
detailReq.setGoodsDetail(detail.getGoodsDetail().stream().map(goodsDetail -> {
List<GoodsDetail> goodsDetails = detail.getGoodsDetail().stream().map(goodsDetail -> {
GoodsDetail goodsDetailReq = new GoodsDetail();
goodsDetailReq.setGoodsName(goodsDetail.getItemName());
goodsDetailReq.setMerchantGoodsId(goodsDetail.getItemId());
goodsDetailReq.setQuantity(goodsDetail.getQuantity());
goodsDetailReq.setUnitPrice(BigDecimal.valueOf(goodsDetail.getUnitPrice()*100.0).intValue());
return goodsDetailReq;
}).toList());
}).toList();
if (goodsDetails.isEmpty()){
goodsDetails = new ArrayList<>();
GoodsDetail goodsDetail = new GoodsDetail();
String snowId = StringUtils.generateSnowflakeId("goodsId");
goodsDetail.setMerchantGoodsId(snowId);
goodsDetail.setGoodsName(goodsTag);
goodsDetail.setQuantity(1);
goodsDetail.setUnitPrice(BigDecimal.valueOf(amount.getTotal()*100.0).intValue());
goodsDetails.add(goodsDetail);
}
detailReq.setGoodsDetail(goodsDetails);
detailReq.setCostPrice(BigDecimal.valueOf(detail.getCostPrice()*100.0).intValue());
detailReq.setInvoiceId(detail.getInvoiceId());
request.setDetail(detailReq);
SettleInfo settleInfo = new SettleInfo();
settleInfo.setProfitSharing(false);
request.setSettleInfo(settleInfo);
//log.info("微信支付下单prepay订单号{}, {}", outTradeNo, JSONObject.toJSONString(request));
PrepayResponse response = wxJsapiService.prepay(request);
String payId = response.getPrepayId();
//入库 重新发起支付更新记录订单号<-->记录号一一对应
if (payId != null) {
if (response != null && response.getPrepayId() != null) {
log.info("微信支付下单prepay成功支付订单号{}", response.getPrepayId());
if (ebikePayment == null) {
ebikePayment = new EbikePayment();
ebikePayment.setOrderId(outTradeNo);
@ -189,14 +223,16 @@ public class WxPayServiceImpl implements WxPayService {
wxJsapiPromptDto.setAppId(wxPayConfig.getAppId());
wxJsapiPromptDto.setTimeStamp(String.valueOf(Instant.now().getEpochSecond()));
wxJsapiPromptDto.setNonceStr(NonceUtil.createNonce(32));
wxJsapiPromptDto.setExtension("prepay_id=" + payId);
wxJsapiPromptDto.setExtension("prepay_id=" + response.getPrepayId());
return wxJsapiPromptDto.toJson(wxRsaSigner);
}
log.error("微信支付下单prepay失败订单号{}", outTradeNo);
return null;
r.put("error", "微信支付下单prepay失败");
return r;
}catch (Exception e) {
logError("微信支付下单prepay", e);
return null;
r.put("error", "微信支付下单prepay失败: "+e.getMessage());
return r;
}
}
@ -209,13 +245,23 @@ public class WxPayServiceImpl implements WxPayService {
}
@Override
public HandleNotifyResult queryOrderStatusByOutTradeNo(String outTradeNo) {
public Transaction queryOrderByOrderId(String orderId) {
EbikePayment ebikePayment = ebikePaymentService.getByOrderId(orderId);
if (ebikePayment==null){
log.error("{}支付订单不存在", orderId);
return null;
}
return queryOrderByOutTradeNo(ebikePayment.getPaymentId());
}
@Override
public HandleNotifyResult queryOrderStatusByOrderId(String orderId) {
// 先查本地数据库如果退款状态不为SUCCESS需要查询微信支付
HandleNotifyResult result = new HandleNotifyResult();
EbikePayment ebikePayment = ebikePaymentService.getByOrderId(outTradeNo);
EbikePayment ebikePayment = ebikePaymentService.getByOrderId(orderId);
if (ebikePayment==null){
log.error("{}支付订单不存在", outTradeNo);
result.setMessage(String.format("{%s}支付订单不存在", outTradeNo));
log.error("{}支付订单不存在", orderId);
result.setMessage(String.format("{%s}支付订单不存在", orderId));
return result;
}
result.setSuccess(String.valueOf(Transaction.TradeStateEnum.SUCCESS.ordinal()).equals(ebikePayment.getTradeState()));
@ -223,11 +269,9 @@ public class WxPayServiceImpl implements WxPayService {
result.setMessage(String.format("订单%s支付成功", ebikePayment.getOrderId()));
return result;
}
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setOutTradeNo(outTradeNo);
request.setMchid(wxPayConfig.getMerchantId());
Transaction r = wxJsapiService.queryOrderByOutTradeNo(request);
if(r!=null&& r.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {
Transaction r =queryOrderByOutTradeNo(ebikePayment.getPaymentId());
if(r!=null&& Transaction.TradeStateEnum.SUCCESS.equals(r.getTradeState())) {
result.setMessage(String.format("订单%s支付成功", ebikePayment.getOrderId()));
result.setSuccess(true);
// 更新支付状态
@ -295,6 +339,7 @@ public class WxPayServiceImpl implements WxPayService {
ebikeRefund.setCreateTime(LocalDateTime.now());
ebikeRefund.setRefundId(outRefundNo);
ebikeRefund.setOrderId(outTradeNo);
ebikeRefund.setTotal(ebikePayment.getTotal());
ebikeRefund.setTransactionId(transactionId);
ebikeRefund.setReason(reason);
ebikeRefund.setStatus(String.valueOf(Status.PROCESSING.ordinal()));

View File

@ -1,5 +1,6 @@
package com.cdzy.payment.task;
import com.cdzy.payment.config.WxPayConfig;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@ -17,17 +18,18 @@ import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ScheduledTaskManager {
// 每隔30秒执行1次
private final static String CRON_EXPRESSION = "0/30 * * * *?";
// @Scheduled任务线程默认串行执行需要考虑并发问题
@Resource
private WxPayConfig wxPayConfig;
@Resource
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Resource
private WsPayTask wxPayTask;
@PostConstruct
public void init() {
String CRON_EXPRESSION = wxPayConfig.getCronExpression();
// 启动定时任务
// 1. 每隔30秒执行1次查询创建未超过5分钟并且未支付的订单
threadPoolTaskScheduler.schedule(new Runnable() {
@ -41,17 +43,18 @@ public class ScheduledTaskManager {
}
}, new CronTrigger(CRON_EXPRESSION));
// 2. 每隔30秒执行1次查询创建超过5分钟并且未支付的订单
threadPoolTaskScheduler.schedule(new Runnable() {
@Override
public void run() {
try {
wxPayTask.closeOrder();
} catch (Exception e) {
log.error("closeOrder 执行异常", e);
}
}
}, new CronTrigger(CRON_EXPRESSION));
//========支付系统自动关闭不主动关闭每次发起请求的时候检测是否关闭========
//// 2. 每隔30秒执行1次查询创建超过5分钟并且未支付的订单
//threadPoolTaskScheduler.schedule(new Runnable() {
// @Override
// public void run() {
// try {
// wxPayTask.closeOrder();
// } catch (Exception e) {
// log.error("closeOrder 执行异常", e);
// }
// }
//}, new CronTrigger(CRON_EXPRESSION));
// 3. 每隔30秒执行1次查询创建未超过5分钟并且未成功的退款单
threadPoolTaskScheduler.schedule(new Runnable() {

View File

@ -36,7 +36,7 @@ public class WsPayTask {
/**
* 查询创建未超过5分钟并且未支付的订单
* 查询创建未超过24小时并且未支付的订单
*/
public void checkOrderStatus() throws Exception {
log.info("checkOrderStatus 执行......");
@ -54,7 +54,7 @@ public class WsPayTask {
}
/**
* 查询创建超过5分钟并且未支付的订单
* 查询创建超过24小时并且未支付的订单
*/
public void closeOrder() throws Exception {
log.info("closeOrder 执行......");
@ -76,7 +76,7 @@ public class WsPayTask {
}
/**
* 查询创建未超过5分钟并且未成功的退款单
* 查询创建未超过24小时并且未成功的退款单
*/
public void checkRefundStatus() throws Exception {
log.info("checkRefundStatus 执行......");

View File

@ -36,13 +36,14 @@ payment:
app-id: wx327d788d7bd6eddf
merchant-id: 1715147005
private-key-path: D:/Projects/eBIKE/docs/secrets/apiclient_key.pem
public-key-path: D:/Projects/eBIKE/docs/secrets/apiclient_cert.pem
public-key-path: D:/Projects/eBIKE/docs/secrets/apiclient_pubkey.pem
merchant-serial-number: 7873E3E694ADD0368EA3E9FAC929F496EECB8DF9
api-v3-key: 1715147005V3Key20250425174554633
public-key-id: PUB_KEY_ID_0117151470052025042500331704000601
pay-notify_url: http://192.168.2.156:10017/wxPayment/pay-notify
refund-notify_url: http://192.168.2.156:10017/wxPayment/refund-notify
expire-minutes: 5
expire-minutes: 1440
cron-expression: 0 0 0,12 * * ?
task-scheduler-pool:
poolSize: 100
threadNamePrefix: task-scheduled-