xiongzhu před 3 roky
rodič
revize
99411f9155

+ 14 - 0
src/main/java/com/izouma/nineth/config/HmPayProperties.java

@@ -0,0 +1,14 @@
+package com.izouma.nineth.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@Data
+@ConfigurationProperties(prefix = "hmpay")
+public class HmPayProperties {
+    private String merNo;
+    private String appPrivateKey;
+    private String appPublicKey;
+    private String hmPublicKey;
+    private String notifyUrl;
+}

+ 107 - 0
src/main/java/com/izouma/nineth/service/HMPayService.java

@@ -0,0 +1,107 @@
+package com.izouma.nineth.service;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.internal.util.AlipaySignature;
+import com.github.kevinsawicki.http.HttpRequest;
+import com.izouma.nineth.config.GeneralProperties;
+import com.izouma.nineth.config.HmPayProperties;
+import com.izouma.nineth.utils.DateTimeUtils;
+import com.izouma.nineth.utils.SnowflakeIdWorker;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+@Slf4j
+@AllArgsConstructor
+@EnableConfigurationProperties({HmPayProperties.class})
+public class HMPayService {
+    private final HmPayProperties   hmPayProperties;
+    private final GeneralProperties generalProperties;
+
+    private String getSign(Map<String, String> params) {
+        try {
+            Map<String, String> m = new HashMap<>(params);
+            m.remove("expire_time");
+            m.remove("goods_name");
+            m.remove("product_code");
+            m.remove("clear_cycle");
+            m.remove("jump_scheme");
+            m.remove("meta_option");
+            return AlipaySignature.rsaSign(m, hmPayProperties.getAppPrivateKey(), "UTF-8");
+        } catch (AlipayApiException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private void encodeParams(Map<String, String> params) {
+        List<String> arr = Arrays.asList("notify_url", "return_url", "goods_name", "meta_option", "sign", "pay_extra");
+        params.forEach((k, v) -> {
+            if (arr.contains(k)) {
+                params.put(k, URLEncoder.encode(v, StandardCharsets.UTF_8));
+            }
+        });
+    }
+
+    public String getReqTime() {
+        return DateTimeUtils.format(LocalDateTime.now(), "yyyyMMddHHmmss");
+    }
+
+    public String getTimeout(int seconds) {
+        return DateTimeUtils.format(LocalDateTime.now().plusSeconds(seconds), "yyyyMMddHHmmss");
+    }
+
+    public String requestAlipay(String orderId, BigDecimal amount, String subject, String desc,
+                                int timeout, String extend, String returnUrl) {
+        if (orderId.length() < 12) {
+            for (int i = orderId.length(); i < 12; i++) {
+                orderId = "0" + orderId;
+            }
+        }
+        Map<String, String> params = new HashMap<>();
+        params.put("version", "10");
+        params.put("mer_no", "664403000025502");
+        params.put("mer_order_no", orderId);
+        params.put("create_time", getReqTime());
+        params.put("expire_time", getTimeout(timeout));
+        params.put("order_amt", amount.setScale(2, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString());
+        params.put("notify_url", hmPayProperties.getNotifyUrl());
+        params.put("return_url", returnUrl);
+        params.put("create_ip", "58_213_205_43");
+        params.put("goods_name", subject);
+        params.put("store_id", "100001");
+        params.put("product_code", "01020002");
+        params.put("clear_cycle", "0"); //不参与签名
+        params.put("meta_option", "[{\"s\":\"Android\",\"n\":\"绿洲宇宙\",\"id\":\"vip.raex.nft\",\"sc\":\"wzsc://\"},{\"s\":\"IOS\",\"n\":\"\",\"id\":\"\",\"sc\":\"\"}]"); //不参与签名
+        params.put("jump_scheme", "hemacash://hmpay");
+        params.put("sign_type", "RSA");
+        params.put("accsplit_info", "N");
+        log.info("支付请求参数: {}", JSON.toJSONString(params, true));
+        params.put("sign", getSign(params));
+        log.info("支付请求签名: {}", params.get("sign"));
+        encodeParams(params);
+
+        String body = HttpRequest.post("https://cash.sandgate.cn/gateway/api")
+                .contentType("application/json")
+                .send(JSON.toJSONString(params)).body();
+        JSONObject res = JSON.parseObject(body);
+        log.info("支付请求结果: {}", JSON.toJSONString(res, true));
+
+
+        return null;
+    }
+}

+ 1 - 1
src/main/java/com/izouma/nineth/service/SandPayService.java

@@ -142,7 +142,7 @@ public class SandPayService {
         //body.put("limitPay", "5");                                  //限定支付方式 送1-限定不能使用贷记卡	送4-限定不能使用花呗	送5-限定不能使用贷记卡+花呗
         body.put("subject", subject);                                 //订单标题
         body.put("body", desc);                                       //订单描述
-        body.put("txnTimeOut", getTimeout(180));              //订单超时时间
+        body.put("txnTimeOut", getTimeout(timeout));              //订单超时时间
         body.put("notifyUrl", sandPayProperties.getNotifyUrl());      //异步通知地址
         body.put("bizExtendParams", "");                              //业务扩展参数
         body.put("merchExtendParams", "");                            //商户扩展参数

+ 78 - 78
src/main/java/com/izouma/nineth/service/UserBalanceService.java

@@ -284,102 +284,102 @@ public class UserBalanceService {
     }
 
     @Async
-    public void autoWithdraw(LocalDate date) {
+    public void autoWithdraw(LocalDate date) throws ExecutionException, InterruptedException {
+        ForkJoinPool customThreadPool = new ForkJoinPool(2);
+        customThreadPool.submit(() -> {
+            autoWithdrawRecordRepo.findByDate(date).ifPresent(a -> {
+                throw new BusinessException("今日已经提现过");
+            });
 
-        autoWithdrawRecordRepo.findByDate(date).ifPresent(a -> {
-            throw new BusinessException("今日已经提现过");
-        });
+            List<UserBalance> list = userBalanceRepo.findByLockedFalseAndBalanceGreaterThanOrderByUserId(BigDecimal.ZERO);
+
+            AutoWithdrawRecord record = AutoWithdrawRecord.builder()
+                    .date(LocalDate.now())
+                    .status("pending")
+                    .progress(0)
+                    .total(list.size())
+                    .build();
+            autoWithdrawRecordRepo.saveAndFlush(record);
+
+            list.parallelStream().forEach(userBalance -> {
+                UserBankCard userBankCard = userBankCardRepo.findByUserId(userBalance.getUserId())
+                        .stream().findFirst().orElse(null);
+                if (userBankCard == null) {
+                    log.info("自动提现userId={}, amount={}, 未绑卡", userBalance.getBalance(), userBalance.getUserId());
+                    record.setProgress(record.getProgress() + 1);
+                    record.setCurrentUserId(userBalance.getUserId());
+                    autoWithdrawRecordRepo.saveAndFlush(record);
+                } else {
+                    log.info("自动提现userId={}, amount={}, name={}, bank={}",
+                            userBalance.getUserId(), userBalance.getBalance(),
+                            userBankCard.getRealName(), userBankCard.getBankNo());
 
-        List<UserBalance> list = userBalanceRepo.findByLockedFalseAndBalanceGreaterThanOrderByUserId(BigDecimal.ZERO);
-
-        AutoWithdrawRecord record = AutoWithdrawRecord.builder()
-                .date(LocalDate.now())
-                .status("pending")
-                .progress(0)
-                .total(list.size())
-                .build();
-        autoWithdrawRecordRepo.saveAndFlush(record);
-
-        for (UserBalance userBalance : list) {
-            UserBankCard userBankCard = userBankCardRepo.findByUserId(userBalance.getUserId())
-                    .stream().findFirst().orElse(null);
-            if (userBankCard == null) {
-                log.info("自动提现userId={}, amount={}, 未绑卡", userBalance.getBalance(), userBalance.getUserId());
-                record.setProgress(record.getProgress() + 1);
-                record.setCurrentUserId(userBalance.getUserId());
-                autoWithdrawRecordRepo.saveAndFlush(record);
-            } else {
-                log.info("自动提现userId={}, amount={}, name={}, bank={}",
-                        userBalance.getUserId(), userBalance.getBalance(),
-                        userBankCard.getRealName(), userBankCard.getBankNo());
-
-                String withdrawId = snowflakeIdWorker.nextId() + "";
-
-                BigDecimal amount = userBalance.getBalance();
-                userBalance.setLastBalance(userBalance.getBalance());
-                userBalance.setBalance(BigDecimal.ZERO);
-                userBalanceRepo.saveAndFlush(userBalance);
-
-                balanceRecordRepo.save(BalanceRecord.builder()
-                        .time(LocalDateTime.now())
-                        .userId(userBalance.getUserId())
-                        .amount(amount.negate())
-                        .balance(BigDecimal.ZERO)
-                        .lastBalance(userBalance.getLastBalance())
-                        .type(BalanceType.WITHDRAW)
-                        .withdrawId(withdrawId)
-                        .build());
-
-                boolean success = false;
-                String msg = null;
-                try {
-                    JSONObject res = sandPayService.transfer(withdrawId, userBankCard.getRealName(), userBankCard.getBankNo(), amount);
-                    if ("0000".equals(res.getString("respCode"))) {
-                        success = true;
-                    } else {
-                        msg = res.getString("respDesc");
-                    }
-                } catch (Exception e) {
-                    msg = e.getMessage();
-                }
+                    String withdrawId = snowflakeIdWorker.nextId() + "";
 
-                if (!success) {
+                    BigDecimal amount = userBalance.getBalance();
                     userBalance.setLastBalance(userBalance.getBalance());
-                    userBalance.setBalance(userBalance.getBalance().add(amount));
+                    userBalance.setBalance(BigDecimal.ZERO);
                     userBalanceRepo.saveAndFlush(userBalance);
 
                     balanceRecordRepo.save(BalanceRecord.builder()
                             .time(LocalDateTime.now())
                             .userId(userBalance.getUserId())
-                            .amount(amount)
-                            .balance(userBalance.getBalance())
+                            .amount(amount.negate())
+                            .balance(BigDecimal.ZERO)
                             .lastBalance(userBalance.getLastBalance())
-                            .type(BalanceType.RETURN)
+                            .type(BalanceType.WITHDRAW)
                             .withdrawId(withdrawId)
-                            .remark(msg)
                             .build());
-                }
 
-                record.setProgress(record.getProgress() + 1);
-                record.setCurrentUserId(userBalance.getUserId());
-                autoWithdrawRecordRepo.saveAndFlush(record);
+                    boolean success = false;
+                    String msg = null;
+                    try {
+                        JSONObject res = sandPayService.transfer(withdrawId, userBankCard.getRealName(), userBankCard.getBankNo(), amount);
+                        if ("0000".equals(res.getString("respCode"))) {
+                            success = true;
+                        } else {
+                            msg = res.getString("respDesc");
+                        }
+                    } catch (Exception e) {
+                        msg = e.getMessage();
+                    }
 
+                    if (!success) {
+                        userBalance.setLastBalance(userBalance.getBalance());
+                        userBalance.setBalance(userBalance.getBalance().add(amount));
+                        userBalanceRepo.saveAndFlush(userBalance);
+
+                        balanceRecordRepo.save(BalanceRecord.builder()
+                                .time(LocalDateTime.now())
+                                .userId(userBalance.getUserId())
+                                .amount(amount)
+                                .balance(userBalance.getBalance())
+                                .lastBalance(userBalance.getLastBalance())
+                                .type(BalanceType.RETURN)
+                                .withdrawId(withdrawId)
+                                .remark(msg)
+                                .build());
+                    }
+
+                    record.setProgress(record.getProgress() + 1);
+                    record.setCurrentUserId(userBalance.getUserId());
+                    autoWithdrawRecordRepo.saveAndFlush(record);
 
-                if (!success) {
-                    msg = msg == null ? "" : msg;
-                    if (msg.contains("风控") || Pattern.matches(".*\\[.*\\]", msg)) {
-                        userBalance.setLocked(true);
-                        userBalance.setLockReason(msg);
-                        userBalance.setLockTime(LocalDateTime.now());
+
+                    if (!success) {
+                        msg = msg == null ? "" : msg;
+                        if (msg.contains("风控") || Pattern.matches(".*\\[.*\\]", msg)) {
+                            userBalance.setLocked(true);
+                            userBalance.setLockReason(msg);
+                            userBalance.setLockTime(LocalDateTime.now());
+                        }
                     }
                 }
-            }
-        }
-
-        record.setStatus("finish");
-        autoWithdrawRecordRepo.saveAndFlush(record);
+            });
 
-        redisTemplate.delete("autoWithdraw::" + DateTimeUtils.format(date, "yyyyMMdd"));
+            record.setStatus("finish");
+            autoWithdrawRecordRepo.saveAndFlush(record);
+        }).get();
     }
 
     public void revert() throws ExecutionException, InterruptedException {

+ 2 - 1
src/main/java/com/izouma/nineth/web/UserBalanceController.java

@@ -30,6 +30,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.time.Duration;
 import java.time.LocalDate;
+import java.util.concurrent.ExecutionException;
 
 @RestController
 @RequestMapping("/userBalance")
@@ -76,7 +77,7 @@ public class UserBalanceController extends BaseController {
     }
 
     @PostMapping("/autoWithdraw")
-    public void autoWithdraw() {
+    public void autoWithdraw() throws ExecutionException, InterruptedException {
 
         LocalDate date = LocalDate.now();
         autoWithdrawRecordRepo.findByDate(date).ifPresent(a -> {

+ 6 - 0
src/main/resources/application.yaml

@@ -233,6 +233,12 @@ sandpay:
   sign-cert-pwd: 3edc#EDC
   sand-cert-path: classpath:cert/sand.cer
   notify-url: http://xiongzhu.frp.izouma.com/sandpay/notify
+hmpay:
+  mer-no: 664403000025502
+  app-private-key: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJCaYSAw8c2uk4gry+/uy5thmzgdg1tPr6TRRj25AKxa13hnhjMEzp/eD8++LHxyPXg0ZXlbotZ/g0ES1XHJMkmHA4fRZ6Ki35LxlY60Z8xRwaTZXIctlaCkjhJmURNncQzyB5XMFF2M6cgy1T0yr+czoVPelVEFbAWu4cg/kQ1tAgMBAAECgYAGQwktsbDm7UZqQStFqpuakPF9zplfnOXIR1+5UIec7xohlqoTD4Q7HAynPF8EzJWo3OwAA13b2A3BBaXElafdwod7J2H9zlGJiqfmsnSTMhapVYsBbqnXGYWFS33wAm1Wx4Y9hSQxUD6AsZ+A05RiolrTTfAAGMS9E4sGhh1O6QJBANGUbGUednN4/0A0KlH3pWgLKPT+JyMeZLFGXeFLJpwWf25Xj98JcGai5t9sDqPl6xSgkEnJHdLB7LNEnmqr5X8CQQCwoaZwsVd0jnW6RU6F3SZ+BY4YS7SAhMlElqaJbBTP7DbXH/Z3cS1V696bvKup6+HlI/l0YwTDdRJIR0hl9XsTAkEAwFpPWkepQ7ZL36uBJBX9FA8aGjGhzhO5KxOAWqTU3PGxJ57qBzTsmubsqybMERWWuynbBD24R5WBH8/c7d6zFQJAShIP9TQ5Y5SWRB3qVvKtwK1dsEyXDkohCRVQE1Lyy1rYiJBL0Dzy8RQvzwlox6I2odvbMXaQudKvbwYNk0xFZwJAAxAeFweoXqDWcYl7Oa4nmgbM9LVqAMCbjzkChquXvj5q5sE9LOXiSMfbnDalNlLonbApnuTx5YCJYB0jA0ml8g==
+  app-public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQmmEgMPHNrpOIK8vv7subYZs4HYNbT6+k0UY9uQCsWtd4Z4YzBM6f3g/Pvix8cj14NGV5W6LWf4NBEtVxyTJJhwOH0Weiot+S8ZWOtGfMUcGk2VyHLZWgpI4SZlETZ3EM8geVzBRdjOnIMtU9Mq/nM6FT3pVRBWwFruHIP5ENbQIDAQAB
+  public-key: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLobxNslmsua5fnegILQSMioA5N0E5Kfg1hYLzhOuPdyJbFIImFkRF8RYt6Fp24LpDfpEEBhX8EGYIJSPpRWPZYVBzlGWOj1jS0FeF/JK+8a8ihgSxAU2JwsUySqGv+8aXIofJUus/j/KEPQZgjyVebi9J/ww75pbpGrymnAZUdwIDAQAB
+  notify-url: http://xiongzhu.frp.izouma.com/hmpay/notify
 ---
 
 spring:

+ 1 - 0
src/main/resources/cert/hmpay_app_private_key.txt

@@ -0,0 +1 @@
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJCaYSAw8c2uk4gry+/uy5thmzgdg1tPr6TRRj25AKxa13hnhjMEzp/eD8++LHxyPXg0ZXlbotZ/g0ES1XHJMkmHA4fRZ6Ki35LxlY60Z8xRwaTZXIctlaCkjhJmURNncQzyB5XMFF2M6cgy1T0yr+czoVPelVEFbAWu4cg/kQ1tAgMBAAECgYAGQwktsbDm7UZqQStFqpuakPF9zplfnOXIR1+5UIec7xohlqoTD4Q7HAynPF8EzJWo3OwAA13b2A3BBaXElafdwod7J2H9zlGJiqfmsnSTMhapVYsBbqnXGYWFS33wAm1Wx4Y9hSQxUD6AsZ+A05RiolrTTfAAGMS9E4sGhh1O6QJBANGUbGUednN4/0A0KlH3pWgLKPT+JyMeZLFGXeFLJpwWf25Xj98JcGai5t9sDqPl6xSgkEnJHdLB7LNEnmqr5X8CQQCwoaZwsVd0jnW6RU6F3SZ+BY4YS7SAhMlElqaJbBTP7DbXH/Z3cS1V696bvKup6+HlI/l0YwTDdRJIR0hl9XsTAkEAwFpPWkepQ7ZL36uBJBX9FA8aGjGhzhO5KxOAWqTU3PGxJ57qBzTsmubsqybMERWWuynbBD24R5WBH8/c7d6zFQJAShIP9TQ5Y5SWRB3qVvKtwK1dsEyXDkohCRVQE1Lyy1rYiJBL0Dzy8RQvzwlox6I2odvbMXaQudKvbwYNk0xFZwJAAxAeFweoXqDWcYl7Oa4nmgbM9LVqAMCbjzkChquXvj5q5sE9LOXiSMfbnDalNlLonbApnuTx5YCJYB0jA0ml8g==

+ 1 - 0
src/main/resources/cert/hmpay_app_public_key.txt

@@ -0,0 +1 @@
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQmmEgMPHNrpOIK8vv7subYZs4HYNbT6+k0UY9uQCsWtd4Z4YzBM6f3g/Pvix8cj14NGV5W6LWf4NBEtVxyTJJhwOH0Weiot+S8ZWOtGfMUcGk2VyHLZWgpI4SZlETZ3EM8geVzBRdjOnIMtU9Mq/nM6FT3pVRBWwFruHIP5ENbQIDAQAB

+ 1 - 0
src/main/resources/cert/hmpay_public_key.txt

@@ -0,0 +1 @@
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLobxNslmsua5fnegILQSMioA5N0E5Kfg1hYLzhOuPdyJbFIImFkRF8RYt6Fp24LpDfpEEBhX8EGYIJSPpRWPZYVBzlGWOj1jS0FeF/JK+8a8ihgSxAU2JwsUySqGv+8aXIofJUus/j/KEPQZgjyVebi9J/ww75pbpGrymnAZUdwIDAQAB

+ 3 - 55
src/test/java/com/izouma/nineth/CommonTest.java

@@ -2,6 +2,7 @@ package com.izouma.nineth;
 
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.internal.util.AlipaySignature;
@@ -640,64 +641,11 @@ public class CommonTest {
 
     @Test
     public void match() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
-        byte[] byteKey = java.util.Base64.getDecoder().decode(Files.readAllBytes(Path.of("/Users/drew/Downloads/应用公钥1024.txt")));
+        byte[] byteKey = java.util.Base64.getDecoder().decode(Files.readAllBytes(Path.of("/Users/drew/Downloads/hmpay_app_public_key.txt")));
         PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(byteKey));
         System.out.println(publicKey);
-        byte[] bytePriv = java.util.Base64.getDecoder().decode(Files.readAllBytes(Path.of("/Users/drew/Downloads/应用私钥1024.txt")));
+        byte[] bytePriv = java.util.Base64.getDecoder().decode(Files.readAllBytes(Path.of("/Users/drew/Downloads/hmpay_app_private_key.txt")));
         PrivateKey privKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(bytePriv));
         System.out.println(privKey);
     }
-
-    @Test
-    public void alih5() throws IOException, AlipayApiException {
-        Map<String, String> params = new HashMap<>();
-        String mer_no = "664403000025502";
-        String mer_order_no = new SnowflakeIdWorker(1, 1).nextId() + "";
-        String create_time = DateTimeUtils.format(LocalDateTime.now(), "yyyyMMddHHmmss");
-        String expire_time = DateTimeUtils.format(LocalDateTime.now().plusMinutes(3), "yyyyMMddHHmmss");
-        String create_ip = "58_213_205_43";
-        String goods_name = "话费充值";
-        String store_id = "100001";
-        String product_code = "01020002";
-        String clear_cycle = "0"; //不参与签名
-        String meta_option = "[{\"s\":\"Android\",\"n\":\"绿洲宇宙\",\"id\":\"vip.raex.nft\",\"sc\":\"wzsc://\"},{\"s\":\"IOS\",\"n\":\"\",\"id\":\"\",\"sc\":\"\"}]"; //不参与签名
-        String jump_scheme = "sandcash://scpay";
-        String sign_type = "RSA";
-        String order_amt = "0.01";
-        String notify_url = "https://ordertest.raex.vip/sand";
-        String return_url = "https://test.raex.vip/9th/home";
-
-        params.put("mer_no", mer_no);
-        params.put("mer_order_no", mer_order_no);
-        params.put("create_time", create_time);
-        params.put("create_ip", create_ip);
-        params.put("store_id", store_id);
-        params.put("sign_type", sign_type);
-        params.put("order_amt", order_amt);
-        params.put("notify_url", notify_url);
-        params.put("return_url", return_url);
-
-        String sign = AlipaySignature.rsaSign(params, Files.readString(Path.of("/Users/drew/Downloads/应用私钥1024.txt")), "UTF-8");
-        System.out.println(sign);
-
-        String url = "https://cash.sandgate.cn/h5/?version=10&mer_no=" + mer_no +
-                "&mer_order_no=" + mer_order_no +
-                "&create_time=" + create_time +
-                "&expire_time=" + expire_time +
-                "&order_amt=" + order_amt +
-                "&notify_url=" + URLEncoder.encode(notify_url, StandardCharsets.UTF_8) +
-                "&return_url=" + URLEncoder.encode(return_url, StandardCharsets.UTF_8) +
-                "&create_ip=" + create_ip +
-                "&goods_name=" + URLEncoder.encode(goods_name, StandardCharsets.UTF_8) +
-                "&store_id=" + store_id +
-                "&product_code=" + product_code +
-                "&clear_cycle=" + clear_cycle +
-                "&pay_extra=" +
-                "&meta_option=" + URLEncoder.encode(meta_option, StandardCharsets.UTF_8) +
-                "&jump_scheme=" + jump_scheme +
-                "&sign_type=RSA&sign=" + URLEncoder.encode(sign, StandardCharsets.UTF_8) +
-                "#/hippoh5";
-        System.out.println(url);
-
-    }
 }

+ 120 - 0
src/test/java/com/izouma/nineth/HMPayTest.java

@@ -0,0 +1,120 @@
+package com.izouma.nineth;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.internal.util.AlipaySignature;
+import com.github.kevinsawicki.http.HttpRequest;
+import com.izouma.nineth.utils.DateTimeUtils;
+import com.izouma.nineth.utils.SnowflakeIdWorker;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class HMPayTest {
+    private String appPrivateKey;
+    private String appPublicKey;
+    private String hmPublicKey;
+
+    public HMPayTest() {
+        try {
+            this.appPrivateKey = new String(getClass().getClassLoader().getResourceAsStream("cert/hmpay_app_private_key.txt").readAllBytes()).trim();
+            this.appPublicKey = new String(getClass().getClassLoader().getResourceAsStream("cert/hmpay_app_public_key.txt").readAllBytes()).trim();
+            this.hmPublicKey = new String(getClass().getClassLoader().getResourceAsStream("cert/hmpay_public_key.txt").readAllBytes()).trim();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private String getSign(Map<String, String> params) {
+        try {
+            Map<String, String> m = new HashMap<>(params);
+            m.remove("expire_time");
+            m.remove("goods_name");
+            m.remove("product_code");
+            m.remove("clear_cycle");
+            m.remove("jump_scheme");
+            m.remove("meta_option");
+            return AlipaySignature.rsaSign(m, appPrivateKey, "UTF-8");
+        } catch (AlipayApiException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private void encodeParams(Map<String, String> params) {
+        List<String> arr = Arrays.asList("notify_url", "return_url", "goods_name", "meta_option", "sign", "pay_extra");
+        params.forEach((k, v) -> {
+            if (arr.contains(k)) {
+                params.put(k, URLEncoder.encode(v, StandardCharsets.UTF_8));
+            }
+        });
+    }
+
+    @Test
+    public void alih5() throws IOException, AlipayApiException {
+        Map<String, String> params = new HashMap<>();
+        params.put("version", "10");
+        params.put("mer_no", "664403000025502");
+        params.put("mer_order_no", new SnowflakeIdWorker(1, 1).nextId() + "");
+        params.put("create_time", DateTimeUtils.format(LocalDateTime.now(), "yyyyMMddHHmmss"));
+        params.put("expire_time", DateTimeUtils.format(LocalDateTime.now().plusMinutes(3), "yyyyMMddHHmmss"));
+        params.put("order_amt", "0.01");
+        params.put("notify_url", "https://ordertest.raex.vip/sand/notify");
+        params.put("return_url", "https://test.raex.vip/9th/home");
+        params.put("create_ip", "58_213_205_43");
+        params.put("goods_name", "话费充值");
+        params.put("store_id", "100001");
+        params.put("product_code", "01020002");
+        params.put("clear_cycle", "0"); //不参与签名
+        params.put("meta_option", "[{\"s\":\"Android\",\"n\":\"绿洲宇宙\",\"id\":\"vip.raex.nft\",\"sc\":\"wzsc://\"},{\"s\":\"IOS\",\"n\":\"\",\"id\":\"\",\"sc\":\"\"}]"); //不参与签名
+        params.put("jump_scheme", "hemacash://hmpay");
+        params.put("sign_type", "RSA");
+        params.put("accsplit_info", "N");
+        params.put("sign", getSign(params));
+        encodeParams(params);
+
+//        String url = "https://cash.sandgate.cn/h5/?" +
+//                params.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
+//                        .collect(Collectors.joining("&")) + "#/hippoh5";
+//        System.out.println(url);
+        String url = "https://cash.sandgate.cn/h5/" +
+                "?version=" + params.get("version") +
+                "&mer_no=" + params.get("mer_no") +
+                "&mer_order_no=" + params.get("mer_order_no") +
+                "&create_time=" + params.get("create_time") +
+                "&expire_time=" + params.get("expire_time") +
+                "&order_amt=" + params.get("order_amt") +
+                "&notify_url=" + params.get("notify_url") +
+                "&return_url=" + params.get("return_url") +
+                "&create_ip=" + params.get("create_ip") +
+                "&goods_name=" + params.get("goods_name") +
+                "&store_id=" + params.get("store_id") +
+                "&product_code=" + params.get("product_code") +
+                "&clear_cycle=" + params.get("clear_cycle") +
+                "&meta_option=" + params.get("meta_option") +
+                "&jump_scheme=" + params.get("jump_scheme") +
+                "&accsplit_info=" + params.get("accsplit_info") +
+                "&sign_type=" + params.get("sign_type") +
+                "&sign=" + params.get("sign") +
+                "#/hippoh5";
+        System.out.println(url);
+
+        String body = HttpRequest.post("https://cash.sandgate.cn/gateway/api")
+                .contentType("application/json")
+                .send(JSON.toJSONString(params)).body();
+        System.out.println(JSON.toJSONString(JSON.parseObject(body), true));
+    }
+
+}