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 requestBody(String order_id, String gross_amount) { Map params = new HashMap<>(); Map transactionDetails = new HashMap<>(); transactionDetails.put("order_id", order_id); transactionDetails.put("gross_amount", gross_amount); Map 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 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; } }