订单
createPayment 和 createPayout 是仅有的两个写调用。两者都接收 API Reference 请求体里的业务字段,加密签名后发送,返回创建的订单。各国家、各方式的字段全集在 API Reference:打开代收或代付,再选国家和方式。
请求结构
| 字段 | 必填 | 说明 |
|---|---|---|
merchantOrderNo | 是 | 你为这笔订单起的唯一号。唯一能防止重复下单的键,见幂等。 |
currency | 是 | ISO 4217,大写。 |
amount | 是 | decimal 字符串,大于零,整数部分最多 16 位、小数最多 2 位,如 "100.00"。 |
paymentMethod / payoutMethod | 是 | code 加最多一个以 code 的 lowerCamelCase 命名的 extra 对象:PIX → pix,PK_JAZZCASH → pkJazzcash。 |
webhookUrl | 是 | 接收终态的绝对 https:// 地址。 |
country | 否 | ISO 3166 两位码。仅币种有歧义时需要。 |
returnUrl | 仅代收 | 托管收银台把付款人送回的地址。 |
attach | 否 | 商户透传数据,查询和 webhook 原样返回。 |
创建代收
- Go
- JavaScript
- Python
- PHP
- Java
order, err := c.CreatePayment(ctx, &deepayment.CreatePaymentReq{
MerchantOrderNo: "M202605060001",
Currency: deepayment.CurrencyBRL,
Amount: "250.00",
PaymentMethod: deepayment.PaymentMethod{
Code: deepayment.MethodCodePIX,
Pix: &deepayment.PaymentPixExtra{PayerCPF: "12345678901", PayerName: "Joao Silva"},
},
ReturnUrl: "https://merchant.example/return",
WebhookUrl: "https://merchant.example/webhook/payment",
Attach: "user_123",
})
if err != nil { /* 见错误一章 */ }
redirect(order.Action.Url) // 或渲染 order.Action.QrCode / order.Action.PayContent
Go 为每种方式的 extra 提供类型化结构体(PaymentPixExtra、PaymentPkWalletExtra……)。你的 SDK 版本还没有的方式,按名字设置 extra:
m := deepayment.PaymentMethod{Code: "NEW_METHOD"}
_ = m.SetExtra("newMethod", map[string]any{"customerName": "X", "bankCode": "001"})
const order = await client.createPayment({
merchantOrderNo: 'M202605060001',
currency: 'BRL',
amount: '250.00',
paymentMethod: { code: 'PIX', pix: { payerCPF: '12345678901', payerName: 'Joao Silva' } },
returnUrl: 'https://merchant.example/return',
webhookUrl: 'https://merchant.example/webhook/payment',
attach: 'user_123',
});
redirect(order.action?.url); // 或渲染 order.action?.qrCode / order.action?.payContent
from deepayment import CreatePaymentReq
order = client.create_payment(CreatePaymentReq(
merchantOrderNo="M202605060001",
currency="BRL",
amount="250.00",
paymentMethod={"code": "PIX", "pix": {"payerCPF": "12345678901", "payerName": "Joao Silva"}},
returnUrl="https://merchant.example/return",
webhookUrl="https://merchant.example/webhook/payment",
attach="user_123",
))
redirect(order.action.url) # 或渲染 order.action.qrCode / order.action.payContent
CreatePaymentReq、CreatePayoutReq 是 dataclass,paymentMethod / payoutMethod 是普通 dict。响应是字段名与 API 一致的 dataclass(order.orderNo、order.action.url)。
$order = $client->createPayment([
'merchantOrderNo' => 'M202605060001',
'currency' => 'BRL',
'amount' => '250.00',
'paymentMethod' => ['code' => 'PIX', 'pix' => ['payerCPF' => '12345678901', 'payerName' => 'Joao Silva']],
'returnUrl' => 'https://merchant.example/return',
'webhookUrl' => 'https://merchant.example/webhook/payment',
'attach' => 'user_123',
]);
redirect($order['action']['url'] ?? ''); // 或渲染 $order['action']['qrCode'] / ['payContent']
请求和响应都是键名与 API 一致的关联数组。
Map<String, Object> order = client.createPayment(Map.of(
"merchantOrderNo", "M202605060001",
"currency", "BRL",
"amount", "250.00",
"paymentMethod", Map.of("code", "PIX",
"pix", Map.of("payerCPF", "12345678901", "payerName", "Joao Silva")),
"returnUrl", "https://merchant.example/return",
"webhookUrl", "https://merchant.example/webhook/payment",
"attach", "user_123"));
Map<String, Object> action = (Map<String, Object>) order.get("action");
redirect((String) action.get("url")); // 或渲染 action.get("qrCode") / action.get("payContent")
请求和响应都是键名与 API 一致的 Map<String, Object>。
创建代付
调用形态相同,换成 createPayout 和 payoutMethod 对象。代付 extra 承载收款账户;网关对该币种要求的每个字段都会在发送前本地检查。
- Go
- JavaScript
- Python
- PHP
- Java
order, err := c.CreatePayout(ctx, &deepayment.CreatePayoutReq{
MerchantOrderNo: "P202605060001",
Currency: deepayment.CurrencyBRL,
Amount: "1000.00",
PayoutMethod: deepayment.PayoutMethod{
Code: deepayment.MethodCodePIX,
Pix: &deepayment.PayoutPixExtra{KeyType: "CPF", Key: "12345678901"},
},
WebhookUrl: "https://merchant.example/webhook/payout",
})
const order = await client.createPayout({
merchantOrderNo: 'P202605060001',
currency: 'BRL',
amount: '1000.00',
payoutMethod: { code: 'PIX', pix: { keyType: 'CPF', key: '12345678901' } },
webhookUrl: 'https://merchant.example/webhook/payout',
});
order = client.create_payout(CreatePayoutReq(
merchantOrderNo="P202605060001",
currency="BRL",
amount="1000.00",
payoutMethod={"code": "PIX", "pix": {"keyType": "CPF", "key": "12345678901"}},
webhookUrl="https://merchant.example/webhook/payout",
))
$order = $client->createPayout([
'merchantOrderNo' => 'P202605060001',
'currency' => 'BRL',
'amount' => '1000.00',
'payoutMethod' => ['code' => 'PIX', 'pix' => ['keyType' => 'CPF', 'key' => '12345678901']],
'webhookUrl' => 'https://merchant.example/webhook/payout',
]);
Map<String, Object> order = client.createPayout(Map.of(
"merchantOrderNo", "P202605060001",
"currency", "BRL",
"amount", "1000.00",
"payoutMethod", Map.of("code", "PIX", "pix", Map.of("keyType", "CPF", "key", "12345678901")),
"webhookUrl", "https://merchant.example/webhook/payout"));
代付会动资金。上线前先读错误:代付遇到传输错误绝不能当失败处理。
订单对象
两个创建调用和查询调用返回同一种对象。
| 字段 | 含义 |
|---|---|
orderNo | 平台订单号,查询和回单用它。 |
merchantOrderNo | 你的单号,原样返回。 |
status | PENDING、PROCESSING、SUCCEEDED、FAILED、EXPIRED、CANCELED。见订单状态。 |
amount、paidAmount | decimal 字符串。paidAmount 是实际结算金额;区间金额的方式可能与 amount 不同。 |
paymentMethod / payoutMethod | 方式码字符串。 |
action.url | 把付款人送去的托管收银台或渠道页面。 |
action.payContent、action.qrCode | 自建收银台要渲染的内容,格式见各方式页。 |
failure | status 为 FAILED 时出现:code、msg、message。按 msg 分支。 |
attach | 原样返回。 |
createdAt、updatedAt | Unix 毫秒。 |
本地校验
签名前 SDK 会检查请求,任一项不通过就报请求错误(Go:errors.Is(err, ErrInvalidRequest)),什么都不会发出。
| 检查 | 规则 |
|---|---|
| 必填字段 | merchantOrderNo、currency、amount、webhookUrl 非空白。 |
amount | 字符串,匹配 ^(0|[1-9][0-9]{0,15})(\.[0-9]{1,2})?$ 且大于零。JSON number 被拒绝。 |
webhookUrl | 以 https:// 开头。 |
| 方式结构 | code 非空白;最多一个 extra 对象;带的那个必须与 code 对应;该币种若有方式码白名单,code 必须在内。 |
| extra 必填 | 网关对该币种要求的字段,加上个别方式码额外要求的(如 INR 代付只有 IN_IFSC 需要 ifsc 和 account)。 |
格式刻意不查:手机号位数、邮箱、IFSC、证件号、银行代码由网关校验并以 INVALID_FIELD 返回。SDK 不认识的币种不拦,交给网关判定。
幂等与重试
merchantOrderNo 是唯一能防止重复下单的键。 用同一个号再次创建绝不会产生第二笔订单:平台返回原单,或在识别到号已被占用但无法返回原单时回 IDEMPOTENCY_CONFLICT。
由此两条规则:
- 结果未知(超时、传输错误、非 envelope 响应)之后,绝不分配新的
merchantOrderNo。查询原号,或用原号原样重发。 - 重发必须参数完全一致。平台返回原单时不比较字段,改了金额或账户会被静默忽略。要改任何东西,用新的
merchantOrderNo,并先把原单对账清楚。
SDK 不自动重试。每次调用重新生成 nonce 和签名,所以再调一次方法是合法的重试,重放抓到的字节不是。
创建调用可以附一个幂等键,放在 Idempotency-Key header 里用于追踪和关联;它不是去重键。
- Go
- JavaScript
- Python
- PHP
- Java
order, err := c.CreatePayment(ctx, req, deepayment.WithIdempotencyKey(key)) // key:UUID v4
await client.createPayment(req, { idempotencyKey: key });
client.create_payment(req, idempotency_key=key)
$client->createPayment($req, $key);
client.createPayment(req, key);
不传时 SDK 自动生成。传入的键必须是 UUID v4。
实际付款人信息
签名鉴权的 GET /api/v1/payments(按平台订单号或商户订单号)和代收 Webhook 均可返回:
{"payer":{"name":"Maria Silva","documentNumber":"01234567890"}}
付款人来自渠道确认的实际付款信息,不使用商户下单填写的姓名或 CPF 替代。渠道未提供的字段省略,两个字段均缺失时省略整个 payer。证件号保持字符串及前导零,不推断证件类型。
代收查单要求商户签名鉴权及订单归属校验,其他商户即使知道订单号也不可查询。代收 Webhook 将相同的可选对象发送到订单的回调地址,商户须验证平台签名后使用。创建订单、公共收银台、普通代付查单和代付 Webhook 不返回此对象。使用 HTTPS 并正确校验证书;商户记录日志时应对姓名、证件号脱敏。查单响应和 Webhook 正文均使用 JSON,不额外进行正文加密。
Webhook 正文在通知事件首次生成时固定,重试保持不变。历史已生成通知不会自动补上付款人信息,可通过签名鉴权的代收查单获取已有数据。