MaxPay 支付接口文档
本文档提供了MaxPay支付平台所有接口的详细说明和使用方法,包括代收、代付、订单查询及回调处理等核心功能。
5. 代收订单回调
POST
application/json
注意: 商户接收到系统的异步回调请求后,如成功处理订单,请在接口响应纯文本字符串 'success'。
回调参数
| 参数名 | 类型 | 描述 |
|---|---|---|
tradeno |
string | 订单号 |
outtradeno |
string | 商户订单号 |
amount |
string | 订单金额(回调时默认会保留3位小数,加签时请将小数位一起加入,例如:100.000) |
ramount |
string | 实际收款金额(回调时默认会保留3位小数,加签时请将小数位一起加入,例如:100.000) |
oamount |
string | 原始下单金额(回调时默认会保留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();
Map signMap = 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);
}
}