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

支付修改

上级 d396743e
......@@ -29,4 +29,13 @@ public interface RemoteOrderService {
*/
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 {
*
* @param timestamp 时间搓
* @param nonce nonce
* @param serialNo 唯一序列号
* @param signature 签名
* @param result 支付通知密文
* @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 {
@Override
public String jsapi(UnifiedOrderModel unifiedOrderModel) throws Exception {
unifiedOrderModel
.setAppid(config.getAppId())
.setMchid(config.getMchId())
.setNotify_url(config.getNotify());
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
IJPayHttpResponse response = WxPayApi.v3(
RequestMethodEnum.POST,
......@@ -204,13 +208,14 @@ public class WxPayServiceImpl implements IWxPayService {
}
@Override
public JsapiNotifyModel notify(String timestamp, String nonce, String serialNo, String signature, String result) {
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
public JsapiNotifyModel notify(String timestamp, String nonce, String signature, String result) {
String serialNumber = getSerialNumber();
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNumber, signature);
log.info("支付通知密文 {}", result);
try {
// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
String plainText = WxPayKit.verifyNotify(
serialNo,
serialNumber,
result,
signature,
nonce,
......
......@@ -98,15 +98,8 @@
</dependency>
<dependency>
<groupId>com.github.javen205</groupId>
<artifactId>IJPay-WxPay</artifactId>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.github.javen205</groupId>
<artifactId>IJPay-Core</artifactId>
<version>2.9.9</version>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-pay</artifactId>
</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;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.pay.service.IWxPayService;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.order.api.RemoteOrderService;
import org.dromara.order.api.domain.RemoteOrder;
......@@ -46,6 +47,7 @@ public class RemoteOrderServiceImpl implements RemoteOrderService {
private final IOrderService orderService;
private final IOrderTradeService orderTradeService;
private final IOrderFeeService orderFeeService;
private final IWxPayService wxPayService;
@Value("${pay.expire.minute:5}")
private int expireMinute;
......@@ -152,4 +154,8 @@ public class RemoteOrderServiceImpl implements RemoteOrderService {
}).collect(Collectors.toList());
}
@Override
public boolean autoUpdateOrGetCertificate(String serialNumber) throws Exception {
return wxPayService.autoUpdateOrGetCertificate(serialNumber);
}
}
package org.dromara.order.service;
import org.dromara.common.pay.domain.JsapiNotifyModel;
/**
* @author hzh
* @date 2024-12-09
......@@ -14,4 +16,11 @@ public interface IOrderPayService {
*/
boolean notify(String body);
/**
* 微信支付查询
* @param outTradeNo 【商户订单号】 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一。
* @return 支付结果
*/
JsapiNotifyModel query(String outTradeNo);
}
......@@ -38,6 +38,6 @@ public interface IPayStrategy {
* @param feeList 费用集合
* @return 登录验证信息
*/
String pay(String tradeBody, List<OrderFeeVo> feeList);
String pay(String tradeBody, List<OrderFeeVo> feeList) throws Exception;
}
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.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.Payer;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.vo.OrderFeeVo;
import org.dromara.order.service.IPayStrategy;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;
/**
* 微信公众号支付策略
......@@ -42,79 +27,27 @@ import java.util.Map;
@RequiredArgsConstructor
public class JsapiPayStrategy implements IPayStrategy {
private final WechatPayConfiguration config;
private final IWxPayService wxPayService;
@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);
try {
String timeExpire = DateTimeZoneUtil.dateToTimeZone(ot.getExpireTime().getTime());
// 计算总金额
BigDecimal feeAmount = feeList.stream().map(OrderFeeVo::getFeeAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
//获取支付金额
int amount = new BigDecimal("100").multiply(feeAmount).intValue();
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(config.getAppId())
.setMchid(config.getMchId())
.setDescription("测试")
.setOut_trade_no(ot.getOrderPayNo())
.setTime_expire(timeExpire)
.setAttach("")
.setNotify_url(config.getNotify())
.setAmount(new Amount().setTotal(amount))
.setPayer(new Payer().setOpenid(ot.getPayOpenId()));
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
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("下单失败");
String timeExpire = DateTimeZoneUtil.dateToTimeZone(ot.getExpireTime().getTime());
// 计算总金额
BigDecimal feeAmount = feeList.stream().map(OrderFeeVo::getFeeAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
//获取支付金额
int amount = new BigDecimal("100").multiply(feeAmount).intValue();
UnifiedOrderModel model = new UnifiedOrderModel()
.setDescription("测试")
.setOut_trade_no(ot.getOrderPayNo())
.setTime_expire(timeExpire)
.setAttach("")
.setAmount(new Amount().setTotal(amount))
.setPayer(new Payer().setOpenid(ot.getPayOpenId()));
return wxPayService.jsapi(model);
}
/**
* 商户 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;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.config.WechatPayConfiguration;
import org.dromara.order.domain.bo.OrderBo;
import org.dromara.order.domain.bo.OrderTradeBo;
import org.dromara.order.domain.vo.OrderTradeVo;
......@@ -26,7 +25,6 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Objects;
......@@ -39,11 +37,11 @@ import java.util.Objects;
@RequiredArgsConstructor
public class WeChatOrderPayServiceImpl implements IOrderPayService {
private final WechatPayConfiguration config;
private final IOrderTradeService orderTradeService;
private final IOrderService orderService;
@DubboReference
private final RemoteYsOrderService ysOrderService;
private final IWxPayService wxPayService;
@GlobalTransactional(rollbackFor = Exception.class)
@Override
......@@ -51,58 +49,16 @@ public class WeChatOrderPayServiceImpl implements IOrderPayService {
log.info("微信支付回调参数:{}", body);
JSONObject bodyJson = JSONObject.parseObject(body);
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);
//支付订单号
String orderPayNo = resJson.getString("out_trade_no");
String tradeState = resJson.getString("trade_state");
OrderTradeVo orderTrade = orderTradeService.getOne(new OrderTradeBo().setOrderPayNo(orderPayNo));
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));
JsapiNotifyModel model = wxPayService.notify(
bodyJson.getString("timestamp"),
bodyJson.getString("nonce"),
bodyJson.getString("signature"),
bodyJson.getString("result")
);
//跟新云上相关信息
ysOrderService.payed(order.getOrderType(), order.getOrderNo());
}
//处理订单
dealOrder(model);
return true;
} catch (Exception e) {
......@@ -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 证书序列号
*/
private String serialNo;
//跟新主表信息
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));
/**
* 获取证书序列号
*
* @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));
}
//跟新云上相关信息
ysOrderService.payed(order.getOrderType(), order.getOrderNo());
}
return serialNo;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论