This commit is contained in:
777
2025-07-08 02:08:12 +08:00
parent 64518bf580
commit 9a72fe0151
6 changed files with 283 additions and 1 deletions

View File

@@ -1,5 +1,9 @@
package com.ruoyi.cai.trdpay; package com.ruoyi.cai.trdpay;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.DigestUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
@@ -41,6 +45,24 @@ public class PayMd5Util {
return resp; return resp;
} }
public static MultiValueMap<String,String> createParamsOfMapMD5(Map<String, String> params, String key) {
return createParamsOfMapMD5(params,key,"","sign");
}
public static MultiValueMap<String,String> createParamsOfMapMD5(Map<String, String> params, String key, String keyPre, String signKey) {
MultiValueMap<String, String> resp = new LinkedMultiValueMap<>();
List<String> url = new ArrayList<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
url.add(entry.getKey() + "=" + entry.getValue());
resp.add(entry.getKey(), entry.getValue());
}
url = url.stream().sorted().collect(Collectors.toList());
String stringSignTemp = StringUtils.join(url, "&") + keyPre + key;
String sign = DigestUtil.md5Hex(stringSignTemp).toUpperCase();
resp.add(signKey,sign);
return resp;
}
public static String createParams(Map<String, String> params, String key) { public static String createParams(Map<String, String> params, String key) {
List<String> url = new ArrayList<>(); List<String> url = new ArrayList<>();
for (Map.Entry<String, String> entry : params.entrySet()) { for (Map.Entry<String, String> entry : params.entrySet()) {

View File

@@ -0,0 +1,69 @@
package com.ruoyi.cai.trdpay;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.Sign;
import cn.hutool.crypto.asymmetric.SignAlgorithm;
import cn.hutool.crypto.digest.DigestUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
public class PaySignUtil {
public static String createMD5ToUpperCase(Map<String, String> params,String subKey) {
return createMD5(params,subKey).toUpperCase();
}
public static String createMD5ToLowerCase(Map<String, String> params,String subKey) {
return createMD5(params,subKey).toLowerCase();
}
public static String createMD5(Map<String, String> params,String subKey) {
List<String> url = new ArrayList<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
url.add(entry.getKey() + "=" + entry.getValue());
}
url = url.stream().sorted().collect(Collectors.toList());
String stringSignTemp = StringUtils.join(url, "&") + subKey;
return DigestUtil.md5Hex(stringSignTemp);
}
public static boolean checkRSA256(Map<String, String> params, String privateKey, String publicKey,String sign,String ... ignoreKeys) {
if(sign == null){
return false;
}
Set<String> ignoreKeySet = ignoreKeys == null ? new HashSet<>() : new HashSet<>(Arrays.asList(ignoreKeys));
List<String> url = new ArrayList<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
if(ignoreKeySet.contains(entry.getKey())){
continue;
}
url.add(entry.getKey() + "=" + entry.getValue());
}
url = url.stream().sorted().collect(Collectors.toList());
String stringSignTemp = StringUtils.join(url, "&");
String signLLL = rsa256(stringSignTemp, privateKey, publicKey);
return signLLL.equals(sign);
}
public static String createRSA256(Map<String, String> params, String privateKey, String publicKey) {
List<String> url = new ArrayList<>();
for (Map.Entry<String, String> entry : params.entrySet()) {
url.add(entry.getKey() + "=" + entry.getValue());
}
url = url.stream().sorted().collect(Collectors.toList());
String stringSignTemp = StringUtils.join(url, "&");
return rsa256(stringSignTemp,privateKey,publicKey);
}
public static String rsa256(String privateKey,String publicKey,String data) {
String privateKeyBase = Base64.encode(privateKey);
String publicKeyBase = Base64.encode(publicKey);
Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, privateKeyBase, publicKeyBase);
String s = sign.signHex(data);
return Base64.encode(s);
}
}

View File

@@ -128,7 +128,11 @@ public enum TrdPayTypeEnum {
* 支付宝 8 微信 4 * 支付宝 8 微信 4
*/ */
V11("http://bgxa.peiqi.zhifusg.com","/Pay_Index.html","/Pay_Trade_query.html","/api/pay/trd/notify/v11","OK"), V11("http://bgxa.peiqi.zhifusg.com","/Pay_Index.html","/Pay_Trade_query.html","/api/pay/trd/notify/v11","OK"),
V12("https://cashapi.sandpay.com.cn","/gateway/trade","/Pay_Trade_query.html","/api/pay/trd/notify/v11","OK"), V12("https://cashapi.sandpay.com.cn","/gateway/trade","/Pay_Trade_query.html","/api/pay/trd/notify/v12","OK"),
/**
* https://pp123.bghyvwk.cn/doc/pay_create.html
*/
V13("https://pp123.bghyvwk.cn","/api/pay/create","/api/pay/query","/api/pay/trd/notify/v12","success"),
; ;
private final String gatewayUrl; private final String gatewayUrl;

View File

@@ -0,0 +1,158 @@
package com.ruoyi.cai.trdpay.handle;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.cai.domain.PayTrdConfig;
import com.ruoyi.cai.pay.PayManager;
import com.ruoyi.cai.pay.PayOrderInfoDTO;
import com.ruoyi.cai.pay.PayReturnResp;
import com.ruoyi.cai.service.OrderLogsService;
import com.ruoyi.cai.service.PayTrdConfigService;
import com.ruoyi.cai.trdpay.PayMd5Util;
import com.ruoyi.cai.trdpay.PaySignUtil;
import com.ruoyi.cai.trdpay.PayTrdService;
import com.ruoyi.cai.trdpay.TrdPayTypeEnum;
import com.ruoyi.cai.trdpay.dto.NotifyResp;
import com.ruoyi.cai.util.MapUtils;
import com.ruoyi.cai.util.RestTemplateUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import java.util.HashMap;
import java.util.Map;
@Service
@Slf4j
public class PayTrdV13Service implements PayTrdService {
@Autowired
private OrderLogsService orderLogsService;
@Autowired
private PayManager payManager;
@Autowired
private PayTrdConfigService payTrdConfigService;
@Override
public TrdPayTypeEnum getType() {
return TrdPayTypeEnum.V13;
}
public static final String PUBLIC_KEY = "";
private String v1(PayOrderInfoDTO payOrderInfoDTO, PayTrdConfig payTrdConfig, boolean wx){
TrdPayTypeEnum type = getType();
Map<String, String> params = new HashMap<>();
params.put("pid", payTrdConfig.getMchId());
params.put("device", "mobile");
if(wx){
params.put("type", "wxpay");
}else{
params.put("type", "alipay");
}
params.put("out_trade_no", payOrderInfoDTO.getOrderNo());
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
params.put("notify_url", notifyUrl);
// params.put("return_url", notifyUrl);
params.put("name", payOrderInfoDTO.getSubject());
params.put("money", payOrderInfoDTO.getPriceYuanStr());
params.put("clientip", ServletUtils.getClientIP());
String sign = PaySignUtil.createMD5ToLowerCase(params, "");
MultiValueMap<String, String> multiValueMap = MapUtils.mapToMultiValueMap(params);
multiValueMap.add("sign", sign);
multiValueMap.add("sign_type", "MD5");
String gatewayUrl = getGatewayUrl(payTrdConfig);
String createOrderUrl = gatewayUrl + type.getCreateOrderUrl();
return RestTemplateUtil.postFormData(createOrderUrl, multiValueMap);
}
@Override
public PayReturnResp createOrderAli(PayOrderInfoDTO payOrderInfoDTO, PayTrdConfig payTrdConfig, boolean wx) {
TrdPayTypeEnum type = getType();
Map<String, String> params = new HashMap<>();
params.put("param", payTrdConfig.getId()+"");
params.put("pid", payTrdConfig.getMchId());
params.put("method", "jump");
params.put("device", "mobile");
if(wx){
params.put("type", "wxpay");
}else{
params.put("type", "alipay");
}
params.put("out_trade_no", payOrderInfoDTO.getOrderNo());
String notifyUrl = type.getNotifyUrl(payTrdConfig.getNotifyUrl(), wx);
params.put("notify_url", notifyUrl);
// params.put("return_url", notifyUrl);
params.put("name", payOrderInfoDTO.getSubject());
params.put("money", payOrderInfoDTO.getPriceYuanStr());
params.put("clientip", ServletUtils.getClientIP());
params.put("timestamp", System.currentTimeMillis()/1000+"");
String rsa = PaySignUtil.createRSA256(params, payTrdConfig.getSign(), PUBLIC_KEY);
MultiValueMap<String, String> multiValueMap = MapUtils.mapToMultiValueMap(params);
MultiValueMap<String, String> map = PayMd5Util.createParamsOfMapMD5(params, payTrdConfig.getSign());
multiValueMap.add("sign", rsa);
multiValueMap.add("sign_type", "RSA");
String gatewayUrl = getGatewayUrl(payTrdConfig);
String createOrderUrl = gatewayUrl + type.getCreateOrderUrl();
String body = RestTemplateUtil.postFormData(createOrderUrl, multiValueMap);
JSONObject jsonObject = JSON.parseObject(body);
boolean success = checkSuccess(jsonObject);
orderLogsService.createAliPayLogs(payOrderInfoDTO.getOrderNo(), createOrderUrl+JSON.toJSONString(map), jsonObject, success, type, getStepName(wx));
if(!success){
log.info("第三方支付失败 V13 统一支付失败失败 url={} params={} body={}, payTrdConfig={}", createOrderUrl,JSON.toJSONString(map), body, JSON.toJSONString(payTrdConfig));
throw new ServiceException("调用支付失败");
}
String payUrl = jsonObject.getString("pay_info");
return PayReturnResp.createH5(payUrl);
}
private boolean checkSuccess(JSONObject jsonObject) {
if(jsonObject == null){
return false;
}
if(!"0".equals(jsonObject.getString("code"))){
return false;
}
return true;
}
@Override
public NotifyResp getNotifyResp(Map<String, String> sourceData) {
String sign = sourceData.get("sign");
String mchOrderNo = sourceData.get("trade_no");
String payOrderId = sourceData.get("out_trade_no");
String status = sourceData.get("trade_status");
String params = sourceData.get("params");
NotifyResp resp = new NotifyResp();
resp.setOrderNo(mchOrderNo);
resp.setTrdOrderNo(payOrderId);
resp.setPayTypeEnum(getType());
resp.setSourceData(sourceData);
PayTrdConfig payTrdConfig = payTrdConfigService.getById(params);
if(payTrdConfig == null){
resp.setSuccess(false);
return resp;
}
boolean checked = PaySignUtil.checkRSA256(sourceData, PUBLIC_KEY, payTrdConfig.getSign(), sign, "sign", "sign_type");
if(!checked){
resp.setSuccess(false);
return resp;
}
resp.setSuccess("TRADE_SUCCESS".equals(status));
return resp;
}
@Override
public JSONObject queryOrder(String orderNo, PayTrdConfig payTrdConfig) {
throw new ServiceException("V13不支持订单查询");
}
@Override
public JSONObject resetOrder(String orderNo, PayTrdConfig payTrdConfig, boolean updateData) {
throw new ServiceException("V13不支持订单查询");
}
}

View File

@@ -0,0 +1,15 @@
package com.ruoyi.cai.util;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.util.Map;
public class MapUtils {
public static MultiValueMap<String, String> mapToMultiValueMap(Map<String, String> map) {
MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
map.forEach(multiValueMap::add);
return multiValueMap;
}
}

View File

@@ -71,4 +71,18 @@ public class RestTemplateUtil {
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
return restTemplate.postForObject(url, request, String.class); return restTemplate.postForObject(url, request, String.class);
} }
public static String postFormData(String url, MultiValueMap<String, String> map) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
return restTemplate.postForObject(url, request, String.class);
}
public static String postFormDataNoSSL(String url, MultiValueMap<String, String> map) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
return NO_SSL_REST_TEMPLATE.postForObject(url, request, String.class);
}
} }