diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayOrderInfoDTO.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayOrderInfoDTO.java index d616e932..3f6e8394 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayOrderInfoDTO.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/pay/PayOrderInfoDTO.java @@ -17,4 +17,8 @@ public class PayOrderInfoDTO { public String getPriceFenStr(){ return NumberUtil.mul(price,100).longValue()+""; } + + public String getPriceYuanStr(){ + return price.toString(); + } } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/PayMd5Util.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/PayMd5Util.java index 5bca85c6..51e07ebc 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/PayMd5Util.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/PayMd5Util.java @@ -2,6 +2,8 @@ package com.ruoyi.cai.trdpay; import cn.hutool.crypto.digest.DigestUtil; import org.apache.commons.lang3.StringUtils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import java.util.ArrayList; import java.util.List; @@ -9,6 +11,21 @@ import java.util.Map; import java.util.stream.Collectors; public class PayMd5Util { + public static MultiValueMap createParamsOfMap(Map params, String key) { + MultiValueMap resp = new LinkedMultiValueMap<>(); + List url = new ArrayList<>(); + for (Map.Entry 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, "&") + "&key=" + key; + String sign = DigestUtil.md5Hex(stringSignTemp).toUpperCase(); + url.add("sign=" + sign); + resp.add("sign",sign); + return resp; + } + public static String createParams(Map params, String key) { List url = new ArrayList<>(); for (Map.Entry entry : params.entrySet()) { diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/TrdPayTypeEnum.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/TrdPayTypeEnum.java index 6aaff35d..b8d84118 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/TrdPayTypeEnum.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/TrdPayTypeEnum.java @@ -66,6 +66,24 @@ public enum TrdPayTypeEnum { * 通道编码:8010 */ V6("https://ydwg.blzf.cc","/api/pay/create_order","/api/pay/query_order","/api/pay/trd/notify/v6","success"), + /** + * [商户号]: M1736166248 + * [用户名]: huaxiang + * [商户名称]: 花香 + * [API密钥]: 17ddac0a588280c6b0e1220b87bf3575 + * [默认密码]: Aa123456 + * + * [通道编码]: 888 + * [通道名称]: 支付宝 + * [金额]: 10 - 1000 + * + * [支付网关]: https://baihe.lhsifang.com/api/pay/create + * [查询地址]: https://baihe.lhsifang.com/api/pay/query + * [商户登录地址]: https://baihe.lhsifang.com/merchants + * [对接文档]: https://baihe.lhsifang.com/merchants/#/api-document + * [回调IP]: 142.171.122.90 + */ + V7("https://baihe.lhsifang.com","/api/pay/create","/api/pay/query","/api/pay/trd/notify/v7","success"), ; private final String gatewayUrl; private final String createOrderUrl; diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/handle/PayTrdV7Service.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/handle/PayTrdV7Service.java new file mode 100644 index 00000000..3c99b319 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/trdpay/handle/PayTrdV7Service.java @@ -0,0 +1,146 @@ +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.trdpay.PayMd5Util; +import com.ruoyi.cai.trdpay.PayTrdService; +import com.ruoyi.cai.trdpay.TrdPayTypeEnum; +import com.ruoyi.cai.trdpay.dto.NotifyResp; +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.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; + +@Service +@Slf4j +public class PayTrdV7Service implements PayTrdService { + + @Autowired + private OrderLogsService orderLogsService; + @Autowired + private PayManager payManager; + + @Override + public TrdPayTypeEnum getType() { + return TrdPayTypeEnum.V7; + } + + @Override + public PayReturnResp createOrderAli(PayOrderInfoDTO payOrderInfoDTO, PayTrdConfig payTrdConfig) { + TrdPayTypeEnum type = getType(); + RestTemplate rest = RestTemplateUtil.getNoSSLRest(); + Map params = new HashMap<>(); + params.put("merchantCode", payTrdConfig.getMchId()); + params.put("channelType", payTrdConfig.getAliProductId()); + params.put("merchantOrderNo", payOrderInfoDTO.getOrderNo()); + params.put("amount", payOrderInfoDTO.getPriceYuanStr()); + String notifyUrl = payTrdConfig.getNotifyUrl() + type.getNotifyPath(); + params.put("notifyUrl", notifyUrl); + params.put("ip", ServletUtils.getClientIP()); + params.put("title", payOrderInfoDTO.getSubject()); + params.put("describe", payOrderInfoDTO.getBody()); + MultiValueMap map = PayMd5Util.createParamsOfMap(params, payTrdConfig.getSign()); + String gatewayUrl = type.getGatewayUrl(); + if (StringUtils.isNotBlank(payTrdConfig.getGatewayUrl())) { + gatewayUrl = payTrdConfig.getGatewayUrl(); + } + String createOrderUrl = gatewayUrl + type.getCreateOrderUrl(); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + HttpEntity> request = new HttpEntity<>(map, headers); + String body = rest.postForObject(createOrderUrl, request, String.class); + JSONObject jsonObject = JSON.parseObject(body); + orderLogsService.createAliPayLogs(payOrderInfoDTO.getOrderNo(), createOrderUrl+JSON.toJSONString(map), jsonObject, type); + if(jsonObject == null){ + log.error("第三方支付失败 V7 返回数据为空 url={} params={}",createOrderUrl,JSON.toJSONString(map)); + throw new ServiceException("调用支付失败"); + } + if(!"1".equals(jsonObject.getString("code"))){ + log.info("第三方支付失败 V7 统一支付失败失败 url={} params={} body={}, payTrdConfig={}, typeEnum={}", createOrderUrl,JSON.toJSONString(map), body, JSON.toJSONString(payTrdConfig), JSON.toJSONString(jsonObject)); + throw new ServiceException("调用支付失败"); + } + String payUrl = jsonObject.getJSONObject("data").getString("payUrl"); + return PayReturnResp.createH5(payUrl); + } + + @Override + public NotifyResp getNotifyResp(Map sourceData) { + String mchOrderNo = sourceData.get("order_no"); // 第三方订单号 + String payOrderId = sourceData.get("merchant_order_no"); // 商户订单号 + String status = sourceData.get("status"); + NotifyResp resp = new NotifyResp(); + resp.setOrderNo(mchOrderNo); + resp.setTrdOrderNo(payOrderId); + resp.setPayTypeEnum(getType()); + resp.setSourceData(sourceData); + resp.setSuccess("1".equals(status)); + return resp; + } + + @Override + public JSONObject queryOrder(String orderNo, PayTrdConfig payTrdConfig) { + throw new ServiceException("V7不支持订单查询"); + /*TrdPayTypeEnum type = getType(); + Map params = new HashMap<>(); + params.put("merchantId", payTrdConfig.getMchId()); + params.put("merchantPayNo", orderNo); + String para = PayMd5Util.createParams(params,payTrdConfig.getSign()); + String gatewayUrl = type.getGatewayUrl(); + if(StringUtils.isNotBlank(payTrdConfig.getGatewayUrl())){ + gatewayUrl = payTrdConfig.getGatewayUrl(); + } + String queryOrderUrl = gatewayUrl + type.getQueryOrderUrl(); + String url = queryOrderUrl + "?" + para; + String body = RestTemplateUtil.getRest().getForEntity(url, String.class).getBody(); + JSONObject jsonObject = JSON.parseObject(body); + if(jsonObject == null){ + log.error("第三方支付查询失败 返回数据为空 URL={}",url); + throw new ServiceException("调用支付失败"); + } + log.info("第三方支付查询成功 V7 URL={}, body={}",url, body); + return jsonObject;*/ + } + + @Override + public JSONObject resetOrder(String orderNo, PayTrdConfig payTrdConfig, boolean updateData) { + throw new ServiceException("V7不支持订单查询"); + /*JSONObject jsonObject = this.queryOrder(orderNo,payTrdConfig); + if(!"SUCCESS".equals(jsonObject.getString("retCode"))){ + log.info("第三方支付失败 V7统一支付失败失败 orderNo={}, payTrdConfig={}", orderNo, JSON.toJSONString(payTrdConfig)); + throw new ServiceException("调用支付失败"); + } + Integer status = jsonObject.getInteger("status"); + if(status != null && status.equals(1)){ + String mchOrderNo = jsonObject.getString("mchOrderNo"); + String payOrderId = jsonObject.getString("payOrderId"); + String productId = jsonObject.getString("productId"); + Map objectJson = new HashMap<>(); + for (String key : jsonObject.keySet()) { + objectJson.put(key, jsonObject.getString(key)); + } + if(updateData){ + payManager.callBack(mchOrderNo,payOrderId,objectJson,"V7", PayTypeEnum.TRD); + } + } + return jsonObject;*/ + } + +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/util/CustomHttpClient.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/util/CustomHttpClient.java new file mode 100644 index 00000000..2563ea7c --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/util/CustomHttpClient.java @@ -0,0 +1,18 @@ +package com.ruoyi.cai.util; + +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.SSLContextBuilder; + +import javax.net.ssl.SSLContext; + +public class CustomHttpClient { + + public static CloseableHttpClient createClient() throws Exception { + SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (x509Certificates, s) -> true).build(); + SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); + return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build(); + } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/util/RestTemplateUtil.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/util/RestTemplateUtil.java index f9c39a97..f0c7b6cc 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/util/RestTemplateUtil.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/util/RestTemplateUtil.java @@ -1,6 +1,8 @@ package com.ruoyi.cai.util; +import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.http.client.BufferingClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; @@ -12,6 +14,7 @@ import java.util.List; public class RestTemplateUtil { public static RestTemplate restTemplate; + public static RestTemplate NO_SSL_REST_TEMPLATE; static { SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); @@ -26,10 +29,34 @@ public class RestTemplateUtil { converter.setDefaultCharset(StandardCharsets.UTF_8); } } + + CloseableHttpClient httpClient = null; + try { + httpClient = CustomHttpClient.createClient(); + HttpComponentsClientHttpRequestFactory requestFactoryNoSSL = new HttpComponentsClientHttpRequestFactory(httpClient); + requestFactoryNoSSL.setConnectTimeout(3000); + requestFactoryNoSSL.setReadTimeout(3000); + NO_SSL_REST_TEMPLATE = new RestTemplate(requestFactoryNoSSL); + List> messageConvertersNoSSL = restTemplate.getMessageConverters(); + for (HttpMessageConverter messageConverter : messageConvertersNoSSL) { + if (messageConverter instanceof StringHttpMessageConverter) { + StringHttpMessageConverter converter = (StringHttpMessageConverter) messageConverter; + converter.setDefaultCharset(StandardCharsets.UTF_8); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + public static RestTemplate getRest(){ return restTemplate; } + + public static RestTemplate getNoSSLRest(){ + return NO_SSL_REST_TEMPLATE; + } }