duhuizhe
2024-05-15 aa2c3d4deba76ade0958ff3ced88396e226a4964
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
package com.nuvole.hnnx.hnnxPay;
 
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.csii.sg.codec.Hex;
import com.csii.sg.security.ValidationException;
import com.csii.sg.security.impl.Permutation;
import com.csii.sg.security.impl.SignatureVerifier;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nuvole.hnnx.conf.NxPayConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 
/**
 * @ClassName HttpClientUtils
 * @Author cy
 * @Date 2023/9/27
 * @Description
 * @Version 1.0
 **/
 
@Component
@Slf4j
//@ConditionalOnClass(name = "com.nuvole.hnnx.conf.NxPayConfig")
public class NXHttpClientUtils {
    @Autowired
    private PrivateKey nxPayPrivateKey;
    @Autowired
    private PrivateKey nxKjPayPrivateKey;
 
    @Autowired
    private PublicKey nxPayPublicKey;
    @Autowired
    private PublicKey nxKjPayPublicKey;
 
    @Autowired
    private NxPayConfig nxPayConfig;
 
    private static SignatureSigner signable = new SignatureSigner();
 
    private static SignatureVerifier verifier = new SignatureVerifier();
 
    public String getPlatSubMerchantId() {
        return nxPayConfig.getPlatSubMerchantId();
    }
 
    public String getKjPlatSubMerchantId() {
        return nxPayConfig.getKjPlatSubMerchantId();
    }
 
    public String getBranchId() {
        return nxPayConfig.getBranchId();
    }
 
 
    /**
     * 分转为元
     *
     * @param payMoney
     * @return
     */
    public static BigDecimal convertToYuan(long payMoney) {
        BigDecimal totalAmount = new BigDecimal("" + payMoney).divide(new BigDecimal("100"));
        return totalAmount;
    }
 
    public static void writeToFile(String sign, String plainStr) {
        File file = new File("D:\\dev_nginx-1.18.0\\html\\testNetPay4.html");
 
        String content = "<!DOCTYPE html>\n" +
                "<html lang=\"en\">\n" +
                "<head>\n" +
                "    <meta charset=\"UTF-8\">\n" +
                "    <title>Title</title>\n" +
                "\n" +
                "</head>\n" +
                "<body>\n" +
                "\n" +
                "</body>\n" +
                "<script>\n" +
//                "    var url = \"http://221.176.112.3:8006/pmPay/pmPay/quickPayRequest.do\";\n" +
                "    var url = \"http://https://pay.hnnx.com/pmPay/pmPay/quickPayRequest.do\";\n" +
                "    var data ={\n" +
                "        \"deviceType\":\"5\",\n" +
                "        \"signature\": \"" + sign + "\",\n" +
                "        \"plain\": JSON.stringify(" + plainStr + ")\n" +
                "    }\n" +
                "    post(url, data);\n" +
                "    var myForm;\n" +
                "\n" +
                "    function post(URL, pram) {\n" +
                "        var temp = document.createElement(\"form\");\n" +
                "        temp.action = URL;\n" +
                "        temp.method = \"post\";\n" +
                "        temp.style.display = \"none\";\n" +
                "        for (var x in pram) {\n" +
                "            var opt = document.createElement(\"textarea\");\n" +
                "            opt.name = x;\n" +
                "            opt.value = pram[x];\n" +
                "            // alert(opt.name)\n" +
                "            temp.appendChild(opt);\n" +
                "        }\n" +
                "        document.body.appendChild(temp);\n" +
                "        temp.submit();\n" +
                "//        myForm=temp;\n" +
                "    }\n" +
                "</script>\n" +
                "</html>\n";
 
        try (FileWriter writer = new FileWriter(file)) {
            writer.write(content);
            System.out.println("内容成功写入文件。");
        } catch (IOException e) {
            System.err.println("写入文件时发生错误:" + e.getMessage());
        }
    }
 
    public static void writeToFile(String url) {
        File file = new File("D:\\dev_nginx-1.18.0\\html\\redirect1.html");
 
        String content = "<!DOCTYPE html>\n" +
                "<html>\n" +
                "<head>\n" +
                "    <meta http-equiv=\"refresh\" content=\"0; URL='" + url +
                "'\" />\n" +
                "</head>\n" +
                "<body>\n" +
                "    <h1>正在跳转,请稍等...</h1>\n" +
                "</body>\n" +
                "</html>\n";
 
        try (FileWriter writer = new FileWriter(file)) {
            writer.write(content);
            System.out.println("内容成功写入文件。");
        } catch (IOException e) {
            System.err.println("写入文件时发生错误:" + e.getMessage());
        }
    }
 
    /**
     * 加签方法
     *
     * @param headerMap   加密报文头
     * @param payloadJson 加密报文体
     * @return
     */
    public String sign(Map headerMap, String payloadJson) {
        log.info("----------- 签名开始 ----------- ");
        log.info("请求参数payloadJson:{}", payloadJson);
        ObjectMapper objectMapper = new ObjectMapper();
        String palin = null;
        try {
            if (payloadJson != null) {
                Map payloadMap = objectMapper.readValue(payloadJson, Map.class);
                headerMap.putAll(payloadMap);
            }
            palin = Permutation.sort(headerMap, "UTF-8");
            log.info("待签名字符串:{}", palin);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String sign = signable.sign(palin, nxPayPrivateKey);
        log.info("签名结果:{}", sign);
        log.info("----------- 签名结束 ----------- ");
        return sign;
    }
 
    public String kjSign(String palin) {
        log.info("----------- 签名开始 ----------- ");
        log.info("请求参数 palin:{}", palin);
        String sign = signable.kjSign(palin, nxKjPayPrivateKey).replaceAll("\\s", "");
        log.info("签名结果:{}", sign);
        log.info("----------- 签名结束 ----------- ");
        return sign;
    }
 
    // 回调通知校验
    public boolean notifyRespVerify(String notifyParam) {
        log.info("回调通知验签 body 为: {}", notifyParam);
        int signIndex = notifyParam.lastIndexOf("&signature=");
 
        if (signIndex == -1) {
            return false;
        }
 
        String palin = notifyParam.substring(0, signIndex);
        String sign = notifyParam.substring(signIndex).replace("&signature=", "");
 
        boolean verfiy = false;
        try {
            verfiy = verifier.verify(HnnxConstant.ALGORITHM, palin.getBytes(), Hex.toByte(sign), nxPayPublicKey);
        } catch (ValidationException e) {
            e.printStackTrace();
        }
 
        if (verfiy) {
            log.info("返回值验签 结果:{}", verfiy);
        } else {
            log.error("返回值验签 结果:{}", verfiy);
        }
        log.info("----------- 返回值验签 结束 ----------- ");
        return verfiy;
    }
 
    public String sendForm(HttpRequest nxPayHttpClient, HashMap<String, Object> payLoadMap) {
        log.info("transCode : {}", nxPayHttpClient.header("transCode"));
        Map headerMap = nxPayConfig.getHeadMap(nxPayHttpClient.header("sequenceId"), nxPayHttpClient.header("timestamp"), nxPayHttpClient.header("branchId"));
        HashMap<String, Object> formData = new HashMap<>();
        formData.put("payload", JSONObject.toJSONString(payLoadMap));
        // 及具体接口的请求报文构成。
        log.info("请求头: {}", JSONObject.toJSONString(headerMap));
        formData.put("signature", sign(headerMap, (String) formData.get("payload")));
        log.info("请求参数: {}", JSONObject.toJSONString(formData));
        HttpResponse httpResponse = nxPayHttpClient.form(formData).execute();
        String body = httpResponse.body();
        verify(body);
        return body;
    }
 
    /**
     * 预支付
     * 可以实现用户到支付宝或微信的预支付功能。
     *
     * @param subMerchantId 二级商户号
     * @param branchId      交易机构号
     * @param merSeqNbr     商户总交易流水号
     * @param transAmt      交易总金额
     * @param timeStamp     商户交易时间
     * @param channelNbr    渠道编号 01-O2O 02-B2C 03-扫码
     * @param payTypCd      B-支付宝扫码支付C-微信扫码支付D-银联扫码支付
     * @param orderTitle    订单标题
     */
    public String qrCodePrePay(String subMerchantId, String branchId, String operatorId, String merSeqNbr, String authCode, long transAmt, String timeStamp, String channelNbr, char payTypCd,
                               String merUrl, String orderTitle) {
        log.info("----------------- 预支付 开始 订单号为: {},订单金额为:{}----------------- ", merSeqNbr, transAmt);
        HttpRequest nxPayHttpClient = nxPayConfig.getPayCloseableHttpClient();
        //请求序列号【渠道端产生,一笔业务的唯一标识,贯穿整个交易始终】渠道代码(2位)+日期(yyMMdd 6位)+流水号(24位,appCode+剩余位数流水号)
//        String sequenceId = getSequenceId(timeStamp, nxPayConfig.getAppCode());
//        nxPayHttpClient.header("sequenceId", sequenceId);
        nxPayHttpClient.header("timestamp", timeStamp);
        nxPayHttpClient.header("transCode", HnnxConstant.TRANS_CODE_QRCODEPREPAY_PAY);
        nxPayHttpClient.header("branchId", branchId);
 
        // 拼接业务数据
        HashMap<String, Object> formData = new HashMap<>();
        // 一级商户号
        formData.put("MerNbr", nxPayConfig.getMerNbr());
        // 二级商户编号
        formData.put("SubMerchantId", subMerchantId);
        //商户交易时间
        formData.put("MerTransDateTime", timeStamp);
        //交易总金额
        formData.put("TransAmt", convertToYuan(transAmt));
        formData.put("AuthCode", authCode);
 
        //渠道编号 01-O2O 02-B2C 03-扫码
        formData.put("ChannelNbr", channelNbr);
        //支付方式 8-线上支付宝扫码支付9-线上微信扫码支付A-线上银联扫码支付B-线下支付宝扫码支付C-线下微信扫码支付D-线下银联扫码支付
        formData.put("PayTypCd", payTypCd);
        formData.put("OrderTitle", orderTitle);
        formData.put("CurrencyCd", "CNY");
        //异步通知Url
        formData.put("MerUrl", merUrl);
        //操作员
        formData.put("OperatorId", operatorId);
 
        //支付订单号
        formData.put("MerSeqNbr", merSeqNbr);
        String body = sendForm(nxPayHttpClient, formData);
        log.info("----------------- 预支付 结束 ----------------- ");
        return body;
    }
 
    /**
     * 用户主动扫码生成
     * 用户需要扫描商户收款码动码进行支付时,通过此接口生成商户收款码动码,生产上现在微信主扫动码已经停止使用。
     *
     * @param subMerchantId 二级商户号
     * @param branchId      交易机构号
     * @param merSeqNbr     商户总交易流水号
     * @param transAmt      交易总金额
     * @param timeStamp     商户交易时间
     * @param channelNbr    渠道编号 01-O2O 02-B2C 03-扫码
     * @param payTypCd      B 支付宝扫码支付 C    微信扫码支付 D    银联扫码支付
     * @param orderTitle    订单标题
     */
    public String dyActiveQrCode(String subMerchantId, String branchId, String merSeqNbr, long transAmt, String timeStamp, String channelNbr, char payTypCd,
                                 String merUrl, String orderTitle) {
        log.info("----------------- 用户主动扫码生成 开始 订单号为: {},订单金额为:{}----------------- ", merSeqNbr, transAmt);
        HttpRequest nxPayHttpClient = nxPayConfig.getPayCloseableHttpClient();
        //请求序列号【渠道端产生,一笔业务的唯一标识,贯穿整个交易始终】渠道代码(2位)+日期(yyMMdd 6位)+流水号(24位,appCode+剩余位数流水号)
//        String sequenceId = getSequenceId(timeStamp, nxPayConfig.getAppCode());
//        nxPayHttpClient.header("sequenceId", sequenceId);
        nxPayHttpClient.header("timestamp", timeStamp);
        nxPayHttpClient.header("transCode", HnnxConstant.TRANS_CODE_DYACTIVEQRCODE_PAY);
        nxPayHttpClient.header("branchId", branchId);
 
        // 拼接业务数据
        HashMap<String, Object> formData = new HashMap<>();
        // 一级商户号
        formData.put("MerNbr", nxPayConfig.getMerNbr());
        //商户总交易流水号
        formData.put("MerSeqNbr", merSeqNbr);
        //交易总金额
        formData.put("TransAmt", convertToYuan(transAmt));
        //商户交易时间
        formData.put("MerTransDateTime", timeStamp);
        //渠道编号 01-O2O 02-B2C 03-扫码
        formData.put("ChannelNbr", channelNbr);
        formData.put("CurrencyCd", "CNY");
        //支付方式 B 支付宝扫码支付 C    微信扫码支付 D    银联扫码支付
        formData.put("PayTypCd", payTypCd);
        // 二级商户编号
        formData.put("SubMerchantId", subMerchantId);
        //异步通知Url
        formData.put("MerUrl", merUrl);
        formData.put("OrderTitle", orderTitle);
 
        String body = sendForm(nxPayHttpClient, formData);
        log.info("----------------- 用户主动扫码生成  结束 ----------------- ");
        return body;
    }
 
    /**
     * 用户被扫支付
     * 第三方平台扫描用户付款码,通过此接口提交支付
     */
    public String coretoall(String subMerchantId, String branchId, String merSeqNbr, long transAmt, String payAuthCode, String timeStamp, String channelNbr,
                            String merUrl, String orderTitle, String terminalCode) {
        log.info("----------------- 用户被扫支付 开始, 订单编号= {} ,订单金额为:{}----------------- ", merSeqNbr, transAmt);
        HttpRequest nxPayHttpClient = nxPayConfig.getPayCloseableHttpClient();
 
        nxPayHttpClient.header("timestamp", timeStamp);
        nxPayHttpClient.header("transCode", HnnxConstant.TRANS_CODE_CORETOALL_PAY);
        nxPayHttpClient.header("branchId", branchId);
 
        // 拼接业务数据
        HashMap<String, Object> formData = new HashMap<>();
        // 一级商户号
        formData.put("MerNbr", nxPayConfig.getMerNbr());
        //商户总交易流水号
        formData.put("MerSeqNbr", merSeqNbr);
        //交易总金额
        formData.put("TransAmt", convertToYuan(transAmt));
        //商户交易时间
        formData.put("MerTransDateTime", timeStamp);
        //二维码
        formData.put("QrCodeInfo", payAuthCode);
        //渠道编号 01-O2O 02-B2C 03-扫码
        formData.put("ChannelNbr", channelNbr);
        formData.put("CurrencyCd", HnnxConstant.CURRENCYCD);
        formData.put("AreaInfo", HnnxConstant.AREAINFO);
 
        // 二级商户编号
        formData.put("SubMerchantId", subMerchantId);
        //订单详情
        formData.put("OrderTitle", orderTitle);
        //异步通知Url
        formData.put("MerUrl", merUrl);
        //商户终端设备类型
        formData.put("TerminalType", "04");
        //终端编号 由柜员在后管添加终端时生成
        formData.put("Equipid", "002000156");
        //终端序列号
        formData.put("Equipseqnbr", "000002041170132246");
        formData.put("Lgtude", "113.988007");
        formData.put("Lttude", "34.819642");
        formData.put("AppVersion", "01234567");
        String body = sendForm(nxPayHttpClient, formData);
        log.info("----------------- 用户被扫支付  结束 ----------------- ");
        return body;
    }
 
    /**
     * 订单交易状态查询
     * 接口说明
     * 该接口提供支付、退款交易的交易状态查询。
     * 根据交易状态TransStatus确定交易结果;
     * 微信退款交易状态查询,非银行卡交易一般在15秒内可查询交易成功,银行卡交易退款需在两分钟后可查询到交易成功。
     *
     * @param merSeqNbr 订单流水号
     * @return
     * @throws Exception
     */
    public String qryQrOrderStatus(String merSeqNbr, String branchId) {
        log.info("----------------- 订单交易状态查询 开始----------------- ");
        HttpRequest nxPayHttpClient = nxPayConfig.getPayCloseableHttpClient();
        String timeStamp = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
        //请求序列号【渠道端产生,一笔业务的唯一标识,贯穿整个交易始终】渠道代码(2位)+日期(yyMMdd 6位)+流水号(24位,appCode+剩余位数流水号)
//        String sequenceId = getSequenceId(timeStamp, nxPayConfig.getAppCode());
//        nxPayHttpClient.header("sequenceId", sequenceId);
        nxPayHttpClient.header("timestamp", timeStamp);
        nxPayHttpClient.header("transCode", HnnxConstant.TRANS_CODE_QRYQRORDERSTATUS_PAY);
        nxPayHttpClient.header("branchId", branchId);
 
        // 拼接业务数据
        HashMap<String, Object> formData = new HashMap<>();
        // 一级商户号
        formData.put("MerNbr", nxPayConfig.getMerNbr());
        //商户总交易流水号
        formData.put("MerSeqNbr", merSeqNbr);
 
        String body = sendForm(nxPayHttpClient, formData);
        log.info("----------------- 订单交易状态查询  结束 ----------------- ");
        return body;
    }
 
    /**
     * 退款
     *
     * @param subMerchantId 原支付交易二级商户号
     * @param branchId
     * @param merSeqNbr     要退款的订单号
     * @param merSeqDate    要退款的订单交易日期 例:20190810
     * @param origTransAmt  要退款的订单的交易金额
     * @param transAmt      退款金额
     * @param timeStamp     当前日期 例:yyyyMMddHHmmss
     * @return
     */
    public String returntrans(String subMerchantId, String branchId, String merSeqNbr, String merSeqDate, long origTransAmt,
                              long transAmt, String timeStamp) {
        log.info("----------------- 退款 开始----------------- ");
        HttpRequest nxPayHttpClient = nxPayConfig.getPayCloseableHttpClient();
        nxPayHttpClient.header("timestamp", timeStamp);
        nxPayHttpClient.header("transCode", HnnxConstant.TRANS_CODE_RETURNTRANS_PAY);
        nxPayHttpClient.header("branchId", branchId);
 
        // 拼接业务数据
        HashMap<String, Object> formData = new HashMap<>();
        // 一级商户号
        formData.put("MerNbr", nxPayConfig.getMerNbr());
        //商户总交易流水号
        formData.put("MerSeqNbr", "TK_" + merSeqNbr);
        //退货交易时间
        formData.put("MerTransDateTime", timeStamp);
 
        //原商户流水
        formData.put("OrigMerSeqNbr", merSeqNbr);
        //原商户日期
        formData.put("OrigMerDate", merSeqDate);
        //原支付交易金额
        formData.put("OrigTransAmt", convertToYuan(origTransAmt));
        //二级商户号
        formData.put("SubMerchantId", subMerchantId);
 
        //退货二级商户流水号(随机生成,不允许出现重复,可以和一级商户交易流水号保持一致)
        formData.put("SubMerSeqNo", "TK_" + merSeqNbr);
        //二级商户时间
        formData.put("SubMerDateTime", timeStamp);
        //二级商户退货交易退货金额
        formData.put("SubTransAmt", convertToYuan(transAmt));
        //原二级商户支付流水号(与原支付交易一级商户号流水号一致)
        formData.put("OrigSubMerSeqNo", merSeqNbr);
        //原二级商户支付交易日期(例:20190810)
        formData.put("OrigSubMerDate", merSeqDate);
        //订单号 与二级商户流水号保持一致
        formData.put("OrderNbr", merSeqNbr);
        String body = sendForm(nxPayHttpClient, formData);
        log.info("----------------- 退款 结束----------------- ");
        return body;
    }
 
    /**
     * 对账查询
     * String subMerchantId, String branchId,
     *
     * @param clearDate 对账日期    YYYY-MM-DD
     * @param branchId  交易机构号
     * @return
     */
    public String accountqry(String clearDate, String branchId) {
        log.info("----------------- 对帐 开始----------------- ");
        HttpRequest nxPayHttpClient = nxPayConfig.getPayCloseableHttpClient();
        nxPayHttpClient.header("timestamp", DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN));
        nxPayHttpClient.header("transCode", HnnxConstant.TRANS_CODE_ACCOUNTQRY_PAY);
        nxPayHttpClient.header("branchId", branchId);
 
        // 拼接业务数据
        HashMap<String, Object> formData = new HashMap<>();
        formData.put("ClearDate", clearDate);
 
        String body = sendForm(nxPayHttpClient, formData);
        log.info("----------------- 对帐 结束----------------- ");
        return body;
    }
 
    // 订单号生成规则 “渠道简称-订单号-主/优惠-发起支付次数”构成
    // 例如商圈的主订单第一次发起支付,订单号就是“SQ-商圈订单id-Z-1”
    // 如果这个订单关联有优惠金,优惠金订单号就是"SQ-商圈订单id-Y-1"
    public static String getNxPayOrderId(String prefix, long orderId, char orderType, short tryNum) {
        return new StringBuilder(prefix).append("-").append(orderId).append("-").append(orderType).append("-").append(tryNum).toString();
    }
 
    /**
     * 验签
     *
     * @return
     */
    public boolean verify(String body) {
        log.info("返回值验签 body 为: {}", body);
        log.info("----------- 返回值验签 开始 ----------- ");
        boolean verfiy = false;
        try {
            Map<String, Object> bodyMap = JSON.parseObject(body, Feature.OrderedField);
            String sign = (String) bodyMap.remove("signature");
            String payload = (String) bodyMap.remove("payload");
 
            ObjectMapper objectMapper = new ObjectMapper();
            if (payload != null) {
                Map payloadMap = objectMapper.readValue(payload, Map.class);
                bodyMap.putAll(payloadMap);
            }
            String palin = null;
            palin = Permutation.sort(bodyMap, "UTF-8");
            log.info("排序后的字符串:{}", palin);
            if (StrUtil.isNotEmpty(sign)) {
                verfiy = verifier.verify(HnnxConstant.ALGORITHM, palin.getBytes(), Hex.toByte(sign), nxPayPublicKey);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.error("返回值验签 结果:{}", verfiy);
        log.info("----------- 返回值验签 结束 ----------- ");
        return verfiy;
    }
 
    public boolean kjVerify(String sign, String palin) {
        log.info("返回值验签 palin 为: {}", palin);
        log.info("返回值验签 sign 为: {}", sign);
        log.info("----------- 返回值验签 开始 ----------- ");
        boolean verfiy = false;
        try {
            if (StrUtil.isNotEmpty(sign)) {
                verfiy = verifier.verify(HnnxConstant.KJ_ALGORITHM, palin.getBytes(), Base64.getDecoder().decode(sign), nxKjPayPublicKey);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.error("返回值验签 结果:{}", verfiy);
        log.info("----------- 返回值验签 结束 ----------- ");
        return verfiy;
    }
 
    /**
     * 通用快捷支付
     *
     * @param merSeqNbr     商户流水号
     * @param subMerchantId 二级商户号
     * @param backURL       后台通知URL
     * @param frontURL      返回前端URL
     */
    public String quickPayRequest(String subMerchantId, String merSeqNbr, long transAmt, String nowTime,
                                  String frontURL, String backURL, String orderTitle) {
        log.info("----------------- 通用快捷支付 开始, 订单编号= {} ,订单金额为:{}----------------- ", merSeqNbr, transAmt);
 
 
        // 拼接业务数据
        JSONObject formData = new JSONObject();
        formData.put("deviceType", "5");
 
        HashMap<String, Object> plainData = new HashMap<>();
        // plain 开始
        //一级商户id
//        plainData.put("merchId", "010020170801000001");
        plainData.put("merchId", nxPayConfig.getKjMerNbr());
        //优惠前金额    disCountAmt
        //订单名称    orderName
        plainData.put("orderName", orderTitle);
        //币种    ccy
        plainData.put("ccy", "01");
        //返回前端URL    frontURL
        plainData.put("frontURL", frontURL);
        //后台通知URL    backURL
        plainData.put("backURL", backURL);
        //订单备注    orderRemark
        plainData.put("orderRemark", "");
        //渠道号    channelNbr
        plainData.put("channelNbr", "02");
        //交易总金额    txnAmt
        plainData.put("txnAmt", convertToYuan(transAmt));
        //商户流水号    merSeqNbr
        plainData.put("merSeqNbr", merSeqNbr);
        //交易时间    orderTime
        plainData.put("orderTime", nowTime);
        //收款方名称    payee
        //优惠活动名称    specialOffers
 
        //商户订单号    merchOrderId
        plainData.put("merchOrderId", merSeqNbr);
 
        JSONArray plainMerTrans = new JSONArray();
        JSONObject plainMerTransData1 = new JSONObject();
        //子交易金额
        plainMerTransData1.put("subTransAmt", convertToYuan(transAmt));
        //二级商户号
        plainMerTransData1.put("subMerchantId", subMerchantId);
        //二级商户交易时间    subMerDateTime
        plainMerTransData1.put("subMerDateTime", nowTime);
        //子流水号    subMerSeqNo
        plainMerTransData1.put("subMerSeqNo", merSeqNbr);
        //二级商户名称    subMerchantName
        plainMerTransData1.put("subMerchantName", "");
        //二级商户商品信息    productInfo
        plainMerTransData1.put("productInfo", "");
        //二级商户信息    subMerImport
//        plainMerTransData1.put("subMerImport", "学费");
//        plainMerTransData1.put("subMerImport", orderTitle);
        //卡券号    platCouponId
        plainMerTransData1.put("platCouponId", "");
        //子卡券交易金额    platCouponAmount
        plainMerTransData1.put("platCouponAmount", "");
        //子积分数    bonusPoint
        plainMerTransData1.put("bonusPoint", "");
        //子积分交易金额    bonusPointAmount
        plainMerTransData1.put("bonusPointAmount", "");
 
//        plainMerTransData1.put("subInteralAmt", "0.01");
//        plainMerTransData1.put("subCouponAmt", "");
 
        plainMerTrans.add(plainMerTransData1);
        plainData.put("MerTransList", plainMerTrans);
 
        String plainStr = JSONObject.toJSONString(plainData);
        log.info("请求参数:{}", plainStr);
//        formData.put("plain", plainData);
        formData.put("plain", plainStr);
        // plain 结束
 
        // 开始签名
        String signature = kjSign(plainStr);
        formData.put("signature", signature);
//        writeToFile(signature, plainStr);
 
        HttpResponse httpResponse = HttpRequest.post(nxPayConfig.getKjUrl() + HnnxConstant.QUICK_PAY_URL)
                .contentType("application/x-www-form-urlencoded;")
                .form(formData.getInnerMap())
                .timeout(20000)//超时,毫秒
                .execute();
        String location = null;
        log.info("----------------- 通用快捷支付  结束 ----------------- ");
        if (httpResponse.getStatus() == 302) {
            location = httpResponse.header("Location");
//            writeToFile(location);
            return location;
        }else {
            throw new RuntimeException(httpResponse.body());
        }
 
    }
 
    /**
     * 快捷支付 订单状态查询
     *
     * @param orderNbr  订单编号
     * @param startDate 起始日期     yyyyMMdd
     * @param endDate   结束日期 yyyyMMdd
     * @param pageSize  每页记录数
     * @param pageNum   当前第几页
     * @return
     */
 
    public String quickPayOrderQueryRequest(String orderNbr, String startDate, String endDate, int pageSize, int pageNum) {
        log.info("----------------- 快捷支付订单交易状态查询 开始----------------- ");
        String body = null;
        JSONObject formData = new JSONObject();
        formData.put("MerNbr", nxPayConfig.getKjMerNbr());
        //订单编号
        if (StrUtil.isNotEmpty(orderNbr)) {
            formData.put("OrderNbr", orderNbr);
        }
        //起始日期
        if (StrUtil.isNotEmpty(orderNbr)) {
            formData.put("StartDate", startDate);
        }
        //结束日期
        if (StrUtil.isNotEmpty(orderNbr)) {
            formData.put("EndDate", endDate);
        }
        //每页记录数
        formData.put("PageSize", pageSize);
        //当前第几页
        formData.put("PageNum", pageNum);
        log.info("快捷支付订单查询参数:{}", formData);
        body = HttpRequest.post(nxPayConfig.getKjUrl() +  HnnxConstant.QUICK_PAY_QUERY_URL)
                .contentType("application/x-www-form-urlencoded;")
                .form(formData.getInnerMap())
                .timeout(20000)//超时,毫秒
                .execute().body();
        log.info("快捷支付订单查询请求结果:{}", body);
        log.info("----------------- 快捷支付订单交易状态查询  结束 ----------------- ");
        return body;
    }
 
    /**
     * 退款
     *
     * @param subMerchantId 原支付交易二级商户号
     * @param merSeqNbr     要退款的订单号
     * @param merSeqDate    要退款的订单交易日期 yyyyMMdd 例:20190810
     * @param origTransAmt  要退款的订单的交易金额
     * @param transAmt      退款金额
     * @param timeStamp     当前日期 例:yyyyMMddHHmmss
     * @return
     */
    public String quickPayReturntrans(String subMerchantId, String merSeqNbr, String merSeqDate, long origTransAmt,
                                      long transAmt, String timeStamp) {
        log.info("----------------- 退款 开始----------------- ");
        String timeStampWithSpace = timeStamp.substring(0, 8) + " " + timeStamp.substring(8);
        // 拼接业务数据
        HashMap<String, Object> formData = new HashMap<>();
        // 一级商户号
        formData.put("MerNbr", nxPayConfig.getKjMerNbr());
        //商户总交易流水号
        formData.put("MerSeqNbr", "TK_" + merSeqNbr);
        //退货交易时间 yyyyMMdd HHmmss  差异!!!!
        formData.put("MerTransDateTime", timeStampWithSpace);
//        formData.put("MerTransDateTime", timeStamp);
 
        //原商户流水
        formData.put("OrigMerSeqNbr", merSeqNbr);
        //原商户日期
        formData.put("OrigMerDate", merSeqDate);
        //原支付交易金额
        formData.put("OrigTransAmt", convertToYuan(origTransAmt));
 
        //二级商户号
        formData.put("SubMerchantId", subMerchantId);
        //退货二级商户流水号(随机生成,不允许出现重复,可以和一级商户交易流水号保持一致)
        formData.put("SubMerSeqNo", "TK_" + merSeqNbr);
        //二级商户时间
        formData.put("SubMerDateTime", timeStampWithSpace);
//        formData.put("SubMerDateTime", timeStamp);
        //二级商户退货交易退货金额
        formData.put("SubTransAmt", convertToYuan(transAmt));
 
 
        //原二级商户支付流水号(与原支付交易一级商户号流水号一致)
        formData.put("OrigSubMerSeqNo", merSeqNbr);
        //原二级商户支付交易日期(例:20190810)
        formData.put("OrigSubMerDate", merSeqDate);
        //订单号 与二级商户流水号保持一致
        formData.put("OrderNbr", merSeqNbr);
        log.info("快捷支付订单退款参数:{}", formData);
 
        String body = null;
        HttpResponse httpResponse = HttpRequest.post(nxPayConfig.getKjUrl() + HnnxConstant.QUICK_PAY_RETURN_URL)
                .contentType("application/x-www-form-urlencoded;")
                .form(formData)
                .timeout(20000)//超时,毫秒
                .execute();
        body = httpResponse.body();
        log.info(" 快捷支付退款结果:{}", body);
        log.info("----------------- 退款 结束----------------- ");
        return body;
    }
 
 
 
}