MaxPay 支付接口文档
本文档提供了MaxPay支付平台所有接口的详细说明和使用方法,包括代收、代付、订单查询及回调处理等核心功能。
5. 代收订单回调
POST
application/json
注意: 商户接收到系统的异步回调请求后,如成功处理订单,请在接口响应纯文本字符串 'success'。
回调参数
参数名 | 类型 | 描述 |
---|---|---|
tradeno |
string | 订单号 |
outtradeno |
string | 商户订单号 |
amount |
number | 订单金额(回调时默认会保留3位小数,加签时请将小数位一起加入,例如:100.000) |
ramount |
number | 实际收款金额(回调时默认会保留3位小数,加签时请将小数位一起加入,例如:100.000) |
oamount |
number | 原始下单金额(回调时默认会保留3位小数,加签时请将小数位一起加入,例如:100.000) |
endtime |
string | 支付时间 |
status |
string | 订单状态:1已支付(支付成功时才会触发回调通知) |
remark |
string | 备注 |
sign |
string | 签名 |
Java 示例
Java 示例代码
import com.google.gson.*; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; public class CollectionExample { private static final String SECRET = "your_secret_key"; // 替换为真实密钥 // 处理回调 public static String handleCallback(String jsonData) { JsonObject params = JsonParser.parseString(jsonData).getAsJsonObject(); // 验证签名 if (!verifySign(params, SECRET)) { return "sign_error"; } // 提取字段 String outTradeNo = getAsString(params, "outtradeno"); String tradeNo = getAsString(params, "tradeno"); String status = getAsString(params, "status"); String amount = getAsString(params, "amount"); String ramount = getAsString(params, "ramount"); System.out.println("处理代收订单: " + outTradeNo); System.out.println("平台订单号: " + tradeNo); System.out.println("订单状态: " + status); System.out.println("金额: " + amount + ", 实收: " + ramount); return "success"; } private static boolean verifySign(JsonObject params, String secret) { String receivedSign = params.get("sign").getAsString(); MapsignMap = new HashMap<>(); for (Map.Entry entry : params.entrySet()) { String key = entry.getKey(); if (!"sign".equals(key) && !entry.getValue().isJsonNull()) { signMap.put(key, entry.getValue().getAsString()); } } String generatedSign = generateSign(signMap, secret); return generatedSign.equalsIgnoreCase(receivedSign); } private static String generateSign(Map params, String secret) { Map sorted = new TreeMap<>(); for (Map.Entry entry : params.entrySet()) { if (entry.getValue() != null && !entry.getValue().trim().isEmpty()) { sorted.put(entry.getKey(), entry.getValue().trim()); } } StringBuilder sb = new StringBuilder(); for (Map.Entry entry : sorted.entrySet()) { if (sb.length() > 0) sb.append("&"); sb.append(entry.getKey()).append("=") .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8).toLowerCase()); } sb.append("&secret=").append(secret); return md5(sb.toString()); } private static String md5(String input) { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] hash = md.digest(input.getBytes(StandardCharsets.UTF_8)); StringBuilder hex = new StringBuilder(); for (byte b : hash) { String h = Integer.toHexString(0xff & b); if (h.length() == 1) hex.append("0"); hex.append(h); } return hex.toString(); } catch (Exception e) { throw new RuntimeException("MD5计算失败", e); } } private static String getAsString(JsonObject obj, String key) { return obj.has(key) && !obj.get(key).isJsonNull() ? obj.get(key).getAsString() : ""; } // ✅ 测试方法 public static void main(String[] args) { Map mock = new HashMap<>(); mock.put("tradeno", "TRADE123456"); mock.put("outtradeno", "ORDER123456"); mock.put("amount", "100.000"); mock.put("ramount", "99.000"); mock.put("oamount", "100.000"); mock.put("endtime", "2024-07-21 12:30:00"); mock.put("status", "1"); mock.put("remark", "测试回调"); // ➕ 生成签名 String sign = generateSign(mock, SECRET); mock.put("sign", sign); // 构建 JSON JsonObject json = new JsonObject(); for (Map.Entry entry : mock.entrySet()) { json.addProperty(entry.getKey(), entry.getValue()); } String jsonStr = json.toString(); // 测试回调 String result = handleCallback(jsonStr); System.out.println("回调处理结果: " + result); } }