Skip to Content
🔑 开发者平台通用商户收款

通用商户收款

外部系统已有自己的订单、订阅和交付时,可由商户服务端按金额创建 TDCloud 收款单,再让买家跳转收银台。无需预先创建 TDCloud 商品;TDCloud 确认收款和结算,商户系统自行更新业务订单与权益。

1. 准备商户服务端凭据

使用已绑定启用商户的机密客户端,申请并获批 merchant_payments.create、merchant_payments.read。现有应用不会自动增加权限,需联系平台调整。所有本页 OpenAPI 只接受 client_credentials 令牌,不接受用户授权令牌;调用方不提交买家编号或商户编号。

服务地址使用开发者中心分配的完整地址,见 接入概览。Secret 和 Access Token 仅保存在商户服务端。

curl -X POST "{TOKEN_ENDPOINT}" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=client_credentials" \ --data-urlencode "client_id={CLIENT_ID}" \ --data-urlencode "client_secret={CLIENT_SECRET}" \ --data-urlencode "scope=merchant_payments.create merchant_payments.read"

使用响应的 access_token 和实际 scope,按 expires_in 管理有效期。Client Credentials 不签发用户 Refresh Token,过期后用服务端凭据重新获取令牌。公共客户端或未绑定商户的客户端不能使用此流程。

2. 查询可用支付渠道

GET {API_BASE_URL}/openapi/v1/merchant-payment-channels

权限:merchant_payments.read。HTTP 200 返回 {"items": [...]};无可用渠道时数组为空。

curl "{API_BASE_URL}/openapi/v1/merchant-payment-channels" \ -H "Authorization: Bearer {ACCESS_TOKEN}"
字段类型说明
payment_nostring建单时使用的真实渠道编号,不是显示名称
namestring渠道名称
providerstring当前支持 stripe、dog-coin(TDC)
ownerstringplatform 或 merchant
currencystring渠道支付币种
login_requiredboolean渠道本身是否必须登录;商户禁止未登录消费时,收银台也会要求登录
test_modeboolean是否使用管理员分配的 Stripe 测试渠道

可用渠道受商户归属、启用状态和调试授权约束。首期支持 TDC 与已配置签名 Webhook 的 Stripe。TDC 始终要求买家登录 TDCloud;其他渠道是否允许未登录消费由商户设置。不要将本目录套用于商品订单的渠道发现或报价。

3. 创建收款单

POST {API_BASE_URL}/openapi/v1/merchant-payments

权限:merchant_payments.create。请求头:Authorization: Bearer {ACCESS_TOKEN}、Content-Type: application/json。

参数类型必填约束
external_referencestring是商户业务订单引用,去除首尾空白后非空,最长 191 字节;创建后固定
idempotency_keystring是同一次建单的重试键,最长 128 字节;同商户内唯一
amount_minorinteger是正整数,按支付币种精度表示金额;最大 9007199254740991,且须能在平台内部精度内表示
currencystring是已启用且渠道支持的币种,使用大写代码
payment_nostring否传入时限定为该渠道;省略或为空时由买家在收银台选择可用支付方式
descriptionstring否展示给买家的付款内容,最长 500 字节,去除首尾空白;不要放入内部编号、密钥或其他敏感信息
expires_ininteger否支付有效秒数,60~7200;默认 1800(30 分钟),建单后固定
notify_enabledboolean否省略时使用显式通知参数或商户默认配置;false 关闭本单通知,不能同时传通知地址/密钥;true 要求存在有效通知配置
notify_urlstring否服务端通知的公网 HTTPS 地址,须与 notify_secret 同时传入;见状态通知一节
notify_secretstring否独立随机签名密钥,32~256 字节,不含空白
return_urlstring否支付确认后返回的完整 HTTPS 地址,最长 2048 字节;规则见下一节

金额示例

此接口使用 amount_minor,与商品订单接口的八位定点金额不同。

付款金额currencyamount_minor返回的 currency_decimals
10.50 USDUSD10502
10.50 TDCTDC10500000008

使用整数或十进制库按币种精度换算,不使用浮点乘法四舍五入拼金额。创建成功后核对返回的金额、币种和精度,不用实时汇率改写原订单。

curl -X POST "{API_BASE_URL}/openapi/v1/merchant-payments" \ -H "Authorization: Bearer {ACCESS_TOKEN}" \ -H "Content-Type: application/json" \ --data '{"external_reference":"merchant-order-123","idempotency_key":"merchant-order-123-attempt-1","amount_minor":1050,"currency":"USD","description":"订单说明","expires_in":1800,"return_url":"https://merchant.example/orders/merchant-order-123?source=tdcloud#payment"}'

成功响应:HTTP 201,重复同参数请求也返回 201 和同一收款单。

{ "order_no": "MP_EXAMPLE", "external_reference": "merchant-order-123", "payment_no": "", "amount_minor": 1050, "currency": "USD", "currency_decimals": 2, "status": "pending", "test_mode": false, "checkout_url": "https://tdcloud.cc/checkout/merchant/MP_EXAMPLE?token=CHECKOUT_TOKEN", "return_url": "https://merchant.example/orders/merchant-order-123?source=tdcloud#payment", "expires_at": "2026-10-08T14:00:00+08:00" }

示例编号、地址、时间均为占位值。保存 order_no 与业务订单的映射,将实际返回的 checkout_url 交给买家浏览器;不能把它当作已支付凭证或在日志中暴露其中的令牌。expires_at 在建单时确定,发起渠道会话、刷新页面和重试均不会延长。

买家选择支付方式

默认建单不传 payment_no,TDCloud 保存当时可用的渠道及金额、汇率、手续费规则,返回一个收银台地址。买家在收银台选择付款方式;只有一种可用方式时直接显示该方式。商户希望限定单一渠道时仍可传入 payment_no,历史固定渠道订单沿用原方式。

买家只能看到当前商户可用且符合实际付款账号授权的方式。需要登录的方式会提示登录,未登录时不能使用 TDC 余额。TDC 可按建单时保存的汇率支付其他已启用币种的订单,页面显示实际应付 TDC;金额和订单币种保持不变。自有渠道手续费在建单时检查,发起付款时再次检查并预占。

首次发起渠道付款后锁定方式。再次点击或刷新继续使用同一支付尝试;渠道响应不明时不能切换方式另建会话。超时不会因选择方式或刷新而延期。尚未选择时,服务端 payment_no 为空、test_mode 为 false;选择后查单反映实际渠道和测试模式,不改变原建单参数的幂等校验。

买家收银台只展示商户名称、付款内容、金额、倒计时与支付方式。外部订单引用、TDCloud 对账单号、支付事件号、渠道交易号及钱包结算信息保留在商户服务端查单和通知接口。收银台使用本单专属的方式标识,不能通过提交任意渠道编号越权付款。

幂等与重试

同一商户、同一 idempotency_key、相同规范化参数返回同一单。金额、币种、渠道、外部引用、描述、有效期或返回地址变化返回 HTTP 409 payment_conflict。不同商户的幂等空间隔离;external_reference 本身不是去重键。

超时后以原键和原参数重试,不能仅因没有收到响应就换键再建一张可支付单。确需新支付尝试时,先核对原单和渠道结果,避免两张单均可付款。

支付超时

  • expires_in 是从建单成功开始计算的有效秒数。省略默认 30 分钟,也可传入 60~7200 秒(1 分钟~2 小时)。显式 1800 与省略等价;同键改为其他有效期返回 409 payment_conflict。
  • 到达固定 expires_at 后禁止继续发起支付,收银台发起接口返回 410 payment_expired。同键重试仍返回原单及原期限,不能续期。新上限用于新建订单,历史订单保留原期限。服务端查询及对账仍可用。
  • 尚未发起渠道会话的单在查询、列表、收银台读取或后台巡检时转为 expired。已发起会话的单先进入 review,后台核对付款并关闭尚未支付的 Stripe 会话;确认渠道已过期后才释放预占手续费和授信,且只释放一次。
  • 异步付款未决、渠道查单/关单失败或会话创建响应不明继续保留 review,不能直接认定失败或重复扣费。真实付款确认后,即使回调晚到,也会在钱包及账务完成后返回 paid;无法获取会话号的异常需管理员按 TDCloud 单号到 Stripe 核对。
  • 后台每分钟巡检,并在渠道异常时重试。Stripe 会话自身的最短期限为 30 分钟,可能晚于收款单期限;TDCloud 会主动关单,不会用渠道期限延长收款单。不能将本地到期理解为渠道地址必定在同一瞬间关闭。Stripe 会话到期规则 、主动关闭会话 。

收银台显示倒计时,到期后移除支付入口并提示核对结果。商户遇到 review 或已付款异常时,应继续查询原单、保留稳定单号供人工对账;确认原支付尝试结束后再创建新尝试。

4. 接入支付返回地址

return_url 由商户服务端创建收款单时设置,随后固定。首尾空白会被去除;必须是完整 HTTPS URL,不允许账号密码、反斜线或未编码空白/控制字符。未传、空字符串或历史单没有此字段时,买家留在原收银台流程。

此参数与 OAuth 的 redirect_uri 不同:无需登记为 OAuth 回调,可以按业务订单设置路径、查询参数和 # 片段。浏览器自行在收银台 URL 上添加 return_url 或 status=paid 不会覆盖保存地址或确认支付。

支付流程如下:

  1. 买家进入 TDCloud checkout_url,按商户策略登录或匿名付款。
  2. Stripe 完成或取消付款先回 TDCloud 收银台;TDC 支付也在收银台确认。
  3. 收银台向服务端查单。只有返回 paid 后才自动进入保存的 return_url,并保留“返回商户”入口。
  4. 异步付款未决时继续查单;取消支付、到期或退款/拒付不触发成功返回。浏览器取消不保证收款单立即变为终态。
  5. 商户返回页调用自己的后端,再由后端查 TDCloud 收款单,核对金额、币种和稳定 event_id,幂等更新业务订单。

原地址的路径、查询参数和片段保留;TDCloud 不自动添加 Token、单号或支付状态。商户应在原地址中携带自己的业务引用,并将回跳仅作为触发查单的入口。浏览器付款成功页、回跳参数或渠道会话创建成功都不能作为交付依据。

5. 服务端状态通知

状态通知是可选能力。默认不开启,可通过服务端查单确认结果。浏览器返回地址和服务端通知互相独立,均不必填写。

商户默认配置

在 商户中心 → 支付与结算 → 收款状态通知 配置公网 HTTPS 通知地址。保存地址后平台生成独立签名密钥,仅本次显示;将其保存到商户接收服务,完成验签后再启用默认通知。读取设置不会返回密钥,遗失时可更换。

建单方式通知行为
不传通知参数商户默认通知启用时使用默认地址与密钥,否则不发送
notify_enabled: false关闭本单通知,即使商户已启用默认通知;不要同时传地址或密钥
notify_enabled: true使用显式地址和密钥,否则使用已启用的商户默认配置;均不可用则拒绝建单
成对传 notify_url / notify_secret覆盖商户默认配置,使用本次提供的地址和密钥

生效地址与密钥在建单时固定。更改、关闭默认配置或更换密钥只影响新订单,历史订单及补发继续使用原配置;接收服务需保留旧密钥直到历史通知处理完成。相同幂等键重试不会重新读取默认配置或改变已创建订单。

单笔指定通知

建单时同时传入 notify_url 和 notify_secret,TDCloud 会向商户服务端发送 HTTPS POST。notify_url 仅允许公网 HTTPS,最长 2048 字节,不允许账号密码、片段、内网地址或重定向。notify_secret 使用商户生成的独立随机密钥,32~256 字节,不含空白;不要使用 OAuth Client Secret。地址和密钥参与建单幂等校验,创建后不能修改。

{ "external_reference": "merchant-order-123", "idempotency_key": "merchant-order-123-attempt-1", "amount_minor": 1050, "currency": "USD", "payment_no": "CHANNEL_NO", "notify_url": "https://merchant.example/hooks/tdcloud", "notify_secret": "MERCHANT_GENERATED_RANDOM_SECRET_AT_LEAST_32_BYTES" }

商户默认通知未启用时,两个参数均省略不发送通知;已启用默认通知时,使用 notify_enabled: false 关闭本单通知。密钥不会出现在建单、查单、收银台或投递记录响应中。

通知内容与验签

每次状态变化发送 merchant_payment.status_changed,包含创建后的 pending、支付确认后的 paid、review、expired、refunded 和 chargeback。支付和账务确认、订单状态与通知事件在同一事务中保存;投递失败不撤销已确认支付。

{ "id": "MN_NOTIFICATION_EXAMPLE", "type": "merchant_payment.status_changed", "api_version": 1, "created_at": "2026-10-08T07:45:00Z", "data": { "order_no": "MP_EXAMPLE", "external_reference": "merchant-order-123", "payment_no": "CHANNEL_NO", "amount_minor": 1050, "currency": "USD", "currency_decimals": 2, "status": "paid", "status_version": 2, "test_mode": false, "expires_at": "2026-10-08T08:00:00Z", "event_id": "MPE_PAYMENT_EXAMPLE", "provider_trade_no": "cs_EXAMPLE", "occurred_at": "2026-10-08T07:45:00Z" } }

data 是该状态发生时的收款单快照,不含收银台 Token、返回地址或通知密钥。id 是通知事件号;data.event_id 是确认支付后的稳定支付事件号,两者用途不同。created_at 是通知生成时间,支付时间使用 data.occurred_at。

请求头含义
X-TDCloud-Event-ID与请求体 id 一致的通知事件号
X-TDCloud-Timestamp本次投递的 Unix 秒时间戳
X-TDCloud-Signaturesha256= 加 HMAC-SHA256 的小写十六进制结果

签名内容为 时间戳 + "." + 原始 HTTP 请求体字节,密钥为该单创建时生效的签名密钥(商户默认密钥或显式 notify_secret)。使用常量时间比较签名,并拒绝与当前时间相差超过 5 分钟的时间戳;服务端时钟须同步。验签前不要重新序列化 JSON。例如 Python:

import hashlib import hmac import time # raw_body is the untouched HTTP request body; secret is stored server-side. timestamp = request.headers["X-TDCloud-Timestamp"] provided = request.headers["X-TDCloud-Signature"] expected = "sha256=" + hmac.new( secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256 ).hexdigest() if abs(time.time() - int(timestamp)) > 300 or not hmac.compare_digest(expected, provided): raise ValueError("Invalid TDCloud notification")

验签后核对订单引用、金额、币种和 test_mode,按通知 id 去重。通知可能重复或乱序;同单 status_version 单调递增,低版本不能覆盖高版本。查单也返回 status_version。支付记账继续按 data.event_id 幂等;只有 paid 表示确认支付,review 和 expired 不能作为已支付凭证。退款/拒付保留原支付事件号。

确认、重试与补发

商户应先可靠保存通知,再返回 HTTP 2xx;响应体不参与确认。单次投递最多等待 8 秒。超时、连接失败、3xx、4xx 和 5xx 均重试,不跟随重定向。首次异步投递后,失败间隔依次为 1 分钟、5 分钟、15 分钟、30 分钟、1 小时、2 小时、4 小时、6 小时、8 小时,共最多 10 次自动尝试。工作进程中断后可恢复;通知事件号与请求体保持一致,每次重签时间戳。

商户也可在 商户中心 → 支付与结算 → 收款通知记录 输入收款单号查看投递结果和最近明细,自动重试耗尽后点击“补发通知”。页面仅查询当前商户的订单。

方法与路径权限用途
GET /openapi/v1/merchant-payments/{order_no}/notificationsmerchant_payments.read本商户订单的投递记录,HTTP 200 返回 {"items": [...]}
POST /openapi/v1/merchant-payments/{order_no}/notifications/{event_id}/retrymerchant_payments.create将自动重试耗尽的通知重新排队,HTTP 202 返回 {"accepted": true}

记录按状态版本倒序返回,limit 默认 20,允许 1~100,越界回退 20。字段包括通知 event_id、status_version、event 快照、delivery_status(pending / delivered / failed)、累计 attempts、next_attempt_at、last_http_status、last_error 和最近 20 条 delivery_attempts。尝试记录没有 finished_at 表示进程中断或仍在投递。补发不生成新事件、不改订单状态;已送达或仍待投递的事件重复请求不会额外排队。重试接口按客户端每分钟最多 30 次。

通知与查单并用:未收到通知时查询原单并定期对账;通知投递耗尽不代表支付失败。

6. 服务端查单与对账

方法与路径权限用途
GET /openapi/v1/merchant-payments/{order_no}merchant_payments.read查询本商户一单,HTTP 200 返回收款单对象
GET /openapi/v1/merchant-paymentsmerchant_payments.read按本商户外部引用或 TDCloud 单号过滤,HTTP 200 返回 {"items": [...]}

列表必须提供至少一个 external_reference 或 order_no;两者同时提供时共同匹配。limit 默认 20,范围 1–100,非法或越界回退 20。按最新记录优先返回,不提供页码、总数或完整商户订单分页。

curl "{API_BASE_URL}/openapi/v1/merchant-payments/{ORDER_NO}" \ -H "Authorization: Bearer {ACCESS_TOKEN}" curl --get "{API_BASE_URL}/openapi/v1/merchant-payments" \ -H "Authorization: Bearer {ACCESS_TOKEN}" \ --data-urlencode "external_reference=merchant-order-123" \ --data-urlencode "limit=20"

单笔结果包含建单返回的字段;下列字段用于确认支付:

字段类型说明
statusstring收款单状态,见下表
status_versioninteger同单状态版本,防止旧通知覆盖新状态;历史未变更订单可为 0
event_idstring,可省略确认支付后的稳定、唯一支付事件号;用于商户幂等记账
provider_trade_nostring,可省略渠道交易号;TDC 为 TDC:{order_no}
occurred_atstring 或 null确认支付时间,RFC 3339,保留响应中的时区
settlement_tdcstring,可省略平台渠道锁定的结算 TDC,按八位定点整数表示;非支付凭证
return_urlstring,可省略已保存的商户返回地址

已支付响应示例(仅展示核对字段):

{ "order_no": "MP_EXAMPLE", "external_reference": "merchant-order-123", "payment_no": "CHANNEL_NO", "amount_minor": 1050, "currency": "USD", "currency_decimals": 2, "status": "paid", "event_id": "MPE_EXAMPLE", "provider_trade_no": "pi_EXAMPLE", "occurred_at": "2026-10-08T13:45:00+08:00" }

expires_at 同样为 RFC 3339 时间,不是 Unix 秒。单号、事件号和渠道交易号按字符串保存。服务端状态通知可触发商户处理,但仍应保留有间隔、有超时上限的查单和定期对账。

状态含义与处理
pending未决,继续核对;不能交付,也不能仅凭本地计时判失败
paid收款与商户账务处理已确认;核对订单引用、金额和币种后按 event_id 只记一次
review支付期限已到但渠道结果未决或核对失败;创建响应不明时需平台人工核对,不要换键重复收款
expired已核对无付款的到期状态;晚到的真实收款仍可能被核验并转为 paid,应继续异常对账
refunded已确认退款,保留原支付事件;商户按自己的退款与权益规则处理
chargeback已确认拒付,保留原支付事件;商户处理争议和业务状态

不要套用商品订单接口的数字 status。当前未定义通用 failed、cancelled 或公开取消接口;未来未知状态保留为待核对,不按支付成功处理。

7. 结算、手续费与调试

平台渠道按建单汇率转为 TDC 记入商户钱包;平台按商户配置 0–30 天冻结,0 天可实时使用。商户自有渠道由商户自行收款,TDCloud 不重复入账;平台手续费在建单时检查资金,在获取支付链接前再次检查并预占,优先使用可用 TDC,再使用已获批授信。无法筹足则拒绝交易。

投诉先阻止该单冻结销售款释放,核实损失后依次使用该单冻结款、担保金、可用余额及待追回余额处理。不可返还的手续费可能导致损失超出冻结款。退款、拒付和投诉处置由平台核验,不能用修改商户业务订单代替平台账务动作;本页没有商户自助退款/提现 OpenAPI。更多操作见 支付与结算。

test_mode: true 仅表示管理员指定分配的 Stripe 测试渠道。消费者不发生真实 Stripe 扣款,但 TDCloud 正常订单、账务、钱包 TDC 和冻结流程仍执行。测试权限不由管理员身份自动获得,也不代表可随意混用生产业务;测试账号、商品和交付由管理员维护。

调试配置只填写商户时,指定商户的所有买家可使用所选测试渠道;只填写付款用户时,这些用户可在所有商户下使用;同时填写时,商户与付款用户条件须同时满足。两项均为空不授予使用权,多个编号在各自列表内匹配任意一个。

服务端渠道目录和建单没有买家身份,先按商户范围检查。存在付款用户限制的测试渠道会在目录中返回 login_required: true;建单成功不代表任意买家均可付款。收银台按实际 TDCloud 登录用户检查,生成或再次获取渠道支付链接前再次检查;请求中的自报买家编号不能授予调试权限。付款用户限制要求登录,即使商户已允许未登录消费。已发出的渠道会话仍可能在撤销调试权限后成功,已验证收款继续正常入账。

8. 错误、限流与接入检查

错误至少包含字符串 error。HTTP 202 的 provider_result_pending 也不是支付成功。按 HTTP 状态和错误码分支,不依赖中文文案。

HTTPerror处理
400invalid_notify_config开启单笔通知需提供地址和密钥或启用商户默认通知;关闭单笔通知不能同时传地址或密钥
400invalid_notify_url使用公网 HTTPS 通知地址,不能包含账号密码、片段或内网目标
400invalid_notify_secret通知地址须配独立随机密钥,32~256 字节,不含空白
503notification_unavailable稍后用原幂等键重试
400invalid_request核对参数、金额及列表过滤条件
400invalid_expires_in修正有效期为 60~7200 秒的整数;返回 error_description 和 field: "expires_in"
400invalid_return_url修正 HTTPS 地址;同时返回 error_description、field: "return_url"
401invalid_token重新获取有效 OAuth 令牌
403insufficient_scope、client_credentials_required核对批准权限及令牌主体
403merchant_inactive请平台恢复绑定商户
403login_required收银台要求买家登录
403payment_debug_access_denied核对调试渠道分配与启用状态
404payment_not_found核对单号及本商户归属,不推断其他商户资源
409payment_conflict原幂等键参数冲突;收银台支付状态冲突也可能使用此码
410payment_expired不能继续发起支付;查询原单并核对付款结果,避免重复下单
409provider_result_mismatch由平台核对渠道会话、金额和币种
422channel_unavailable、currency_rate_unavailable重新确认渠道、币种及平台汇率
422merchant_fee_funding_unavailable核对平台费率、可用钱包和授信额度
202provider_result_pending渠道结果未明,保留原单并核对
429rate_limited按 Retry-After 等待,避免持续重试
503rate_limit_unavailable限流服务暂不可用,有限退避重试
500server_error保存脱敏上下文并向平台反馈;写请求复用原幂等键

建单每客户端每分钟 60 次;查单、过滤列表与渠道目录共用每客户端每分钟 300 次。收银台按 IP 限速:查询 180 次、发起支付 30 次/分钟。

完成接入后,检查动态金额、支付返回页、同键重试及参数冲突、商户权限、未登录消费、支付超时和异步结果。确认业务订单只按稳定 event_id 更新一次,并按退款、拒付结果处理后续业务。Stripe Webhook 验签由 TDCloud 完成。

最后更新于