提交 58820101 authored 作者: hzh's avatar hzh

支付修改

上级 d396743e
...@@ -29,4 +29,13 @@ public interface RemoteOrderService { ...@@ -29,4 +29,13 @@ public interface RemoteOrderService {
*/ */
List<RemoteOrder> queryList(String orderType, List<String> orderNoList); List<RemoteOrder> queryList(String orderType, List<String> orderNoList);
/**
* 自动获取证书
*
* @param serialNumber serialNumber
* @return 是否成功
* @throws Exception 异常
*/
boolean autoUpdateOrGetCertificate(String serialNumber) throws Exception;
} }
...@@ -38,12 +38,11 @@ public interface IWxPayService { ...@@ -38,12 +38,11 @@ public interface IWxPayService {
* *
* @param timestamp 时间搓 * @param timestamp 时间搓
* @param nonce nonce * @param nonce nonce
* @param serialNo 唯一序列号
* @param signature 签名 * @param signature 签名
* @param result 支付通知密文 * @param result 支付通知密文
* @return 支付通知明文 * @return 支付通知明文
*/ */
JsapiNotifyModel notify(String timestamp, String nonce, String serialNo, String signature, String result); JsapiNotifyModel notify(String timestamp, String nonce, String signature, String result);
} }
...@@ -74,6 +74,10 @@ public class WxPayServiceImpl implements IWxPayService { ...@@ -74,6 +74,10 @@ public class WxPayServiceImpl implements IWxPayService {
@Override @Override
public String jsapi(UnifiedOrderModel unifiedOrderModel) throws Exception { public String jsapi(UnifiedOrderModel unifiedOrderModel) throws Exception {
unifiedOrderModel
.setAppid(config.getAppId())
.setMchid(config.getMchId())
.setNotify_url(config.getNotify());
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
IJPayHttpResponse response = WxPayApi.v3( IJPayHttpResponse response = WxPayApi.v3(
RequestMethodEnum.POST, RequestMethodEnum.POST,
...@@ -204,13 +208,14 @@ public class WxPayServiceImpl implements IWxPayService { ...@@ -204,13 +208,14 @@ public class WxPayServiceImpl implements IWxPayService {
} }
@Override @Override
public JsapiNotifyModel notify(String timestamp, String nonce, String serialNo, String signature, String result) { public JsapiNotifyModel notify(String timestamp, String nonce, String signature, String result) {
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature); String serialNumber = getSerialNumber();
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNumber, signature);
log.info("支付通知密文 {}", result); log.info("支付通知密文 {}", result);
try { try {
// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号 // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
String plainText = WxPayKit.verifyNotify( String plainText = WxPayKit.verifyNotify(
serialNo, serialNumber,
result, result,
signature, signature,
nonce, nonce,
......
...@@ -98,15 +98,8 @@ ...@@ -98,15 +98,8 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.github.javen205</groupId> <groupId>org.dromara</groupId>
<artifactId>IJPay-WxPay</artifactId> <artifactId>ruoyi-common-pay</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.github.javen205</groupId>
<artifactId>IJPay-Core</artifactId>
<version>2.9.9</version>
</dependency> </dependency>
<dependency> <dependency>
......
package org.dromara.order.config;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
/**
* <p>IJPay 让支付触手可及,封装了微信支付、支付宝支付、银联支付常用的支付方式以及各种常用的接口。</p>
*
* <p>不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里。 </p>
*
* <p>IJPay 交流群: 723992875、864988890</p>
*
* <p>Node.js 版: <a href="https://gitee.com/javen205/TNWX">https://gitee.com/javen205/TNWX</a></p>
*
* <p>微信配置 Bean</p>
*
* @author Javen
*/
@Getter
@Setter
@ToString
@RefreshScope
@Component
@ConfigurationProperties(prefix = "pay.wechat.v3")
public class WechatPayConfiguration {
/**
* 应用编号
*/
private String appId;
/**
* 商户号
*/
private String mchId;
/**
* 商户平台「API安全」中的 APIv3 密钥
*/
private String apiKey3;
/**
* API 证书中的 key.pem
*/
private String keyPath;
/**
* API 证书中的 cert.pem
*/
private String certPath;
/**
* 微信平台证书
*/
private String platformCertPath;
/**
* 应用域名,回调中会使用此参数
*/
private String domain;
/**
* 回调函数的接口路径
*/
private String notify;
}
...@@ -12,6 +12,7 @@ import org.dromara.common.core.exception.ServiceException; ...@@ -12,6 +12,7 @@ import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.pay.service.IWxPayService;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.order.api.RemoteOrderService; import org.dromara.order.api.RemoteOrderService;
import org.dromara.order.api.domain.RemoteOrder; import org.dromara.order.api.domain.RemoteOrder;
...@@ -46,6 +47,7 @@ public class RemoteOrderServiceImpl implements RemoteOrderService { ...@@ -46,6 +47,7 @@ public class RemoteOrderServiceImpl implements RemoteOrderService {
private final IOrderService orderService; private final IOrderService orderService;
private final IOrderTradeService orderTradeService; private final IOrderTradeService orderTradeService;
private final IOrderFeeService orderFeeService; private final IOrderFeeService orderFeeService;
private final IWxPayService wxPayService;
@Value("${pay.expire.minute:5}") @Value("${pay.expire.minute:5}")
private int expireMinute; private int expireMinute;
...@@ -152,4 +154,8 @@ public class RemoteOrderServiceImpl implements RemoteOrderService { ...@@ -152,4 +154,8 @@ public class RemoteOrderServiceImpl implements RemoteOrderService {
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
@Override
public boolean autoUpdateOrGetCertificate(String serialNumber) throws Exception {
return wxPayService.autoUpdateOrGetCertificate(serialNumber);
}
} }
package org.dromara.order.service; package org.dromara.order.service;
import org.dromara.common.pay.domain.JsapiNotifyModel;
/** /**
* @author hzh * @author hzh
* @date 2024-12-09 * @date 2024-12-09
...@@ -14,4 +16,11 @@ public interface IOrderPayService { ...@@ -14,4 +16,11 @@ public interface IOrderPayService {
*/ */
boolean notify(String body); boolean notify(String body);
/**
* 微信支付查询
* @param outTradeNo 【商户订单号】 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。
* @return 支付结果
*/
JsapiNotifyModel query(String outTradeNo);
} }
...@@ -38,6 +38,6 @@ public interface IPayStrategy { ...@@ -38,6 +38,6 @@ public interface IPayStrategy {
* @param feeList 费用集合 * @param feeList 费用集合
* @return 登录验证信息 * @return 登录验证信息
*/ */
String pay(String tradeBody, List<OrderFeeVo> feeList); String pay(String tradeBody, List<OrderFeeVo> feeList) throws Exception;
} }
package org.dromara.order.service.impl; package org.dromara.order.service.impl;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil; import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
import com.ijpay.wxpay.model.v3.Amount; import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.Payer; import com.ijpay.wxpay.model.v3.Payer;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel; import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.order.config.WechatPayConfiguration; import org.dromara.common.pay.service.IWxPayService;
import org.dromara.order.domain.bo.OrderTradeBo; import org.dromara.order.domain.bo.OrderTradeBo;
import org.dromara.order.domain.vo.OrderFeeVo; import org.dromara.order.domain.vo.OrderFeeVo;
import org.dromara.order.service.IPayStrategy; import org.dromara.order.service.IPayStrategy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.security.cert.X509Certificate;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 微信公众号支付策略 * 微信公众号支付策略
...@@ -42,79 +27,27 @@ import java.util.Map; ...@@ -42,79 +27,27 @@ import java.util.Map;
@RequiredArgsConstructor @RequiredArgsConstructor
public class JsapiPayStrategy implements IPayStrategy { public class JsapiPayStrategy implements IPayStrategy {
private final WechatPayConfiguration config; private final IWxPayService wxPayService;
@Override @Override
public String pay(String tradeBody, List<OrderFeeVo> feeList) { public String pay(String tradeBody, List<OrderFeeVo> feeList) throws Exception {
OrderTradeBo ot = JsonUtils.parseObject(tradeBody, OrderTradeBo.class); OrderTradeBo ot = JsonUtils.parseObject(tradeBody, OrderTradeBo.class);
try {
String timeExpire = DateTimeZoneUtil.dateToTimeZone(ot.getExpireTime().getTime());
// 计算总金额
BigDecimal feeAmount = feeList.stream().map(OrderFeeVo::getFeeAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
//获取支付金额 String timeExpire = DateTimeZoneUtil.dateToTimeZone(ot.getExpireTime().getTime());
int amount = new BigDecimal("100").multiply(feeAmount).intValue(); // 计算总金额
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() BigDecimal feeAmount = feeList.stream().map(OrderFeeVo::getFeeAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
.setAppid(config.getAppId())
.setMchid(config.getMchId()) //获取支付金额
.setDescription("测试") int amount = new BigDecimal("100").multiply(feeAmount).intValue();
.setOut_trade_no(ot.getOrderPayNo()) UnifiedOrderModel model = new UnifiedOrderModel()
.setTime_expire(timeExpire) .setDescription("测试")
.setAttach("") .setOut_trade_no(ot.getOrderPayNo())
.setNotify_url(config.getNotify()) .setTime_expire(timeExpire)
.setAmount(new Amount().setTotal(amount)) .setAttach("")
.setPayer(new Payer().setOpenid(ot.getPayOpenId())); .setAmount(new Amount().setTotal(amount))
.setPayer(new Payer().setOpenid(ot.getPayOpenId()));
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); return wxPayService.jsapi(model);
IJPayHttpResponse response = WxPayApi.v3(
RequestMethodEnum.POST,
WxDomainEnum.CHINA.toString(),
BasePayApiEnum.JS_API_PAY.toString(),
config.getMchId(),
getSerialNumber(),
null,
config.getKeyPath(),
JSONUtil.toJsonStr(unifiedOrderModel));
log.info("微信小程序统一下单响应 {}", response);
// 根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response, config.getPlatformCertPath());
log.info("verifySignature: {}", verifySignature);
if (response.getStatus() == HttpStatus.HTTP_OK && verifySignature) {
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
String prepayId = jsonObject.getStr("prepay_id");
Map<String, String> map = WxPayKit.jsApiCreateSign(config.getAppId(), prepayId, config.getKeyPath());
return JSONUtil.toJsonStr(map);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("下单失败");
}
throw new RuntimeException("下单失败");
} }
/**
* 商户 API 证书序列号
*/
private String serialNo;
/**
* 获取证书序列号
*
* @return 证书序列号
*/
private String getSerialNumber() {
if (StringUtils.isEmpty(serialNo)) {
// 获取证书序列号
X509Certificate certificate = PayKit.getCertificate(config.getCertPath());
if (null != certificate) {
serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
// 提前两天检查证书是否有效
boolean isValid = PayKit.checkCertificateIsValid(certificate, config.getMchId(), -2);
log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
}
}
return serialNo;
}
} }
package org.dromara.order.service.impl; package org.dromara.order.service.impl;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import io.seata.spring.annotation.GlobalTransactional; import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.pay.domain.Amount;
import org.dromara.common.pay.domain.JsapiNotifyModel;
import org.dromara.common.pay.service.IWxPayService;
import org.dromara.order.api.enums.OrderStatus; import org.dromara.order.api.enums.OrderStatus;
import org.dromara.order.config.WechatPayConfiguration;
import org.dromara.order.domain.bo.OrderBo; import org.dromara.order.domain.bo.OrderBo;
import org.dromara.order.domain.bo.OrderTradeBo; import org.dromara.order.domain.bo.OrderTradeBo;
import org.dromara.order.domain.vo.OrderTradeVo; import org.dromara.order.domain.vo.OrderTradeVo;
...@@ -26,7 +25,6 @@ import org.springframework.stereotype.Service; ...@@ -26,7 +25,6 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.security.cert.X509Certificate;
import java.util.Date; import java.util.Date;
import java.util.Objects; import java.util.Objects;
...@@ -39,11 +37,11 @@ import java.util.Objects; ...@@ -39,11 +37,11 @@ import java.util.Objects;
@RequiredArgsConstructor @RequiredArgsConstructor
public class WeChatOrderPayServiceImpl implements IOrderPayService { public class WeChatOrderPayServiceImpl implements IOrderPayService {
private final WechatPayConfiguration config;
private final IOrderTradeService orderTradeService; private final IOrderTradeService orderTradeService;
private final IOrderService orderService; private final IOrderService orderService;
@DubboReference @DubboReference
private final RemoteYsOrderService ysOrderService; private final RemoteYsOrderService ysOrderService;
private final IWxPayService wxPayService;
@GlobalTransactional(rollbackFor = Exception.class) @GlobalTransactional(rollbackFor = Exception.class)
@Override @Override
...@@ -51,58 +49,16 @@ public class WeChatOrderPayServiceImpl implements IOrderPayService { ...@@ -51,58 +49,16 @@ public class WeChatOrderPayServiceImpl implements IOrderPayService {
log.info("微信支付回调参数:{}", body); log.info("微信支付回调参数:{}", body);
JSONObject bodyJson = JSONObject.parseObject(body); JSONObject bodyJson = JSONObject.parseObject(body);
try { try {
// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
String plainText = WxPayKit.verifyNotify(
getSerialNumber(),
bodyJson.getString("result"),
bodyJson.getString("signature"),
bodyJson.getString("nonce"),
bodyJson.getString("timestamp"),
config.getApiKey3(),
config.getPlatformCertPath());
log.info("支付通知明文 {}", plainText);
JSONObject resJson = JSON.parseObject(plainText);
//支付订单号 JsapiNotifyModel model = wxPayService.notify(
String orderPayNo = resJson.getString("out_trade_no"); bodyJson.getString("timestamp"),
String tradeState = resJson.getString("trade_state"); bodyJson.getString("nonce"),
bodyJson.getString("signature"),
OrderTradeVo orderTrade = orderTradeService.getOne(new OrderTradeBo().setOrderPayNo(orderPayNo)); bodyJson.getString("result")
if (Objects.isNull(orderTrade)) { );
throw new RuntimeException("交易订单不存在");
}
if (!StringUtils.equals(tradeState, orderTrade.getTradeState())) {
//更新订单信息
String successTimeStr = resJson.getString("success_time");
//支付完成时间
Date payTime = StringUtils.isEmpty(successTimeStr) ? null : DateUtil.parse(successTimeStr);
//微信支付订单号
String transactionId = resJson.getString("transaction_id");
JSONObject amount = JSON.parseObject(resJson.getString("amount"));
//支付金额
int actualPayAmount = amount.getInteger("payer_total");
//获取openId
String payOpenId = JSON.parseObject(resJson.getString("payer")).getString("openid");
orderTrade.setPayOpenId(payOpenId);
orderTrade.setPayTime(payTime);
orderTrade.setTransactionId(transactionId);
orderTrade.setActualPayAmount(new BigDecimal(actualPayAmount).divide(new BigDecimal(100), 2, RoundingMode.CEILING));
orderTrade.setTradeState(tradeState);
orderTrade.setTradeInfo(JSON.toJSONString(resJson));
orderTradeService.updateByBo(BeanUtil.copyProperties(orderTrade, OrderTradeBo.class));
//跟新主表信息
OrderVo order = orderService.getOne(new OrderBo().setOrderNo(orderTrade.getOrderNo()));
order.setOrderPayNo(orderPayNo);
order.setActualPayAmount(orderTrade.getActualPayAmount());
order.setPayTime(new Date());
order.setStatus(OrderStatus.PAYED.getCode());
orderService.updateByBo(BeanUtil.copyProperties(order, OrderBo.class));
//跟新云上相关信息 //处理订单
ysOrderService.payed(order.getOrderType(), order.getOrderNo()); dealOrder(model);
}
return true; return true;
} catch (Exception e) { } catch (Exception e) {
...@@ -111,29 +67,55 @@ public class WeChatOrderPayServiceImpl implements IOrderPayService { ...@@ -111,29 +67,55 @@ public class WeChatOrderPayServiceImpl implements IOrderPayService {
} }
} }
@GlobalTransactional(rollbackFor = Exception.class)
@Override
public JsapiNotifyModel query(String outTradeNo) {
JsapiNotifyModel model = wxPayService.query(outTradeNo);
//处理订单
dealOrder(model);
return model;
}
private void dealOrder(JsapiNotifyModel model) {
//支付订单号
String orderPayNo = model.getOut_trade_no();
String tradeState = model.getTrade_state();
OrderTradeVo orderTrade = orderTradeService.getOne(new OrderTradeBo().setOrderPayNo(orderPayNo));
if (Objects.isNull(orderTrade)) {
throw new RuntimeException("交易订单不存在");
}
if (!StringUtils.equals(tradeState, orderTrade.getTradeState())) {
//更新订单信息
String successTimeStr = model.getSuccess_time();
//支付完成时间
Date payTime = StringUtils.isEmpty(successTimeStr) ? null : DateUtil.parse(successTimeStr);
//微信支付订单号
String transactionId = model.getTransaction_id();
Amount amount = model.getAmount();
//支付金额
int actualPayAmount = amount.getPayer_total();
//获取openId
String payOpenId = model.getPayer().getOpenid();
orderTrade.setPayOpenId(payOpenId);
orderTrade.setPayTime(payTime);
orderTrade.setTransactionId(transactionId);
orderTrade.setActualPayAmount(new BigDecimal(actualPayAmount).divide(new BigDecimal(100), 2, RoundingMode.CEILING));
orderTrade.setTradeState(tradeState);
orderTrade.setTradeInfo(JSON.toJSONString(model));
orderTradeService.updateByBo(BeanUtil.copyProperties(orderTrade, OrderTradeBo.class));
/** //跟新主表信息
* 商户 API 证书序列号 OrderVo order = orderService.getOne(new OrderBo().setOrderNo(orderTrade.getOrderNo()));
*/ order.setOrderPayNo(orderPayNo);
private String serialNo; order.setActualPayAmount(orderTrade.getActualPayAmount());
order.setPayTime(new Date());
order.setStatus(OrderStatus.PAYED.getCode());
orderService.updateByBo(BeanUtil.copyProperties(order, OrderBo.class));
/** //跟新云上相关信息
* 获取证书序列号 ysOrderService.payed(order.getOrderType(), order.getOrderNo());
*
* @return 证书序列号
*/
private String getSerialNumber() {
if (StringUtils.isEmpty(serialNo)) {
// 获取证书序列号
X509Certificate certificate = PayKit.getCertificate(config.getCertPath());
if (null != certificate) {
serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
// 提前两天检查证书是否有效
boolean isValid = PayKit.checkCertificateIsValid(certificate, config.getMchId(), -2);
log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
}
} }
return serialNo;
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论