| | |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.InputStreamReader; |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.math.BigDecimal; |
| | | import java.net.HttpURLConnection; |
| | | import java.net.URL; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.security.MessageDigest; |
| | | import java.security.NoSuchAlgorithmException; |
| | | import java.util.*; |
| | | |
| | | @Component |
| | |
| | | @Value("${pay.key}") |
| | | private String key; |
| | | |
| | | @Value("${pay.channelCode}") |
| | | private String channelCode; |
| | | |
| | | @Value("${pay.base-url}") |
| | | private String baseUrl; |
| | | |
| | | @Value("${pay.call-back-url}") |
| | | private String callBackUrl; |
| | | |
| | | /** |
| | | * 创建支付订单 |
| | | * 创建支付订单 - 适配新支付系统(修正版) |
| | | */ |
| | | public String createOrder(BigDecimal amount,String orderNo,String payProductId ) { |
| | | public String createOrder(BigDecimal amount, String orderNo,String payProductId,String callBackUrl) { |
| | | try { |
| | | Map<String, Object> params = new HashMap<>(); |
| | | params.put("mchId", mchId); |
| | | params.put("productId", payProductId); |
| | | params.put("mchOrderNo",orderNo ); |
| | | params.put("amount", amount.intValue()); |
| | | params.put("notifyUrl", callBackUrl); |
| | | |
| | | String sign = generateSign(params); |
| | | // 必需参数 |
| | | params.put("userCode", mchId); // 商户号 |
| | | params.put("channelCode", payProductId); // 通道码 |
| | | params.put("orderId", orderNo); // 订单号 |
| | | params.put("orderMoney", amount); // 金额(格式化金额) |
| | | params.put("callbackUrl", callBackUrl+"/userPolicy/notify"); // 回调地址 |
| | | |
| | | // 可选参数(根据业务需要添加) |
| | | // params.put("returnUrl", returnUrl); |
| | | // params.put("clientIp", clientIp); |
| | | // params.put("currency", currency); |
| | | // params.put("gameId", gameId); |
| | | // params.put("gameIp", gameIp); |
| | | |
| | | // 生成签名 |
| | | String sign = generateNewSign(params); |
| | | params.put("sign", sign); |
| | | |
| | | String url = baseUrl + "/api/pay/create_order"; |
| | | return sendPost(url, params); |
| | | // 构建GET请求URL |
| | | String url = buildGetUrl(baseUrl, params); |
| | | |
| | | // 发送GET请求,设置正确的Content-Type和可能的token |
| | | return sendGet(url); |
| | | |
| | | } catch (Exception e) { |
| | | return "{\"retCode\":\"FAIL\",\"retMsg\":\"" + e.getMessage() + "\"}"; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 查询订单 |
| | | * 发送GET请求 - 修正版 |
| | | */ |
| | | public String queryOrder(String orderNo) { |
| | | private String sendGet(String url) { |
| | | try { |
| | | Map<String, Object> params = new HashMap<>(); |
| | | params.put("mchId", mchId); |
| | | params.put("mchOrderNo", orderNo); |
| | | HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); |
| | | connection.setRequestMethod("GET"); |
| | | connection.setConnectTimeout(10000); // 10秒连接超时 |
| | | connection.setReadTimeout(10000); // 10秒读取超时 |
| | | |
| | | String sign = generateSign(params); |
| | | params.put("sign", sign); |
| | | // 设置请求头 |
| | | connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); |
| | | connection.setRequestProperty("Accept", "*/*"); |
| | | |
| | | String url = baseUrl + "/api/pay/query_order"; |
| | | return sendPost(url, params); |
| | | int responseCode = connection.getResponseCode(); |
| | | if (responseCode == HttpURLConnection.HTTP_OK) { |
| | | BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); |
| | | String inputLine; |
| | | StringBuilder response = new StringBuilder(); |
| | | |
| | | while ((inputLine = in.readLine()) != null) { |
| | | response.append(inputLine); |
| | | } |
| | | in.close(); |
| | | |
| | | return response.toString(); |
| | | } else { |
| | | return "{\"retCode\":\"FAIL\",\"retMsg\":\"HTTP error code: " + responseCode + "\"}"; |
| | | } |
| | | } catch (Exception e) { |
| | | return "{\"retCode\":\"FAIL\",\"retMsg\":\"" + e.getMessage() + "\"}"; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 新支付系统签名生成 - 最终修正版 |
| | | */ |
| | | private String generateNewSign(Map<String, Object> params) { |
| | | try { |
| | | // 1. 参数名ASCII码从小到大排序(字典序) |
| | | List<String> keys = new ArrayList<>(params.keySet()); |
| | | Collections.sort(keys); |
| | | |
| | | // 2. 如果参数的值为空不参与签名 |
| | | StringBuilder stringA = new StringBuilder(); |
| | | for (String key : keys) { |
| | | Object value = params.get(key); |
| | | // 值为空不参与签名,且sign参数不参与签名 |
| | | if (value != null && !"".equals(value.toString().trim()) && !"sign".equals(key)) { |
| | | if (stringA.length() > 0) { |
| | | stringA.append("&"); |
| | | } |
| | | stringA.append(key).append("=").append(value.toString()); |
| | | } |
| | | } |
| | | |
| | | // 3. 在stringA最后拼接上key(根据示例,应该是直接拼接key值,没有key=前缀) |
| | | // 示例:callbackUrl=http://baidu.com&channelCode=0000&orderId=P12312321123&orderMoney=2.01&userCode=10008&key=8c1511c3413106ac529f6ec9ad095c08 |
| | | // 注意:示例中实际上是有"&key="的,但文档描述是"在stringA最后拼接上key" |
| | | // 按照示例,应该是:stringA + "&key=" + key |
| | | String stringSignTemp = stringA.toString() + "&key=" + key; |
| | | |
| | | // 4. 对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为小写 |
| | | String md5Result = md5(stringSignTemp); |
| | | return md5Result.toLowerCase(); |
| | | |
| | | } catch (Exception e) { |
| | | throw new RuntimeException("生成签名失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 金额格式化(确保金额格式正确) |
| | | */ |
| | | private String formatAmount(BigDecimal amount) { |
| | | // 根据接口描述:金额,单位元,小数点的0可随意设置,比如100.10或100.00或100都可以 |
| | | // 这里我们统一格式化为2位小数 |
| | | return String.format("%.2f", amount); |
| | | } |
| | | |
| | | /** |
| | | * 构建GET请求URL |
| | | */ |
| | | private String buildGetUrl(String baseUrl, Map<String, Object> params) { |
| | | try { |
| | | StringBuilder urlBuilder = new StringBuilder(baseUrl); |
| | | boolean firstParam = true; |
| | | |
| | | for (Map.Entry<String, Object> entry : params.entrySet()) { |
| | | if (entry.getValue() != null && !"".equals(entry.getValue().toString().trim())) { |
| | | if (firstParam) { |
| | | urlBuilder.append("?"); |
| | | firstParam = false; |
| | | } else { |
| | | urlBuilder.append("&"); |
| | | } |
| | | urlBuilder.append(URLEncoder.encode(entry.getKey(), "UTF-8")) |
| | | .append("=") |
| | | .append(URLEncoder.encode(entry.getValue().toString(), "UTF-8")); |
| | | } |
| | | } |
| | | |
| | | return urlBuilder.toString(); |
| | | } catch (UnsupportedEncodingException e) { |
| | | throw new RuntimeException("构建URL失败: " + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * MD5加密工具方法 |
| | | */ |
| | | private String md5(String input) { |
| | | try { |
| | | MessageDigest md = MessageDigest.getInstance("MD5"); |
| | | byte[] messageDigest = md.digest(input.getBytes(StandardCharsets.UTF_8)); |
| | | |
| | | StringBuilder hexString = new StringBuilder(); |
| | | for (byte b : messageDigest) { |
| | | String hex = Integer.toHexString(0xff & b); |
| | | if (hex.length() == 1) { |
| | | hexString.append('0'); |
| | | } |
| | | hexString.append(hex); |
| | | } |
| | | return hexString.toString(); |
| | | } catch (NoSuchAlgorithmException e) { |
| | | throw new RuntimeException("MD5加密失败", e); |
| | | } |
| | | } |
| | | /** |
| | | * 验证回调签名 |
| | | */ |
| | | public boolean verifySign(Map<String, String> params) { |