puzhibing
2023-11-06 c36b79ea63fe43aa77ab74508505b727e4976a01
ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/HuiFuTianXiaUtil.java
@@ -7,18 +7,21 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.utils.uuid.IdUtils;
import com.ruoyi.order.domain.dto.WechatPaymentRefundDto;
import com.ruoyi.order.domain.dto.WeixinPaymentNotifyDto;
import com.ruoyi.order.domain.vo.PaymentDelaytransHFTXVo;
import com.ruoyi.order.domain.vo.WeixinPaymentNotifyVo;
import com.ruoyi.order.domain.vo.WeixinPaymentRefundVo;
import org.springframework.util.StringUtils;
import java.math.RoundingMode;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
@@ -43,6 +46,16 @@
    //控制台地址https://dashboard.huifu.com/customers/login,用户名为hh-hrt,最新密码为xw123456
    //接口文档https://paas.huifu.com/partners/api/#/
    //私钥(Base64编码)
    private static String channel_privateKeyBase64 = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCB7Flp+4FuRBvW4FLx5FoR/Hbj1UWJbgt9JAgO5iCFR9MhxXu783hvP88STFWyNkCeAh9EIwVcJxm80Kmt87KH4Nwo1ejfqF8kUkK02byVby39fqTJlyqeDYKjqqc04GpnL7vi4iD4rKGvjyJIFxekCcOSTFWVl879fbPn9+ljsVrrjoyzbPMvPJHPuc2dzkWP98C+y5QFDIdjyS8NrVOpJcwVFrcxkmCmK6azTi6LJeI/wH9MsJHmnF9ZilqY1x00XbTMxE4EfTgXgmv6dvRac6VrqQowUCqIrHpVJIdet1w56vmz3IpxORIOPzU2nY9ziZrMIgGkJ1bLQRbLK2RXAgMBAAECggEAKA4i/u7AV6t93cBpMhRRxIvOZ61/87/OoPUz2swOKKkdKaNF44tLjRjiEWUhYoEhaNWEqDAX3fJcF/9I4M1qNEQZ/Cj+072IH52Q3UrnvjZ5ulaKHXCSfgnyRd0+EapCg6+OwIMw4Rnv+z0ot4sPCc7M0dTg6e7UrQhNJ+4hsU8yZAMzmQyUu7pE6tvbE+u5QdRZRMQIYPgzwpy5Yta0gtRSmMQ9jneUYwEJ5eu8ZhAXRzEWqAA9GgOLgAwC2Ma1xkVyiBS/7pfd0fwbxapzMZMQMbCsv+UrYGYkXDp/1XwrpNWavfHpSCc9fkK2auEqYJtYlhELoa87+WrBOHhkkQKBgQDMi73gDw47XH0T6zuJn2ieDDc7IU1DV9RXXNJNxgcB0yBM8HaI8lv27eThphlEU13KWKTHAylm/q7v4dp88aC+iA3GhIbmE1sWxiSJvwGR2xqmWvIHTadJtQFdXJN+/GmG1fJuCRwkZSyGdUXUdmx3PBATEqUwuSQQDQkMvxYjrwKBgQCimxdXNEZrZKHCbHWTHx0nUu0lF7skgi8x+xLbDT096WvqpeNxOZXVZVhU7yHn2ePF9iFipg8uHviUIJyB2Rzr1M2busY3yoRutXlugZJ9ZVUlxpIpNI94ntcOZUctgfd1quhlgqEwcF1PJ5lkogrKHvLoV30fvgkYpA763ZNr2QKBgDgDkKS6GsCgzFPXGD/Q2pplZ/6WWQ03ERw3fBP2Rdb3FSJcu2k3TX3qmcI9dS/j0IxoB//D2uOsnQVuCSIsHUPwCC2z/ykkZc5vxnO+TdS+dqbVwu/DESu7GLuTbYTZ5KAbGL+PWRiSaEYN+Wh8YampVhGTCo+0M679ktvdvFdbAoGACtCz+bnhYS09URgzrchpAP+UBF6StPfvuaxQcb9srlyDMAU+nn5NB/eIq4WIRMqKStV3HP+cjN3EfN26ayyMrublkhEXNAjFTDPH0uDNZIfD5RJTEGiNjmFKPovf8xfs5Kesc2v/2p/upOAmZLoea5kpYOjIyr6yDjCDSo5ISukCgYBtOo2AgFXyzW1ti8MCsz77BtCJ9MgVoPF4y4Ah8l/6Dwe+cevdlWuOYckIAJqJ9g2FYwp9AAevK7k17xsgBgrdqlI77p3wQb7p/znJnNU6qz78FwUzd8t5WvqFh+3mTxjG7Iyj6brLzEUt8McdmGlwbFJqKgUxBKS4mY7T399L5Q==";
    //公钥(Base64编码)
    private static String channel_publicKeyBase64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgexZafuBbkQb1uBS8eRaEfx249VFiW4LfSQIDuYghUfTIcV7u/N4bz/PEkxVsjZAngIfRCMFXCcZvNCprfOyh+DcKNXo36hfJFJCtNm8lW8t/X6kyZcqng2Co6qnNOBqZy+74uIg+Kyhr48iSBcXpAnDkkxVlZfO/X2z5/fpY7Fa646Ms2zzLzyRz7nNnc5Fj/fAvsuUBQyHY8kvDa1TqSXMFRa3MZJgpiums04uiyXiP8B/TLCR5pxfWYpamNcdNF20zMROBH04F4Jr+nb0WnOla6kKMFAqiKx6VSSHXrdcOer5s9yKcTkSDj81Np2Pc4mazCIBpCdWy0EWyytkVwIDAQAB";
    //汇付公钥
    private static String channel_publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgmGZVY0i5r17BnHiLeL7TbqojpuOmR947JIUB5zYcc/TTYijkCsjtshmvxWslZBdkV6K8/1CmHRAY92+4GZnem6MVO34xn1wTCq65+wMC1oypjJy9T6N0WXoGaf+Wn0nxPdJLvnwpOo6epEES3kQKx+i/Qar7oDwlXMQ6b2QwR/id9cYudN0RmdRLzkmrwEOkjv+WcWCQdoAXv4JWJwU+iwJffoJrWn5pGeVFWFrH1fRJ0IB4YdaL9aWMn2YM5pu+/mcOKQogmNYzsHkZDapX3AF+es0TN+v/qOCgU3OVTVa7ltDAiLuPtMeu/72pQiM0nxunRVnAJDo2BQ5mu6z4wIDAQAB";
    //渠道号
    private static String channel_id = "6666000140729384";
    //控制台地址https://dashboard.huifu.com/partners/login,用户名为hongrt,最新密码为xw123456
    /**
@@ -55,9 +68,6 @@
     */
    public static R<JSONObject> weixinPayment(String req_seq_id, String goods_desc, Double trans_amt, String sub_openid, String notify_url){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        NumberFormat numberInstance = NumberFormat.getInstance();
        //最大两位小数
        numberInstance.setMaximumFractionDigits(2);
        String url = "https://api.huifu.com/v2/trade/payment/jspay";
        JSONObject data = new JSONObject();
        data.put("req_date", sdf.format(new Date()));
@@ -65,7 +75,7 @@
        data.put("huifu_id", huifu_id);
        data.put("goods_desc", goods_desc);
        data.put("trade_type", "T_MINIAPP");
        data.put("trans_amt", numberInstance.format(trans_amt));
        data.put("trans_amt", String.format("%.2f", trans_amt));
        JSONObject wx_data = new JSONObject();
        wx_data.put("sub_appid", sub_appid);
@@ -84,18 +94,13 @@
        headers.put("Content-type","application/json; charset=utf-8");
        headers.put("Accept", "application/json");
        post.addHeaders(headers);
        System.err.println(JSON.toJSONString(body));
        post.body(body.toJSONString());
        HttpResponse execute = post.execute();
        String result1 = execute.body();
        execute.close();
        if(StringUtils.hasLength(result1)){
            JSONObject result = JSONObject.parseObject(result1);
            String result_data = result.getString("data");
            String result_sign = result.getString("sign");
            boolean verify = verify(result_data, result_sign);
            if(!verify){
                return R.fail("结果验签失败");
            }
            JSONObject resultData = result.getJSONObject("data");
            String resp_code = resultData.getString("resp_code");
            String resp_desc = resultData.getString("resp_desc");
@@ -114,18 +119,17 @@
    /**
     * 微信支付回调数据处理
     * @param jsonObject
     * @return
     */
    public static R<WeixinPaymentNotifyVo> weixinPaymentNotify(JSONObject jsonObject){
        String sign = jsonObject.getString("sign");
        JSONObject resp_data = jsonObject.getJSONObject("resp_data");
        String resp_code = jsonObject.getString("resp_code");
        String resp_desc = jsonObject.getString("resp_desc");
    public static R<WeixinPaymentNotifyVo> weixinPaymentNotify(WeixinPaymentNotifyDto dto){
        String sign = dto.getSign();
        JSONObject resp_data = JSON.parseObject(dto.getResp_data());
        String resp_code = dto.getResp_code();
        String resp_desc = dto.getResp_desc();
        if("00000000".equals(resp_code)){
            String code = resp_data.getString("resp_code");
            String desc = resp_data.getString("resp_desc");
            if("00000000".equals(code)){
            if(!"00000000".equals(code)){
                return R.fail(desc);
            }
            WeixinPaymentNotifyVo vo = new WeixinPaymentNotifyVo();
@@ -135,6 +139,8 @@
            vo.setPayAmt(resp_data.getString("pay_amt"));
            vo.setEndTime(resp_data.getString("end_time"));
            vo.setTransStat(resp_data.getString("trans_stat"));
            vo.setFeeFlag(resp_data.getInteger("fee_flag"));
            vo.setFeeAmount(resp_data.getString("fee_amount"));
            return R.ok(vo);
        }
        return R.fail(resp_desc);
@@ -148,23 +154,20 @@
     * @param req_seq_id            请求流水号
     * @param ord_amt               退款金额
     * @param org_req_date          原交易请求日期yyyyMMdd
     * @param org_party_order_id    原交易微信支付宝的商户单号
     * @param org_req_seq_id        原交易请求流水号
     * @param notify_url            异步通知地址
     * @return
     */
    public static R<String> weixinPaymentRefund(String req_seq_id, Double ord_amt, String org_req_date, String org_party_order_id, String notify_url){
    public static R<String> weixinPaymentRefund(String req_seq_id, Double ord_amt, String org_req_date, String org_req_seq_id, String notify_url){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        NumberFormat numberInstance = NumberFormat.getInstance();
        //最大两位小数
        numberInstance.setMaximumFractionDigits(2);
        String url = "https://api.huifu.com/v2/trade/payment/scanpay/refund";
        JSONObject data = new JSONObject();
        data.put("req_date", sdf.format(new Date()));
        data.put("req_seq_id", req_seq_id);
        data.put("huifu_id", huifu_id);
        data.put("ord_amt", ord_amt);
        data.put("ord_amt", String.format("%.2f", ord_amt));
        data.put("org_req_date", org_req_date);
        data.put("org_party_order_id", org_party_order_id);
        data.put("org_req_seq_id", org_req_seq_id);
        data.put("notify_url", notify_url);
        JSONObject body = new JSONObject();
@@ -178,23 +181,20 @@
        headers.put("Accept", "application/json");
        post.addHeaders(headers);
        post.body(body.toJSONString());
        System.err.println(JSON.toJSONString(body));
        HttpResponse execute = post.execute();
        String result1 = execute.body();
        execute.close();
        if(StringUtils.hasLength(result1)){
            JSONObject result = JSONObject.parseObject(result1);
            String result_data = result.getString("data");
            String result_sign = result.getString("sign");
            boolean verify = verify(result_data, result_sign);
            if(!verify){
                return R.fail("结果验签失败");
            }
            JSONObject resultData = result.getJSONObject("data");
            String resp_code = resultData.getString("resp_code");
            String resp_desc = resultData.getString("resp_desc");
            String trans_stat = resultData.getString("trans_stat");
            //交易受理成功;注:交易状态以trans_stat为准;
            String success1 = "00000000";
            if(success1.equals(resp_code)){
            String success2 = "00000100";
            if(success1.equals(resp_code) || success2.equals(resp_code)){
                String req_seq_id1 = resultData.getString("req_seq_id");
                return R.ok(req_seq_id1);
            }
@@ -206,14 +206,13 @@
    /**
     * 微信支付退款回调通知
     * @param jsonObject
     * @return
     */
    public static R<WeixinPaymentRefundVo> weixinPaymentRefundNotify(JSONObject jsonObject){
        String sign = jsonObject.getString("sign");
        JSONObject data = jsonObject.getJSONObject("data");
        String resp_code = data.getString("resp_code");
        String resp_desc = data.getString("resp_desc");
    public static R<WeixinPaymentRefundVo> weixinPaymentRefundNotify(WechatPaymentRefundDto dto){
        String sign = dto.getSign();
        JSONObject data = JSON.parseObject(dto.getResp_data());
        String resp_code = data.getString("sub_resp_code");
        String resp_desc = data.getString("sub_resp_desc");
        if("00000000".equals(resp_code)){
            WeixinPaymentRefundVo vo = new WeixinPaymentRefundVo();
            vo.setReqSeqId(data.getString("req_seq_id"));
@@ -263,6 +262,32 @@
    /**
     * RSA私钥签名:签名方式SHA256WithRSA
     * @param data 待签名字符串
     * @return 签名byte[]
     * @throws Exception
     */
    public static String channel_sign(String data) {
        //先对该json对象数据按照参数字典顺序(参数名ASCII码从小到大排序,参数名区分大小写)排序生成字符串,再进行加签和验签。
        data = JSON.toJSONString(JSONObject.parseObject(data, TreeMap.class));
        // Base64 --> Key
        try {
            byte[] bytes = Base64.getDecoder().decode(channel_privateKeyBase64);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
            KeyFactory keyFactory;
            keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            // Sign
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(privateKey);
            signature.update(data.getBytes("UTF-8"));
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * 使用汇付RSA公钥验签
     * @param data 待签名字符串
     * @return 验签结果
@@ -294,17 +319,19 @@
    /**
     * 确认交易
     * @param req_seq_id        流水号
     * @param org_req_date      原交易请求时间
     * @param org_req_seq_id    原交易请求流水号
     * @param acctInfos        分账明细
     * @return
     */
    public static R<PaymentDelaytransHFTXVo> paymentDelaytrans(String req_seq_id, String org_req_seq_id, JSONArray acctInfos){
    public static R<PaymentDelaytransHFTXVo> paymentDelaytrans(String req_seq_id, String org_req_date, String org_req_seq_id, JSONArray acctInfos){
        String url = "https://api.huifu.com/v2/trade/payment/delaytrans/confirm";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        JSONObject data = new JSONObject();
        data.put("req_seq_id", req_seq_id);
        data.put("req_date", sdf.format(new Date()));
        data.put("huifu_id", huifu_id);
        data.put("org_req_date", org_req_date);
        data.put("org_req_seq_id", org_req_seq_id);
        JSONObject acct_infos = new JSONObject();
@@ -322,6 +349,7 @@
        headers.put("Accept", "application/json");
        post.addHeaders(headers);
        post.body(body.toJSONString());
        System.err.println(JSON.toJSONString(body));
        HttpResponse execute = post.execute();
        String result = execute.body();
        execute.close();
@@ -348,4 +376,14 @@
        return R.fail(resp_desc);
    }
    public static void main(String[] args) {
        //分账对象
        JSONArray acctInfos = new JSONArray();
        com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject();
        jsonObject.put("div_amt", String.format("%.2f", 0.07D));
        jsonObject.put("huifu_id", "6666000141251275");
        acctInfos.add(jsonObject);
        HuiFuTianXiaUtil.paymentDelaytrans(IdUtils.simpleUUID(), "20231020", "0479854e057547698be8b083f2fa550a", acctInfos);
    }
}