Z Zise Developers

沙盒 · 预置某家上游的返回行为

POST /v1/sandbox/upstream-behavior scope: merchant:read
商户自身 x-idempotency-key

设过之后,该上游动钱的那一跳会按你选的那一档返回,

我方随即照生产上逐字相同的分类、重试、留痕与状态机去处置它。

所以你在沙盒里看到的订单状态与 Webhook,就是同一件事在生产上的样子。

五档:success / reject / timeout / unknown / unknown_status

  • timeout —— 上游一个字节都没回;
  • unknown —— 上游回了 5xx / 幂等中间件故障;
  • reject —— 上游明确拒绝这一笔(订单判死、冻结释放);
  • unknown_status —— 上游回了个我方枚举里没有的状态

(哨兵值 SANDBOX_UNKNOWN_STATUS),订单挂起并触发我方告警。

timeoutunknown 落到同一类处置,这不是重复。 两者在你的日志里

长得完全不同,而处置必须相同:都属于结果不明(钱可能已经付出去了),

都不许当成失败去退款/解冻 —— 判错一次就是「既退款又付款」。

分成两档正是为了让你把这一点验证到。

只有动钱的那一跳被替换。 同一家上游的报价 / 解码 / 查询等只读调用

照常打真实上游。这是有意的:扫码付一次支付里 createpay(报价)在前、

paynotify(动钱)在后 —— 全部替换的话订单会在锁钱之前就干净地失败,

而你真正要测的那一支永远走不到。

注入 1 小时后自动回到 successexpires_in: 3600)—— 一个忘了

复位的注入会让你下一次联调拿到一堆莫名其妙的失败。

注入按 (商户, 上游) 隔离,两个沙盒商户之间互不影响;

live 上这个端点是 404

字段类型必填说明
x-idempotency-key string 必填 UUID v4

请求体

字段类型必填说明
upstream "remittance" | "card" | "qrpay" | "chain" 必填 哪一条业务线。大小写不敏感;认不出回 invalid_requestremittance 汇款 · card 发卡 · qrpay 扫码付 · chain 链上存提。
behavior "success" | "reject" | "timeout" | "unknown" | "unknown_status" 必填 预置成什么。unknown = 结果不明(不是失败); unknown_status = 上游回了一个我方枚举里没有的状态。

响应

200已注入
{
  "ok": true,
  "upstream": "qrpay",
  "behavior": "unknown",
  "expires_in": 3600
}
400invalid_request body 非 JSON / upstream 或 behavior 不在枚举内
404不在沙盒环境
请求
curl -X POST 'https://api.zise.com/v1/sandbox/upstream-behavior' \
  -H 'x-auth-token: Bearer $TOKEN' \
  -H 'x-idempotency-key: $IDEMPOTENCY_KEY' \
  -H 'content-type: application/json' \
  -d '{
    "upstream": "qrpay",
    "behavior": "unknown"
  }'
const res = await fetch("https://api.zise.com/v1/sandbox/upstream-behavior", {
  method: "POST",
  headers: {
    "x-auth-token": "Bearer $TOKEN",
    "x-idempotency-key": "$IDEMPOTENCY_KEY",
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "upstream": "qrpay",
    "behavior": "unknown"
  }),
});
// 金额按字符串读,别让它变成 number
const data = await res.json();
import requests

res = requests.post(
    "https://api.zise.com/v1/sandbox/upstream-behavior",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-idempotency-key": "$IDEMPOTENCY_KEY",
        "content-type": "application/json",
    },
    json={
      "upstream": "qrpay",
      "behavior": "unknown"
    },
)
# 金额用 Decimal(str(...)),不要 float
data = res.json()
req, _ := http.NewRequest("POST", "https://api.zise.com/v1/sandbox/upstream-behavior",
    strings.NewReader(`{
  "upstream": "qrpay",
  "behavior": "unknown"
}`))
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-idempotency-key", "$IDEMPOTENCY_KEY")
req.Header.Set("content-type", "application/json")
res, err := http.DefaultClient.Do(req)
// 金额字段用 string 接,不要 float64
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.zise.com/v1/sandbox/upstream-behavior"))
    .header("x-auth-token", "Bearer $TOKEN")
    .header("x-idempotency-key", "$IDEMPOTENCY_KEY")
    .header("content-type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
{
  "upstream": "qrpay",
  "behavior": "unknown"
}
"""))
    .build();
// 金额字段用 String / BigDecimal,不要 double
$ch = curl_init('https://api.zise.com/v1/sandbox/upstream-behavior');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'x-auth-token: Bearer $TOKEN',
    'x-idempotency-key: $IDEMPOTENCY_KEY',
    'content-type: application/json',
  ],
  CURLOPT_POSTFIELDS => <<<'JSON'
{
  "upstream": "qrpay",
  "behavior": "unknown"
}
JSON,
]);
$res = curl_exec($ch);
// 金额用 bcmath / 字符串,不要 floatval
200
{
  "ok": true,
  "upstream": "qrpay",
  "behavior": "unknown",
  "expires_in": 3600
}