package com.stylefeng.guns.modular.system.util; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Set; /** * 创博优订单支付适配工具。 */ @Component public class CbyzxOrderPayHelper { private static final String CALLBACK_PATH = "/base/cbyzxPayOrderTaxi"; private static final Set SUCCESS_STATUS = new HashSet<>(Arrays.asList( "SUCCESS", "SUCCEEDED", "PAY_SUCCESS", "TRADE_SUCCESS", "PAID", "PAYED", "1", "00", "TRUE" )); private static final Set FAIL_STATUS = new HashSet<>(Arrays.asList( "FAIL", "FAILED", "CLOSED", "CANCEL", "CANCELED", "CANCELLED", "0", "FALSE" )); @Value("${callbackPath}") private String callbackPath; public JSONObject createOrder(Integer buyerId, String openId, String outRequestNo, Double amount) { CbyzxPayUtil.CreatePayOrderRequest request = new CbyzxPayUtil.CreatePayOrderRequest(); request.setOutRequestNo(outRequestNo); request.setPaymentOrderAmount(new BigDecimal(String.valueOf(amount)).setScale(2, RoundingMode.HALF_UP)); request.setBuyerId(Long.valueOf(String.valueOf(buyerId))); request.setOpenId(openId); request.setPayEndTime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(System.currentTimeMillis() + 30L * 60L * 1000L))); request.setCallbackUrl(buildCallbackUrl()); return CbyzxPayUtil.createPayOrder(request); } public String buildCallbackUrl() { return callbackPath + CALLBACK_PATH; } public boolean isCreateSuccess(JSONObject response) { if (response == null) { return false; } Object code = findValue(response, names("code")); if (code != null && "0".equals(String.valueOf(code))) { return true; } Object success = findValue(response, names("success")); return success != null && Boolean.parseBoolean(String.valueOf(success)); } /** * 解析创博优表单回调参数。 * out_trade_no 为平台订单号,trade_no 为三方支付流水号。 */ public JSONObject parseCallbackRequest(HttpServletRequest request) { if (request == null || request.getParameterMap() == null || request.getParameterMap().isEmpty()) { return null; } JSONObject payload = new JSONObject(); for (String key : request.getParameterMap().keySet()) { String[] values = request.getParameterValues(key); if (values == null || values.length == 0) { continue; } payload.put(key, values.length == 1 ? values[0] : Arrays.asList(values)); } return payload; } /** * trade_status=TRADE_SUCCESS 视为支付成功。 */ public boolean isPaySuccess(JSONObject payload) { if (payload == null) { return false; } Object status = findValue(payload, names("trade_status", "tradeStatus", "payStatus", "paymentStatus", "status", "result", "resultCode", "state")); if (status != null) { String normalized = String.valueOf(status).trim().toUpperCase(); if (SUCCESS_STATUS.contains(normalized)) { return true; } if (FAIL_STATUS.contains(normalized)) { return false; } } Object success = findValue(payload, names("success")); if (success != null) { return Boolean.parseBoolean(String.valueOf(success)); } Object code = findValue(payload, names("code")); return code != null && "0".equals(String.valueOf(code)) && StringUtils.hasText(extractOutRequestNo(payload)); } /** * out_trade_no 为平台侧订单号,格式为 orderId_orderType_random。 */ public String extractOutRequestNo(JSONObject payload) { return valueToString(findValue(payload, names("outRequestNo", "out_trade_no"))); } /** * 回查本地支付记录时优先使用 out_trade_no。 */ public String extractRecordCode(JSONObject payload) { String code = valueToString(findValue(payload, names("out_trade_no", "outRequestNo", "merchantSerialNo", "merchant_serial_no"))); return StringUtils.hasText(code) ? code : null; } /** * trade_no 为创博优侧交易流水号,用于标记实际支付成功流水。 */ public String extractThirdPartyOrderNo(JSONObject payload) { String orderNo = valueToString(findValue(payload, names( "trade_no", "tradeNo", "merchantSerialNo", "merchant_serial_no", "paymentOrderId", "payment_order_id", "transactionId", "transaction_id", "tradeNo", "trade_no" ))); return StringUtils.hasText(orderNo) ? orderNo : null; } /** * notify_type=directpay.status.sync 表示支付结果同步通知。 */ public String extractNotifyType(JSONObject payload) { String notifyType = valueToString(findValue(payload, names("notify_type", "notifyType"))); return StringUtils.hasText(notifyType) ? notifyType : null; } public String extractMessage(JSONObject payload) { String message = valueToString(findValue(payload, names("msg", "message", "errorMsg", "errorMessage", "trade_status", "notify_type"))); return StringUtils.hasText(message) ? message : "支付回调处理失败"; } private Set names(String... names) { return new HashSet<>(Arrays.asList(names)); } private Object findValue(Object current, Set names) { if (current == null) { return null; } if (current instanceof JSONObject) { JSONObject jsonObject = (JSONObject) current; for (String key : jsonObject.keySet()) { if (names.contains(key)) { return jsonObject.get(key); } } for (String key : jsonObject.keySet()) { Object found = findValue(jsonObject.get(key), names); if (found != null) { return found; } } return null; } if (current instanceof JSONArray) { JSONArray jsonArray = (JSONArray) current; for (Object item : jsonArray) { Object found = findValue(item, names); if (found != null) { return found; } } } return null; } private String valueToString(Object value) { return value == null ? null : String.valueOf(value); } }