From afa0dbb4f54e7244835dd67ec33c3e545f122f71 Mon Sep 17 00:00:00 2001 From: mitao <2763622819@qq.com> Date: 星期五, 17 一月 2025 16:40:43 +0800 Subject: [PATCH] bug修改 --- ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/HuiFuTianXiaUtil.java | 181 +++++++++++++++++++++++++++++++++++--------- 1 files changed, 142 insertions(+), 39 deletions(-) diff --git a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/HuiFuTianXiaUtil.java b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/HuiFuTianXiaUtil.java index e51b325..886d78a 100644 --- a/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/HuiFuTianXiaUtil.java +++ b/ruoyi-modules/ruoyi-order/src/main/java/com/ruoyi/order/util/HuiFuTianXiaUtil.java @@ -7,18 +7,24 @@ 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.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; +import java.math.BigDecimal; +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.*; @@ -28,6 +34,7 @@ * @Date 2023/10/12 0:02 */ public class HuiFuTianXiaUtil { + private static Logger logger = LoggerFactory.getLogger(HuiFuTianXiaUtil.class); //私钥(Base64编码) private static String privateKeyBase64 = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCs658vv/6VdoSGyxr7gVNxlsFGKC2RE4I3S3C78f7t29t6oSm7tdRUr+B/I1VtV1Ps2SQMqG+gUpLiVbHEj+U8PjrvSht6cBNpqhnUjBASEhO0zeCJXNj3zqEDj7CnzDbUzuZWZDyLEaSLRaVb/DSW34D0GDsGWpIMLui/lnubXCYMNsUJBgRmV/tSv7vApVok3aKDoYkZMDhfnKrbxTtWkDfwNLDCInB300YbGeN7hhECRestktymHw0I78x3g+pdzOKOSofqdHyBCemVgOBt2h+oulVU65jc5ok0vwhx7sk2+i3Dw/S/hx3fpnOAToLhpFSTCqArG5wOhiOTX5RRAgMBAAECggEAT+uu7VDdh5L5eZtR40BIos08kwJq3QyZOJlDh3o3ixP8gtcOBNXrRyDqr5hTGXAtN+C8SqVmwkCn5rgYq3KeTpOshYBI5vP55PJbceMyYke8l+o3hwVEunPj81NsIIHALedaij9VxCjpVf1gif0tCTZP0YGJdkxSCBN+O15/zrOred2xkbAGXCNM2j+uIOZ5LTQ064jUAm9KWO4JvVnlNCQPiJCfe652i4RE9adgTlT7d9XffQ/oZDZcDkD1OVUO4ErsSgvjsjPW9Qk8zAVm/C+ycs27GUbVf6yh3zMoL4JDshzoMYAVDqP3VkpYGiW71PzcOZqktcjbtCom4/gpqQKBgQDgHrsQVzMwr18gRAWl8PsgHyMvhyivzk7uPvm6nK9mvEGg7wnrCMKHkpEOCi4mHWnW6H9ptrkeAQbHN7H8Qd6JZm/H0VN8N8fg4IFarlrLWXET6s7or9btCf4aq8DGrU9C+7fcngSfuqlrSxy9e+9InmzUFGKVLBtYIJbG1r00qwKBgQDFhHhhc4WC1MrWLSGhpaNp+swPstfzUBeX2MtZOIv6yXTYPvJ77AAZoLnW482eUZucltCAUucEtTsop25dKdhbdhbVJ/YRRnaklBL2rai4HX+fjoV29tepJhrOrq3Ey+vqvDEsqrBz2C6YyJ6d9aaPPwCFsnDRO+g7tK9S4ZfC8wKBgGlhKbXqOnCW1RSXx0waPgoBegrb816OlABj7go3qCWXOQiRvMkdilIysS+3y7EyPVTGZzpupyYbd3Nulaq10gwq9T4wB6Atnjb3ePsJZjmdKZUi3/z6yQUVoLwrq+3/CXjGYNJEHop4Ozbuz2ms/3rWuvcRSrU3UvgiAqMlzzsXAoGAJY7xDSbYZm5sO5nNYV2L22UzVHDfm4bJVbiFwmt8Qq34J/6gQWw5AKaNcA1VVhuSiY4sC5q4Dy8fFrXvyItidtHXuaSlp9dMTDNT/9A52vjRAdgqDNsXWzTBuDeDF37nV3V9rE6wyvZ6tdbAQq0phOeoA8RQq5jGqkPLauk24H0CgYEAn92Ru9UuloMmTrJbpeeLmMkwCzKff4SpYSY3WzidjdlfHpyNqBUMYb5Ym3OplrSONT9o2H5mEQipjatiu0teILBT7j+f9ZJYQ3vUurAXbmKtthHuEV4lueylNUgmkv+dm295n2x79w1mk/0emJ/Qr9tz43dpSV/kRCxc+lX+OFk="; //公钥(Base64编码) @@ -43,6 +50,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 +72,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 +79,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 +98,13 @@ headers.put("Content-type","application/json; charset=utf-8"); headers.put("Accept", "application/json"); post.addHeaders(headers); + logger.info(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 +123,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,9 +143,65 @@ 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); + } + + + /** + * 查询支付数据 + * @param org_req_seq_id 请求流水号 + * @param org_req_date 请求日期 + * @return + */ + public static R<WeixinPaymentNotifyVo> queryPayment(String org_req_seq_id, String org_req_date){ + String url = "https://api.huifu.com/v2/trade/payment/scanpay/query"; + JSONObject data = new JSONObject(); + data.put("huifu_id", huifu_id); + data.put("org_req_seq_id", org_req_seq_id); + data.put("org_req_date", org_req_date); + + JSONObject body = new JSONObject(); + body.put("sys_id", huifu_id); + body.put("product_id", product_id); + body.put("sign", sign(data.toJSONString())); + body.put("data", data); + HttpRequest post = HttpUtil.createPost(url); + Map<String, String> headers = new HashMap<>(); + headers.put("Content-type","application/json; charset=utf-8"); + headers.put("Accept", "application/json"); + post.addHeaders(headers); + logger.info(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); + JSONObject resultData = result.getJSONObject("data"); + String resp_code = resultData.getString("resp_code"); + String resp_desc = resultData.getString("resp_desc"); + //交易受理成功;注:交易状态以trans_stat为准; + String success1 = "00000000"; + if(success1.equals(resp_code)){ + WeixinPaymentNotifyVo vo = new WeixinPaymentNotifyVo(); + vo.setReqSeqId(resultData.getString("org_req_seq_id")); + vo.setReqDate(resultData.getString("org_req_date")); + vo.setTransAmt(resultData.getString("trans_amt")); + vo.setPayAmt(resultData.getString("pay_amt")); + vo.setEndTime(resultData.getString("end_time")); + vo.setTransStat(resultData.getString("trans_stat")); + String fee_type = resultData.getString("fee_type"); + vo.setFeeFlag("INNER".equals(fee_type) ? 2 : 1); + vo.setFeeAmount(resultData.getString("fee_amt")); + return R.ok(vo); + } + return R.fail(resp_desc); + } + return R.fail("请求异常"); } @@ -148,23 +212,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 +239,20 @@ headers.put("Accept", "application/json"); post.addHeaders(headers); post.body(body.toJSONString()); + logger.info(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 +264,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 +320,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 +377,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 +407,7 @@ headers.put("Accept", "application/json"); post.addHeaders(headers); post.body(body.toJSONString()); + logger.info(JSON.toJSONString(body)); HttpResponse execute = post.execute(); String result = execute.body(); execute.close(); @@ -348,4 +434,21 @@ return R.fail(resp_desc); } + + public static void main(String[] args) { + //174.91 + //分账对象 + JSONArray acctInfos = new JSONArray(); + com.alibaba.fastjson2.JSONObject jsonObject = new com.alibaba.fastjson2.JSONObject(); + jsonObject.put("div_amt", String.format("%.2f", 173.35)); + jsonObject.put("huifu_id", "6666000141286934"); + acctInfos.add(jsonObject); + //平台商户 + com.alibaba.fastjson2.JSONObject jsonObject1 = new com.alibaba.fastjson2.JSONObject(); + jsonObject1.put("div_amt", String.format("%.2f", 1.56)); + jsonObject1.put("huifu_id", "6666000141216769"); + acctInfos.add(jsonObject1); + R<PaymentDelaytransHFTXVo> paymentDelaytransHFTXVoR = HuiFuTianXiaUtil.paymentDelaytrans(IdUtils.simpleUUID(), "20231111", "57dbe646b14542ce8bce166490d3e46e", acctInfos); + System.err.println(JSON.toJSONString(paymentDelaytransHFTXVoR)); + } } -- Gitblit v1.7.1