puzhibing
2024-01-30 80b3ea5587ff7ec20541d9ca7c6c28739e4d615b
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
package com.dsh.course.util.midtrans;
 
import com.alibaba.fastjson.JSON;
import com.midtrans.Config;
import com.midtrans.Midtrans;
import com.midtrans.httpclient.SnapApi;
import com.midtrans.httpclient.TransactionApi;
import com.midtrans.httpclient.error.MidtransError;
import org.json.JSONObject;
 
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
 
/**
* Midtrans支付工具类
* @author pzb
* @Date 2022/11/28 14:19
*/
public class MidtransUtil {
 
    private static String sandbox_merchant_id = "G118246934";
 
    private static String sandbox_serverKey = "SB-Mid-server-qR4Xy4kbpyxND_7f6c1_knb9";
 
    private static String sandbox_clientKey = "SB-Mid-client-xhRbIV8lsbu7bKCE";
 
    private static String merchant_id = "G118246934";
 
    private static String serverKey = "Mid-server-w1ATM2ldDpd0c4Q0s9sNfeNN";
 
    private static String clientKey = "Mid-client-X22yMA1Wvs0v1vfO";
 
 
    /**
     * 获取支付令牌
     * @param order_id          商户订单id
     * @param gross_amount      支付金额
     * @param xOverrideNotification     支付状态通知回调地址
     * @return
     */
    public static TokenResult getPayToken(String order_id, String gross_amount, String xOverrideNotification){
        //沙箱环境
        Midtrans.serverKey = sandbox_serverKey;
        Midtrans.clientKey = sandbox_clientKey;
        Midtrans.isProduction = false;
 
        //生产环境
//        Midtrans.serverKey = serverKey;
//        Midtrans.clientKey = clientKey;
//        Midtrans.isProduction = true;
 
        Midtrans.paymentOverrideNotification(xOverrideNotification);
        // Create Token and then you can send token variable to FrontEnd,
        // to initialize Snap JS when customer click pay button
        //201    成功创建 Snap 令牌。    “令牌”:“66e4fa55-fdac-4ef9-91b5-733b97d1b862”
        //401    未能创建令牌,因为发送了错误的授权。    “访问被拒绝,请检查客户端或服务器密钥”
        //4xx    未能创建令牌,因为发送了错误的参数。按照 error_message 并检查您的参数。    “transaction_details.gross_amount 不等于 item_details 的总和”
        //5xx    由于 Midtrans 内部错误,无法创建令牌。大多数情况下这是暂时的,您可以稍后重试。    “抱歉,我们遇到内部服务器错误。我们会尽快解决这个问题。”
 
        try {
            JSONObject transaction = SnapApi.createTransaction(requestBody(order_id, gross_amount), Config.getGlobalConfig());
            TokenResult tokenResult = new TokenResult();
            tokenResult.setToken(transaction.getString("token"));
            tokenResult.setRedirect_url(transaction.getString("redirect_url"));
            return tokenResult;
        } catch (MidtransError midtransError) {
            midtransError.printStackTrace();
        }
        return null;
    }
 
 
    // Create params JSON Raw Object request
    public static Map<String, Object> requestBody(String order_id, String gross_amount) {
        Map<String, Object> params = new HashMap<>();
        Map<String, String> transactionDetails = new HashMap<>();
        transactionDetails.put("order_id", order_id);
        transactionDetails.put("gross_amount", gross_amount);
        Map<String, String> creditCard = new HashMap<>();
        creditCard.put("secure", "true");
        params.put("transaction_details", transactionDetails);
        params.put("credit_card", creditCard);
        return params;
    }
 
 
    /**
     * 获取订单支付状态
     * @param order_id
     * @return
     */
    public static PaymentNotice getPayStatus(String order_id){
        //沙箱环境
        Midtrans.serverKey = sandbox_serverKey;
        Midtrans.clientKey = sandbox_clientKey;
        Midtrans.isProduction = false;
 
        //生产环境
//        Midtrans.serverKey = serverKey;
//        Midtrans.clientKey = clientKey;
//        Midtrans.isProduction = true;
        JSONObject jsonObject = null;
        try {
            jsonObject = TransactionApi.checkTransaction(order_id);
        } catch (MidtransError midtransError) {
            midtransError.printStackTrace();
        }
        PaymentNotice paymentNotice = JSON.parseObject(jsonObject.toString(), PaymentNotice.class);
        if(null == paymentNotice){
            System.err.println("回调结果解析失败");
            return null;
        }
        String signature_key = paymentNotice.getSignature_key();
        order_id = paymentNotice.getOrder_id();
        String status_code = paymentNotice.getStatus_code();
        if("400".equals(status_code)){
            System.err.println("数据丢失或无效");
            return null;
        }
        if("401".equals(status_code)){
            System.err.println("授权错误");
            return null;
        }
        if("404".equals(status_code)){
            System.err.println("找不到请求的资源");
            return null;
        }
        String gross_amount = paymentNotice.getGross_amount();
        String sha_512_securePassword = get_SHA_512_SecurePassword(order_id + status_code + gross_amount + sandbox_serverKey);
        if(!signature_key.equals(sha_512_securePassword)){
            System.err.println("签名校验失败");
            return null;
        }
        return paymentNotice;
    }
 
 
    /**
     * 支付回调解析
     * @param request
     * @return
     */
    public static PaymentNotice payResultNotification(HttpServletRequest request){
        String param = null;
        try {
            param = getParam(request);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
        System.err.println("支付回调结果:========》" + param);
 
        PaymentNotice paymentNotice = JSON.parseObject(param, PaymentNotice.class);
        if(null == paymentNotice){
            System.err.println("回调结果解析失败");
            return null;
        }
        String signature_key = paymentNotice.getSignature_key();
        String order_id = paymentNotice.getOrder_id();
        String status_code = paymentNotice.getStatus_code();
        if("400".equals(status_code)){
            System.err.println("数据丢失或无效");
            return null;
        }
        if("401".equals(status_code)){
            System.err.println("授权错误");
            return null;
        }
        if("404".equals(status_code)){
            System.err.println("找不到请求的资源");
            return null;
        }
        String gross_amount = paymentNotice.getGross_amount();
        String sha_512_securePassword = get_SHA_512_SecurePassword(order_id + status_code + gross_amount + sandbox_serverKey);
        if(!signature_key.equals(sha_512_securePassword)){
            System.err.println("签名校验失败");
            return null;
        }
        return paymentNotice;
    }
 
 
    /**
     * 退款交易
     * @param transaction_id      支付订单id
     * @param refund_key          商户退款id
     * @param amount              退款金额
     * @param reason              退款说明
     * @return
     */
    public static DirectRefundTransaction directRefundTransaction(String transaction_id, String refund_key, String amount, String reason){
        //沙箱环境
        Midtrans.serverKey = sandbox_serverKey;
        Midtrans.clientKey = sandbox_clientKey;
        Midtrans.isProduction = false;
 
        //生产环境
//        Midtrans.serverKey = serverKey;
//        Midtrans.clientKey = clientKey;
//        Midtrans.isProduction = true;
 
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("refund_key", refund_key);
        requestBody.put("amount", amount);
        requestBody.put("reason", reason);
 
        JSONObject jsonObject = null;
        try {
            jsonObject = TransactionApi.directRefundTransaction(transaction_id, requestBody);
        } catch (MidtransError midtransError) {
            midtransError.printStackTrace();
        }
        /**
         * {
         *   "status_code": "200",
         *   "status_message": "Success, refund request is approved by the bank",
         *   "transaction_id": "fddb5889-fd39-46fe-809d-30679fe42434",
         *   "order_id": "MID-1620622357",
         *   "gross_amount": "10000.00",
         *   "payment_type": "credit_card",
         *   "transaction_time": "2016-06-28 09:42:20",
         *   "transaction_status": "refund",
         *   "refund_chargeback_id": 47594,
         *   "refund_amount": "10000.00",
         *   "refund_key": "01f1f771-b75c-48ef-b21d-193a79f8aa5b"
         * }
         */
 
        DirectRefundTransaction directRefundTransaction = JSON.parseObject(jsonObject.toString(), DirectRefundTransaction.class);
        if(null == directRefundTransaction){
            System.err.println("退款结果解析失败");
            return null;
        }
        String status_code = directRefundTransaction.getStatus_code();
        if("400".equals(status_code)){
            System.err.println("数据丢失或无效");
            return null;
        }
        if("401".equals(status_code)){
            System.err.println("授权错误");
            return null;
        }
        if("404".equals(status_code)){
            System.err.println("找不到请求的资源");
            return null;
        }
        return directRefundTransaction;
    }
 
 
 
 
    /**
     * 获取请求内容
     * @param request
     * @return
     * @throws IOException
     */
    private static String getParam(HttpServletRequest request) throws IOException {
        // 读取参数
        InputStream inputStream;
        StringBuilder sb = new StringBuilder();
        inputStream = request.getInputStream();
        String s;
        BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        while ((s = in.readLine()) != null) {
            sb.append(s);
        }
        in.close();
        inputStream.close();
        return sb.toString();
    }
 
 
    private static String get_SHA_512_SecurePassword(String passwordToHash){
        String generatedPassword = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-512");
            byte[] bytes = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for(int i=0; i< bytes.length ;i++){
                sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
            }
            generatedPassword = sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return generatedPassword;
    }
 
 
}