Merge branch 'main' of https://dd3skj.picp.vip/zhengziru/zhgh_hrbnu
This commit is contained in:
@@ -238,6 +238,7 @@ public class BankChinaWxPayService {
|
||||
String wcPayDataSignType = XmlUtil.getNodeText(document, "wcPayDataSignType");
|
||||
// 签名
|
||||
String wcPayDataPaySign = XmlUtil.getNodeText(document, "wcPayDataPaySign");
|
||||
String qrCode = XmlUtil.getNodeText(document, "qrCode");
|
||||
return new WxAppPayResponse().setOrderSeq(orderSeq)
|
||||
.setTranStatus(tranStatus)
|
||||
.setTradeType(tradeType)
|
||||
@@ -251,7 +252,7 @@ public class BankChinaWxPayService {
|
||||
.setWcPayDataNonceStr(wcPayDataNonceStr)
|
||||
.setWcPayDataPackage(wcPayDataPackage)
|
||||
.setWcPayDataSignType(wcPayDataSignType)
|
||||
.setWcPayDataPaySign(wcPayDataPaySign);
|
||||
.setWcPayDataPaySign(wcPayDataPaySign).setQrCode(qrCode);
|
||||
}
|
||||
|
||||
private void checkConfigPayProperties() {
|
||||
|
||||
@@ -2,24 +2,25 @@ package io.v.nutz.pay.bank.china.controller;
|
||||
|
||||
import io.v.nutz.pay.annotations.Api;
|
||||
import io.v.nutz.pay.annotations.ApiOperation;
|
||||
import io.v.nutz.pay.bank.china.property.ChinaPayProperties;
|
||||
import io.v.nutz.pay.bank.china.sign.BankPaySign;
|
||||
import io.v.nutz.pay.cache.ChinaBankOrderWaitPayCacheService;
|
||||
import io.v.nutz.pay.record.service.IMemberPayRecordService;
|
||||
import io.v.nutz.pay.util.XmlUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.integration.jedis.RedisService;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 支付通知 controller
|
||||
*/
|
||||
@Slf4j
|
||||
@IocBean
|
||||
@Api("支付通知")
|
||||
@At("/notify")
|
||||
@Slf4j
|
||||
public class BankNotifyController
|
||||
{
|
||||
|
||||
@@ -34,6 +35,9 @@ public class BankNotifyController
|
||||
@Inject
|
||||
private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService;
|
||||
|
||||
@Inject
|
||||
private ChinaPayProperties chinaPayProperties;
|
||||
|
||||
|
||||
@Ok("json")
|
||||
@At("/china/bank/pay")
|
||||
@@ -45,6 +49,42 @@ public class BankNotifyController
|
||||
String signature = request.getParameter("signature");
|
||||
log.info("[message]=[" + message + "]");
|
||||
log.info("[signature]=[" + signature + "]");
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
String plainText = new String(io.v.nutz.pay.util.Base64.decode(message), "UTF-8");
|
||||
log.debug("[base64解码后message]=[" + plainText + "]");
|
||||
|
||||
chinaPayProperties.load();
|
||||
boolean isValid = BankPaySign.getInstance().verifySignature(
|
||||
chinaPayProperties.getVerifyCerFile(),
|
||||
signature,
|
||||
plainText
|
||||
);
|
||||
|
||||
if(!isValid) {
|
||||
log.error("[VERIFY FAILED] 中国银行支付通知验签失败");
|
||||
throw new RuntimeException("签名验证失败");
|
||||
}
|
||||
|
||||
log.info("[VERIFY OK] 中国银行支付通知验签成功");
|
||||
|
||||
Document document = XmlUtil.createDocument(plainText);
|
||||
String responseCode = XmlUtil.getNodeText(document, "responseCode");
|
||||
if("OK".equals(responseCode)){
|
||||
String orderNo = XmlUtil.getNodeText(document, "orderNo");
|
||||
String tranStatus = XmlUtil.getNodeText(document, "tranStatus");
|
||||
String payCatalog = XmlUtil.getNodeText(document, "payCatalog");
|
||||
log.debug("[orderNo]=["+orderNo+"] [tranStatus]=["+ tranStatus+"] [payCatalog]=["+payCatalog+"]");
|
||||
request.setAttribute("result",responseCode+",Notice Success!");
|
||||
}
|
||||
}
|
||||
catch (Exception e){
|
||||
log.error("中国银行支付通知处理异常:{}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ public class WxAppPayResponse implements Serializable {
|
||||
// 银行订单流水号 银行的订单流水号(银行产生的订单唯一标识)
|
||||
private String orderSeq;
|
||||
|
||||
// 订单号
|
||||
private String orderNo;
|
||||
|
||||
// 下单状态 1:成功 2:失败
|
||||
private String tranStatus;
|
||||
|
||||
@@ -52,4 +55,7 @@ public class WxAppPayResponse implements Serializable {
|
||||
|
||||
// 签名
|
||||
private String wcPayDataPaySign;
|
||||
|
||||
// 二维码
|
||||
private String qrCode;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,14 @@ import cn.hutool.core.util.StrUtil;
|
||||
import io.v.nutz.pay.util.PKCS7Tool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 中国银行支付签名
|
||||
*/
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Enumeration;
|
||||
|
||||
@Slf4j
|
||||
public class BankPaySign {
|
||||
|
||||
@@ -15,14 +20,6 @@ public class BankPaySign {
|
||||
return new BankPaySign();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名数据
|
||||
* @param signKeyStorePath 证书库路径
|
||||
* @param signKeyStorePassword 证书库口令
|
||||
* @param signKeyPassword 签名私钥口令,与证书库口令相同
|
||||
* @param businessParams 业务参数 参数格式 param|param|param
|
||||
* @return signData
|
||||
*/
|
||||
public String getSinData(String signKeyStorePath, String signKeyStorePassword, String signKeyPassword, String businessParams) {
|
||||
if(StrUtil.isBlank(signKeyStorePath)) {
|
||||
throw new RuntimeException("缺少证书库路径");
|
||||
@@ -43,12 +40,59 @@ public class BankPaySign {
|
||||
byte[] businessDataBytes = businessParams.getBytes("UTF-8");
|
||||
String signData = tool.signature(signKeyStorePath, signKeyPassword, businessDataBytes);
|
||||
log.info("签名数据:signData-{}", signData);
|
||||
// Boolean signDataBoolean = tool.verifySignature(signKeyStorePath, signKeyPassword, businessDataBytes, Base64.getDecoder().decode(signData));
|
||||
// log.info("签名数据生成状态: signDataBoolean-{}", signDataBoolean);
|
||||
return signData;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean verifySignature(String verifyCerFilePath, String signature, String plainText) {
|
||||
if(StrUtil.isBlank(verifyCerFilePath)) {
|
||||
throw new RuntimeException("缺少验签证书文件路径");
|
||||
}
|
||||
if(StrUtil.isBlank(signature)) {
|
||||
throw new RuntimeException("缺少签名数据");
|
||||
}
|
||||
if(plainText == null) {
|
||||
plainText = "";
|
||||
}
|
||||
log.info("验签信息:verifyCerFilePath-{}, signature-{}, plainText-{}", verifyCerFilePath, signature, plainText);
|
||||
|
||||
try {
|
||||
byte[] plainTextBytes = plainText.getBytes("UTF-8");
|
||||
byte[] signatureBytes = io.v.nutz.pay.util.Base64.decode(signature);
|
||||
|
||||
InputStream fis4cer = new FileInputStream(verifyCerFilePath);
|
||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
keyStore.load(fis4cer, null);
|
||||
fis4cer.close();
|
||||
|
||||
Enumeration<String> aliases = keyStore.aliases();
|
||||
String alias = null;
|
||||
while (aliases.hasMoreElements()) {
|
||||
alias = aliases.nextElement();
|
||||
}
|
||||
|
||||
Certificate cert = keyStore.getCertificate(alias);
|
||||
PublicKey publicKey = cert.getPublicKey();
|
||||
|
||||
Signature sig = Signature.getInstance("SHA256withRSA");
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(plainTextBytes);
|
||||
boolean isValid = sig.verify(signatureBytes);
|
||||
|
||||
if(isValid) {
|
||||
log.info("[VERIFY OK] 中国银行支付验签成功");
|
||||
} else {
|
||||
log.error("[VERIFY FAILED] 中国银行支付验签失败");
|
||||
}
|
||||
|
||||
return isValid;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("中国银行支付验签异常:{}", e.getMessage());
|
||||
throw new RuntimeException("验签失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,4 +113,9 @@ public class MemberPayRecord implements Serializable {
|
||||
@Column(value = "device_ip")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String deviceIp;
|
||||
|
||||
@Comment("设备IP")
|
||||
@Column(value = "qr_code")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String qrCode;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.v.nutz.base.service.impl.BaseServiceImpl;
|
||||
import io.v.nutz.pay.bank.china.BankChinaWxPayService;
|
||||
import io.v.nutz.pay.bank.china.pojo.WxAppPayResponse;
|
||||
import io.v.nutz.pay.bank.china.pojo.WxPayOrderQueryResponse;
|
||||
import io.v.nutz.pay.bank.china.property.ChinaPayProperties;
|
||||
import io.v.nutz.pay.cache.ChinaBankOrderPayNotifyCacheService;
|
||||
@@ -20,7 +21,11 @@ import io.v.nutz.pay.order.service.IMemberPaymentOrderService;
|
||||
import io.v.nutz.pay.record.module.MemberPayRecord;
|
||||
import io.v.nutz.pay.record.service.IMemberPayRecordService;
|
||||
import io.v.nutz.pay.util.BillNoUtil;
|
||||
import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo;
|
||||
import io.v.nutz.zhgh.memberCost.service.MemberPaymentService;
|
||||
import io.v.nutz.zhgh.memberCost.service.impl.MemberPaymentServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
@@ -31,7 +36,8 @@ import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord> implements IMemberPayRecordService {
|
||||
public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord> implements IMemberPayRecordService
|
||||
{
|
||||
|
||||
@Inject
|
||||
private ChinaPayProperties chinaPayProperties;
|
||||
@@ -42,12 +48,16 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
@Inject
|
||||
private BankChinaWxPayService bankChinaWxPayService;
|
||||
|
||||
|
||||
@Inject
|
||||
private MemberPaymentService memberPaymentService;
|
||||
@Inject
|
||||
private ChinaBankOrderWaitPayCacheService chinaBankOrderWaitPayCacheService;
|
||||
|
||||
@Inject
|
||||
private ChinaBankOrderPayNotifyCacheService chinaBankOrderPayNotifyCacheService;
|
||||
|
||||
|
||||
public MemberPayRecordServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
@@ -57,9 +67,12 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
Sql sql = Sqls.create("select * from member_pay_record where pay_code = @pay_code limit 0,1");
|
||||
sql.setParam("pay_code", code);
|
||||
List<MemberPayRecord> records = this.listEntity(sql);
|
||||
if(records != null && records.size() > 0) {
|
||||
if (records != null && records.size() > 0)
|
||||
{
|
||||
return records.get(0);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -69,9 +82,12 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
Sql sql = Sqls.create("select * from member_pay_record where id = @id limit 0,1");
|
||||
sql.params().set("id", id);
|
||||
List<MemberPayRecord> records = this.listEntity(sql);
|
||||
if(records != null && records.size() > 0) {
|
||||
if (records != null && records.size() > 0)
|
||||
{
|
||||
return records.get(0);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -81,9 +97,12 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
Sql sql = Sqls.create("select * from member_pay_record where goods_trade_code = @goods_trade_code limit 0,1");
|
||||
sql.params().set("goods_trade_code", goodsTradeCode);
|
||||
List<MemberPayRecord> records = this.listEntity(sql);
|
||||
if(records != null && records.size() > 0) {
|
||||
if (records != null && records.size() > 0)
|
||||
{
|
||||
return records.get(0);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -92,7 +111,8 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
public List<MemberPayRecord> getByGoodsTradeCode(Object... goodsTradeOrders) {
|
||||
Sql sql = Sqls.create("select * from member_pay_record where $goodsTradeOrders");
|
||||
StringBuilder where = new StringBuilder();
|
||||
for(int i = 0; i < goodsTradeOrders.length; i++) {
|
||||
for (int i = 0; i < goodsTradeOrders.length; i++)
|
||||
{
|
||||
where.append("goods_trade_code='" + goodsTradeOrders[i].toString() + "' or ");
|
||||
}
|
||||
String whereStr = where.toString().substring(0, where.toString().lastIndexOf("or"));
|
||||
@@ -117,25 +137,34 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
record.setDeviceIp(deviceIp);
|
||||
|
||||
// 中国银行支付中商品交易单号需要特殊处理 该单号用于发起支付-> 商户订单号
|
||||
if(PayChannelEnum.CHINA_BANK.getCode().equals(payChannel)) {
|
||||
if (PayChannelEnum.CHINA_BANK.getCode().equals(payChannel))
|
||||
{
|
||||
record.setGoodsTradeCode(chinaPayProperties.getMerchantNoAfterChar(6).concat(BillNoUtil.getTimeNo(24)));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
record.setGoodsTradeCode(record.getPayCode());
|
||||
}
|
||||
|
||||
// 会员工会缴费
|
||||
if(MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(businessType)) {
|
||||
// 会员工会缴费订单信息
|
||||
MemberPaymentOrder paymentOrder = memberPaymentOrderService.getById(businessId);
|
||||
if(paymentOrder == null) {
|
||||
throw new RuntimeException("未获取到会员工会缴费订单信息");
|
||||
}
|
||||
record.setBusinessOrderCode(paymentOrder.getOrderCode());
|
||||
record.setPayMoney(paymentOrder.getMoney());
|
||||
record.setMemberId(paymentOrder.getMemberId());
|
||||
record.setPayRemark("工会会员缴费-订单:" + paymentOrder.getOrderCode());
|
||||
if (MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(businessType))
|
||||
{
|
||||
|
||||
} else {
|
||||
List<MemberPaymentInfo> paymentUserList = memberPaymentService.query(Cnd.where("id", "=", businessId));
|
||||
if (paymentUserList == null || paymentUserList.isEmpty())
|
||||
{
|
||||
throw new RuntimeException("缴费记录未找到");
|
||||
}
|
||||
MemberPaymentInfo memberPaymentInfo = paymentUserList.get(0);
|
||||
|
||||
record.setBusinessOrderCode(memberPaymentInfo.getTradeNo());
|
||||
record.setPayMoney(Double.valueOf(memberPaymentInfo.getPaymentMoney()));
|
||||
record.setMemberId(memberPaymentInfo.getUserId());
|
||||
record.setPayRemark("工会会员缴费-订单:" + memberPaymentInfo.getLoginname());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("不支持的支付业务");
|
||||
}
|
||||
this.insert(record);
|
||||
@@ -145,7 +174,8 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
@Override
|
||||
public void paySuccess(String goodsTradeCode, Double oriPayAmount) {
|
||||
MemberPayRecord record = this.getByGoodsTradeCode(goodsTradeCode);
|
||||
if(record == null) {
|
||||
if (record == null)
|
||||
{
|
||||
throw new RuntimeException("商品交易单号不存在");
|
||||
}
|
||||
record.setStatus(MemberPayStatusEnum.SUCCESS.getCode());
|
||||
@@ -153,10 +183,13 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
this.updateById(record);
|
||||
|
||||
// 会员工会缴费
|
||||
if(MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType())) {
|
||||
if (MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType()))
|
||||
{
|
||||
// 支付成功
|
||||
memberPaymentOrderService.paySuccess(record.getBusinessId(), oriPayAmount);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("不支持的业务支付通知");
|
||||
}
|
||||
}
|
||||
@@ -164,7 +197,8 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
@Override
|
||||
public void payFail(String goodsTradeCode, String failReason) {
|
||||
MemberPayRecord record = this.getByGoodsTradeCode(goodsTradeCode);
|
||||
if(record == null) {
|
||||
if (record == null)
|
||||
{
|
||||
throw new RuntimeException("商品交易单号不存在");
|
||||
}
|
||||
record.setStatus(MemberPayStatusEnum.FAIL.getCode());
|
||||
@@ -173,73 +207,93 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
this.updateById(record);
|
||||
|
||||
// 会员工会缴费
|
||||
if(MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType())) {
|
||||
if (MemberPayBusinessTypeEnum.UNION_PAYMENT.getCode().equals(record.getBusinessType()))
|
||||
{
|
||||
// 支付成功
|
||||
memberPaymentOrderService.payFail(record.getBusinessId());
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("不支持的业务支付通知");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkChinaPayOrderStatus() {
|
||||
try {
|
||||
try
|
||||
{
|
||||
// 初始化配置参数
|
||||
chinaPayProperties.load();
|
||||
|
||||
String goodsTradeOrder = chinaBankOrderWaitPayCacheService.get();
|
||||
if (StrUtil.isBlank(goodsTradeOrder)) {
|
||||
if (StrUtil.isBlank(goodsTradeOrder))
|
||||
{
|
||||
log.info("未检测中国银行支付订单:null");
|
||||
return;
|
||||
}
|
||||
List<String> goodsTradeOrders = JSON.parseArray(goodsTradeOrder, String.class);
|
||||
log.info("检测中国银行支付订单:{}", goodsTradeOrders.size());
|
||||
if (goodsTradeOrders.size() == 0) {
|
||||
if (goodsTradeOrders.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
List<MemberPayRecord> payRecords = this.getByGoodsTradeCode(goodsTradeOrders.toArray());
|
||||
if (payRecords == null || payRecords.isEmpty()) {
|
||||
if (payRecords == null || payRecords.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (MemberPayRecord record : payRecords) {
|
||||
if (!record.getStatus().equals(MemberPayStatusEnum.WAIT.getCode())) {
|
||||
for (MemberPayRecord record : payRecords)
|
||||
{
|
||||
if (!record.getStatus().equals(MemberPayStatusEnum.WAIT.getCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!record.getPayChannel().equals(PayChannelEnum.CHINA_BANK.getCode())) {
|
||||
if (!record.getPayChannel().equals(PayChannelEnum.CHINA_BANK.getCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
WxPayOrderQueryResponse response = bankChinaWxPayService.queryOrder(record.getGoodsTradeCode());
|
||||
if(chinaBankOrderPayNotifyCacheService.exists(record.getGoodsTradeCode())) {
|
||||
if (chinaBankOrderPayNotifyCacheService.exists(record.getGoodsTradeCode()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
chinaBankOrderPayNotifyCacheService.setex(record.getGoodsTradeCode(), 30, record.getGoodsTradeCode());
|
||||
if(response.getTranStatus() != null
|
||||
&&(response.getTranStatus().equals(CommonConstants.ONE.toString()) || response.getTranStatus().equals(CommonConstants.TWO.toString()))) {
|
||||
if (response.getTranStatus() != null
|
||||
&& (response.getTranStatus().equals(CommonConstants.ONE.toString()) || response.getTranStatus().equals(CommonConstants.TWO.toString())))
|
||||
{
|
||||
// 支付成功
|
||||
if(response.getTranStatus().equals(CommonConstants.ONE.toString())) {
|
||||
if (response.getTranStatus().equals(CommonConstants.ONE.toString()))
|
||||
{
|
||||
this.paySuccess(response.getOrderNo(), Double.valueOf(response.getTranAmount()));
|
||||
|
||||
// 支付失败
|
||||
} else {
|
||||
// 支付失败
|
||||
}
|
||||
else
|
||||
{
|
||||
this.payFail(response.getOrderNo(), "支付失败");
|
||||
}
|
||||
chinaBankOrderWaitPayCacheService.delItem(record.getGoodsTradeCode());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
log.error("中国银行支付检测待支付订单异常:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private MemberPayRecord sendPay(MemberPayRecord record) {
|
||||
try {
|
||||
Object requestResult = null;
|
||||
try
|
||||
{
|
||||
WxAppPayResponse requestResult = null;
|
||||
// 中国银行支付渠道
|
||||
if (PayChannelEnum.CHINA_BANK.getCode().equals(record.getPayChannel())) {
|
||||
if (PayChannelEnum.CHINA_BANK.getCode().equals(record.getPayChannel()))
|
||||
{
|
||||
|
||||
// 微信APP支付
|
||||
if (ChannelTypeEnum.WX_APP.getCode().equals(record.getChannelType())) {
|
||||
if (ChannelTypeEnum.WX_APP.getCode().equals(record.getChannelType()))
|
||||
{
|
||||
// 获取中国银行微信支付拉起支付订单信息
|
||||
requestResult = bankChinaWxPayService.appPay(
|
||||
record.getGoodsTradeCode(),
|
||||
@@ -251,14 +305,21 @@ public class MemberPayRecordServiceImpl extends BaseServiceImpl<MemberPayRecord>
|
||||
record.getDeviceIp());
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("不支持的支付方式");
|
||||
}
|
||||
if (requestResult != null) {
|
||||
if (requestResult != null)
|
||||
{
|
||||
record.setRequestResult(JSONObject.toJSONString(requestResult));
|
||||
record.setBusinessOrderCode(requestResult.getOrderSeq());
|
||||
record.setQrCode(requestResult.getQrCode());
|
||||
this.updateById(record);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.payFail(record.getGoodsTradeCode(), e.getMessage());
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.v.nutz.pay.util;
|
||||
|
||||
public final class Base64 {
|
||||
private static final Base64Coder coder = new Base64Coder();
|
||||
|
||||
public static String encode(byte abyte0[]) {
|
||||
return coder.encode(abyte0);
|
||||
}
|
||||
|
||||
public static byte[] decode(String s) {
|
||||
return coder.decode(s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package io.v.nutz.pay.util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* @author
|
||||
*
|
||||
* @modified_by
|
||||
*/
|
||||
public final class Base64Coder {
|
||||
|
||||
protected static boolean isWhiteSpace(char c) {
|
||||
return c == ' ' || c == '\r' || c == '\n' || c == '\t';
|
||||
}
|
||||
|
||||
protected static boolean isPad(char c) {
|
||||
return c == '=';
|
||||
}
|
||||
|
||||
protected static boolean isData(char c) {
|
||||
return c < '\200' && base64Alphabet[c] != -1;
|
||||
}
|
||||
|
||||
protected static boolean isBase64(char c) {
|
||||
return isWhiteSpace(c) || isPad(c) || isData(c);
|
||||
}
|
||||
|
||||
public String encode(byte abyte0[]) {
|
||||
if (abyte0 == null)
|
||||
return null;
|
||||
int i = abyte0.length * 8;
|
||||
if (i == 0)
|
||||
return "";
|
||||
int j = i % 24;
|
||||
int k = i / 24;
|
||||
int l = j == 0 ? k : k + 1;
|
||||
char ac[] = null;
|
||||
ac = new char[l * 4];
|
||||
int i1 = 0;
|
||||
int j1 = 0;
|
||||
for (int k1 = 0; k1 < k; k1++) {
|
||||
byte byte5 = abyte0[j1++];
|
||||
byte byte8 = abyte0[j1++];
|
||||
byte byte10 = abyte0[j1++];
|
||||
byte byte3 = (byte) (byte8 & 15);
|
||||
byte byte0 = (byte) (byte5 & 3);
|
||||
byte byte11 = (byte5 & -128) != 0 ? (byte) (byte5 >> 2 ^ 192) : (byte) (byte5 >> 2);
|
||||
byte byte14 = (byte8 & -128) != 0 ? (byte) (byte8 >> 4 ^ 240) : (byte) (byte8 >> 4);
|
||||
byte byte16 = (byte10 & -128) != 0 ? (byte) (byte10 >> 6 ^ 252) : (byte) (byte10 >> 6);
|
||||
ac[i1++] = lookUpBase64Alphabet[byte11];
|
||||
ac[i1++] = lookUpBase64Alphabet[byte14 | byte0 << 4];
|
||||
ac[i1++] = lookUpBase64Alphabet[byte3 << 2 | byte16];
|
||||
ac[i1++] = lookUpBase64Alphabet[byte10 & 63];
|
||||
}
|
||||
|
||||
if (j == 8) {
|
||||
byte byte6 = abyte0[j1];
|
||||
byte byte1 = (byte) (byte6 & 3);
|
||||
byte byte12 = (byte6 & -128) != 0 ? (byte) (byte6 >> 2 ^ 192) : (byte) (byte6 >> 2);
|
||||
ac[i1++] = lookUpBase64Alphabet[byte12];
|
||||
ac[i1++] = lookUpBase64Alphabet[byte1 << 4];
|
||||
ac[i1++] = '=';
|
||||
ac[i1++] = '=';
|
||||
} else if (j == 16) {
|
||||
byte byte7 = abyte0[j1];
|
||||
byte byte9 = abyte0[j1 + 1];
|
||||
byte byte4 = (byte) (byte9 & 15);
|
||||
byte byte2 = (byte) (byte7 & 3);
|
||||
byte byte13 = (byte7 & -128) != 0 ? (byte) (byte7 >> 2 ^ 192) : (byte) (byte7 >> 2);
|
||||
byte byte15 = (byte9 & -128) != 0 ? (byte) (byte9 >> 4 ^ 240) : (byte) (byte9 >> 4);
|
||||
ac[i1++] = lookUpBase64Alphabet[byte13];
|
||||
ac[i1++] = lookUpBase64Alphabet[byte15 | byte2 << 4];
|
||||
ac[i1++] = lookUpBase64Alphabet[byte4 << 2];
|
||||
ac[i1++] = '=';
|
||||
}
|
||||
return new String(ac);
|
||||
}
|
||||
|
||||
public byte[] decode(String s) {
|
||||
if (s == null)
|
||||
return null;
|
||||
char ac[] = s.toCharArray();
|
||||
int i = removeWhiteSpace(ac);
|
||||
if (i % 4 != 0)
|
||||
return null;
|
||||
int j = i / 4;
|
||||
if (j == 0)
|
||||
return new byte[0];
|
||||
byte abyte0[] = null;
|
||||
byte byte0 = 0;
|
||||
byte byte1 = 0;
|
||||
char c = '\0';
|
||||
char c1 = '\0';
|
||||
char c2 = '\0';
|
||||
char c3 = '\0';
|
||||
int k = 0;
|
||||
int l = 0;
|
||||
int i1 = 0;
|
||||
abyte0 = new byte[j * 3];
|
||||
for (; k < j - 1; k++) {
|
||||
if (!isData(c = ac[i1++]) || !isData(c1 = ac[i1++]) || !isData(c2 = ac[i1++]) || !isData(c3 = ac[i1++]))
|
||||
return null;
|
||||
byte0 = base64Alphabet[c];
|
||||
byte1 = base64Alphabet[c1];
|
||||
byte byte2 = base64Alphabet[c2];
|
||||
byte byte5 = base64Alphabet[c3];
|
||||
abyte0[l++] = (byte) (byte0 << 2 | byte1 >> 4);
|
||||
abyte0[l++] = (byte) ((byte1 & 15) << 4 | byte2 >> 2 & 15);
|
||||
abyte0[l++] = (byte) (byte2 << 6 | byte5);
|
||||
}
|
||||
|
||||
if (!isData(c = ac[i1++]) || !isData(c1 = ac[i1++]))
|
||||
return null;
|
||||
byte0 = base64Alphabet[c];
|
||||
byte1 = base64Alphabet[c1];
|
||||
c2 = ac[i1++];
|
||||
c3 = ac[i1++];
|
||||
if (!isData(c2) || !isData(c3)) {
|
||||
if (isPad(c2) && isPad(c3)) {
|
||||
if ((byte1 & 15) != 0)
|
||||
return null;
|
||||
byte abyte1[] = new byte[k * 3 + 1];
|
||||
System.arraycopy(abyte0, 0, abyte1, 0, k * 3);
|
||||
abyte1[l] = (byte) (byte0 << 2 | byte1 >> 4);
|
||||
return abyte1;
|
||||
}
|
||||
if (isPad(c2) && isPad(c3))
|
||||
return null;
|
||||
byte byte3 = base64Alphabet[c2];
|
||||
if ((byte3 & 3) != 0)
|
||||
return null;
|
||||
byte abyte2[] = new byte[k * 3 + 2];
|
||||
System.arraycopy(abyte0, 0, abyte2, 0, k * 3);
|
||||
abyte2[l++] = (byte) (byte0 << 2 | byte1 >> 4);
|
||||
abyte2[l] = (byte) ((byte1 & 15) << 4 | byte3 >> 2 & 15);
|
||||
return abyte2;
|
||||
}
|
||||
byte byte4 = base64Alphabet[c2];
|
||||
byte byte6 = base64Alphabet[c3];
|
||||
abyte0[l++] = (byte) (byte0 << 2 | byte1 >> 4);
|
||||
abyte0[l++] = (byte) ((byte1 & 15) << 4 | byte4 >> 2 & 15);
|
||||
abyte0[l++] = (byte) (byte4 << 6 | byte6);
|
||||
return abyte0;
|
||||
}
|
||||
|
||||
protected static int removeWhiteSpace(char ac[]) {
|
||||
if (ac == null)
|
||||
return 0;
|
||||
int i = 0;
|
||||
int j = ac.length;
|
||||
for (int k = 0; k < j; k++)
|
||||
if (!isWhiteSpace(ac[k]))
|
||||
ac[i++] = ac[k];
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
private static final byte base64Alphabet[];
|
||||
|
||||
private static final char lookUpBase64Alphabet[];
|
||||
|
||||
static {
|
||||
base64Alphabet = new byte[128];
|
||||
lookUpBase64Alphabet = new char[64];
|
||||
for (int i = 0; i < 128; i++)
|
||||
base64Alphabet[i] = -1;
|
||||
|
||||
for (int j = 90; j >= 65; j--)
|
||||
base64Alphabet[j] = (byte) (j - 65);
|
||||
|
||||
for (int k = 122; k >= 97; k--)
|
||||
base64Alphabet[k] = (byte) ((k - 97) + 26);
|
||||
|
||||
for (int l = 57; l >= 48; l--)
|
||||
base64Alphabet[l] = (byte) ((l - 48) + 52);
|
||||
|
||||
base64Alphabet[43] = 62;
|
||||
base64Alphabet[47] = 63;
|
||||
for (int i1 = 0; i1 <= 25; i1++)
|
||||
lookUpBase64Alphabet[i1] = (char) (65 + i1);
|
||||
|
||||
int j1 = 26;
|
||||
for (int k1 = 0; j1 <= 51; k1++) {
|
||||
lookUpBase64Alphabet[j1] = (char) (97 + k1);
|
||||
j1++;
|
||||
}
|
||||
|
||||
int l1 = 52;
|
||||
for (int i2 = 0; l1 <= 61; i2++) {
|
||||
lookUpBase64Alphabet[l1] = (char) (48 + i2);
|
||||
l1++;
|
||||
}
|
||||
|
||||
lookUpBase64Alphabet[62] = '+';
|
||||
lookUpBase64Alphabet[63] = '/';
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package io.v.nutz.pay.util;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.transform.Transformer;
|
||||
@@ -11,9 +14,15 @@ import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.sax.SAXSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.*;
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.InputSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class XmlUtil
|
||||
{
|
||||
@@ -64,6 +73,9 @@ public class XmlUtil
|
||||
public static Document createDocument(String s)
|
||||
throws Exception
|
||||
{
|
||||
// 预处理XML,修复未转义的&符号
|
||||
s = fixUnescapedAmpersands(s);
|
||||
|
||||
DocumentBuilder documentbuilder = createDocumentBuilder();
|
||||
return documentbuilder.parse(new ByteArrayInputStream(s.getBytes("UTF-8")));
|
||||
}
|
||||
@@ -71,6 +83,9 @@ public class XmlUtil
|
||||
public static Document createDocument(String s, String s1)
|
||||
throws Exception
|
||||
{
|
||||
// 预处理XML,修复未转义的&符号
|
||||
s = fixUnescapedAmpersands(s);
|
||||
|
||||
DocumentBuilder documentbuilder = createDocumentBuilder();
|
||||
return documentbuilder.parse(new ByteArrayInputStream(s.getBytes(s1)));
|
||||
}
|
||||
@@ -203,4 +218,69 @@ public class XmlUtil
|
||||
transformer.transform(domsource, new StreamResult(stringwriter));
|
||||
return stringwriter.toString();
|
||||
}
|
||||
|
||||
private static String fixUnescapedAmpersands(String xml) {
|
||||
if (xml == null || xml.isEmpty()) {
|
||||
return xml;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
int i = 0;
|
||||
int len = xml.length();
|
||||
|
||||
while (i < len) {
|
||||
char c = xml.charAt(i);
|
||||
|
||||
// 如果找到&符号
|
||||
if (c == '&') {
|
||||
// 检查是否是已经正确转义的实体(如 & < &#等)
|
||||
if (i + 1 < len) {
|
||||
int j = i + 1;
|
||||
// 查找分号
|
||||
boolean foundSemicolon = false;
|
||||
boolean isValidEntity = false;
|
||||
|
||||
while (j < len && j < i + 10) { // 实体引用通常不会太长
|
||||
if (xml.charAt(j) == ';') {
|
||||
foundSemicolon = true;
|
||||
// 检查&和;之间的内容是否是合法的实体名或数值引用
|
||||
String entityContent = xml.substring(i + 1, j);
|
||||
if (entityContent.startsWith("#") ||
|
||||
entityContent.equals("amp") ||
|
||||
entityContent.equals("lt") ||
|
||||
entityContent.equals("gt") ||
|
||||
entityContent.equals("quot") ||
|
||||
entityContent.equals("apos")) {
|
||||
isValidEntity = true;
|
||||
}
|
||||
break;
|
||||
} else if (xml.charAt(j) == '&' || xml.charAt(j) == '<' || xml.charAt(j) == '>') {
|
||||
// 遇到其他特殊字符,说明这不是一个有效的实体引用
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
// 如果是合法的实体引用,保持原样
|
||||
if (foundSemicolon && isValidEntity) {
|
||||
result.append(c);
|
||||
i++;
|
||||
} else {
|
||||
// 否则将&替换为&
|
||||
result.append("&");
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
// &是最后一个字符,替换为&
|
||||
result.append("&");
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
result.append(c);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,16 +157,16 @@ layout("/mobile/platform.html"){
|
||||
</template>
|
||||
|
||||
<van-radio-group checked-color="#1867b0" v-model="moneyModel">
|
||||
<van-cell @click="moneyModel = 1" clickable title="支付宝">
|
||||
<template #icon>
|
||||
<div class="pay_img" style="margin-right: 4px">
|
||||
<img src="/assets/mobile/svg/memberPay/zfb.svg"/>
|
||||
</div>
|
||||
</template>
|
||||
<template #right-icon>
|
||||
<van-radio :name="1"></van-radio>
|
||||
</template>
|
||||
</van-cell>
|
||||
<!-- <van-cell @click="moneyModel = 1" clickable title="支付宝">-->
|
||||
<!-- <template #icon>-->
|
||||
<!-- <div class="pay_img" style="margin-right: 4px">-->
|
||||
<!-- <img src="/assets/mobile/svg/memberPay/zfb.svg"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- <template #right-icon>-->
|
||||
<!-- <van-radio :name="1"></van-radio>-->
|
||||
<!-- </template>-->
|
||||
<!-- </van-cell>-->
|
||||
<van-cell @click="moneyModel = 2" clickable title="微信">
|
||||
<template #icon>
|
||||
<div class="pay_img" style="margin-right: 4px">
|
||||
@@ -405,52 +405,18 @@ layout("/mobile/platform.html"){
|
||||
return;
|
||||
}
|
||||
|
||||
// 微信支付 初始化微信JS-SDK配置
|
||||
if(paymentMethod.type === 'wx') {
|
||||
await this.loadWxJsConfig();
|
||||
const requestResult = JSON.parse(data.requestResult)
|
||||
|
||||
wx.ready(function () {
|
||||
if(requestResult.qrCode) {
|
||||
this.payToOtherPage = true
|
||||
setTimeout(() => {
|
||||
window.location.href = requestResult.qrCode
|
||||
}, 500)
|
||||
|
||||
// 中国银行 微信APP支付
|
||||
if(data.payChannel === 1 && data.channelType === 1) {
|
||||
const payParams = JSON.parse(data.requestResult)
|
||||
console.log("服务端获取的支付参数payParams->", payParams)
|
||||
const prepayData = {
|
||||
appId: payParams.wcPayDataAppId, // 自己的APPID
|
||||
timeStamp: payParams.wcPayDataTimeStamp, // 时间戳
|
||||
nonceStr: payParams.wcPayDataNonceStr, // 随机字符串
|
||||
package: payParams.wcPayDataPrepayId, // 预支付订单号
|
||||
signType: payParams.wcPayDataSignType, // 签名类型
|
||||
paySign: payParams.wcPayDataPaySign // 签名
|
||||
};
|
||||
console.log("调用微信API支付参数->", prepayData)
|
||||
// 调用微信支付API
|
||||
wx.chooseWXPay({
|
||||
timestamp: prepayData.timeStamp,
|
||||
nonceStr: prepayData.nonceStr,
|
||||
package: prepayData.package,
|
||||
signType: prepayData.signType,
|
||||
paySign: prepayData.paySign,
|
||||
success: function(res) {
|
||||
// 支付成功后的操作
|
||||
console.log('支付成功');
|
||||
setTimeout(()=> {
|
||||
this.paySuccess(res, data.goodsTradeCode)
|
||||
}, 500);
|
||||
},
|
||||
cancel: function(res) {
|
||||
// 用户取消支付的操作
|
||||
console.log('支付取消');
|
||||
},
|
||||
fail: function(res) {
|
||||
// 支付失败的操作
|
||||
console.log('支付失败');
|
||||
this.payFail(res, data.goodsTradeCode)
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
this.startPaymentPolling(data.goodsTradeCode)
|
||||
} else {
|
||||
vant.Toast.fail("订单支付链接不存在")
|
||||
}
|
||||
}
|
||||
}).catch((e) => {
|
||||
if(loading) {
|
||||
@@ -505,6 +471,41 @@ layout("/mobile/platform.html"){
|
||||
async payFail(res, goodsTradeCode) {
|
||||
await $.post('/member/pay/record/del/check/pay/code?payCode=' + goodsTradeCode)
|
||||
},
|
||||
async startPaymentPolling(goodsTradeCode) {
|
||||
let pollCount = 0
|
||||
const maxPollCount = 60
|
||||
|
||||
const pollInterval = setInterval(async () => {
|
||||
pollCount++
|
||||
|
||||
try {
|
||||
const res = await $.get('/member/pay/record/get/by/code?goodsTradeCode=' + goodsTradeCode)
|
||||
|
||||
if(res.code === 0 && res.data) {
|
||||
if(res.data.status === 1) {
|
||||
clearInterval(pollInterval)
|
||||
this.$toast.success('支付成功')
|
||||
this.payToOtherPage = false
|
||||
this.payVisible = false
|
||||
this.doSearch()
|
||||
return
|
||||
} else if(res.data.status === 2) {
|
||||
clearInterval(pollInterval)
|
||||
this.$toast.fail('支付失败')
|
||||
this.payToOtherPage = false
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if(pollCount >= maxPollCount) {
|
||||
clearInterval(pollInterval)
|
||||
this.$toast('支付结果查询超时,请稍后刷新查看')
|
||||
}
|
||||
} catch(e) {
|
||||
console.error('轮询支付状态失败', e)
|
||||
}
|
||||
}, 2000)
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
let nowYear = new Date().getFullYear()
|
||||
|
||||
Reference in New Issue
Block a user