测试模式
请在 api-testing.tongkhoesim.com 使用合作伙伴 tke_test_ 或会员 tke_member_test_ 密钥。虚拟余额和模拟 eSIM 与真实资金、库存和发票隔离。可用操作取决于密钥所有者;会员请查看批量订单、Webhook 和退款审核限制。
管理员按会员授予 OpenAPI 权限。在主侧栏打开 API 密钥,选择正式或测试模式后创建密钥;私密密钥仅显示一次。两种模式共用密钥数量限制。关闭权限将阻止两种模式,但仍可撤销密钥。
正式模式使用会员真实 VND 钱包,无信用额度。测试使用独立虚拟钱包和模拟 eSIM,可在测试页添加余额、创建或重置示例。测试订单不会出现在真实购买历史中。
两种模式均不支持会员批量订单和 Webhook。退款审核申请仅支持正式模式;取消测试 eSIM 可模拟退回虚拟钱包。测试不会调用真实供应商或开具真实 MISA 发票。不支持的操作返回 capability.not_supported。
会员 API 端点按密钥区分两种主机
请使用密钥对应主机,主机错误会在业务处理前被拒绝。
| 主机 | 基础 URL | 接受的密钥 |
|---|---|---|
| 正式 | https://api.tongkhoesim.com/v1 | tke_live_ / tke_member_live_ |
| 测试 | https://api-testing.tongkhoesim.com/v1 | tke_test_ / tke_member_test_ |
测试密钥调用正式主机或反之,返回 403 partner.wrong_host 并提示正确主机,日志不会泄露密钥。
创建测试密钥
在控制台测试模式创建 tke_test_,保存仅显示一次的密钥。首个测试密钥会自动调用 POST /v1/testmode/seed。
正式与测试的 7 项差异
| 项目 | 正式(api、tke_live_) | 测试(api-testing、tke_test_) |
|---|---|---|
| 主机 | api.tongkhoesim.com | api-testing.tongkhoesim.com,主机错误返回 403 |
| 钱包 | 组织真实钱包 | 独立虚拟钱包,可通过控制台或管理员调整无限充值 |
| 库存 | 真实 esim_profiles | 虚拟库存,无限生成模拟 eSIM |
| 发放 | 真实上游运营商 | 确定性模拟:相同输入生成相同 ICCID/LPA(LPA:1$testmode-smdp.tongkhoesim.com$…) |
| 流量使用 | 从运营商读取真实用量 | 每隔几分钟自动增加,75/90% 阈值通知 |
| Webhook | 真实正式事件 | 测试事件,可手动触发与 fail-next |
| 账本 | 真实财务账本 | 隔离虚拟账本,只追加不删除,绝不混入真实账本 |
/v1/testmode/seed
POST /v1/testmode/seed 创建可立即测试的数据集。按组织幂等,重复调用返回 already_seeded: true,不生成重复数据。
- 余额充足的测试钱包
- 按组织价格创建 3 个真实套餐订单,各含 1 个 eSIM
- 三种状态:未安装、已用 40%、已过期
#!/usr/bin/env bash
# Requires: bash + openssl + uuidgen.
KEY_ID="tke_test_REPLACE_WITH_YOUR_KEY_ID"
SECRET="REPLACE_WITH_YOUR_SECRET"
METHOD="POST"
REQ_PATH="/v1/testmode/seed"
BODY='{}'
TS="$(date +%s000)" # epoch milliseconds
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"
curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
--data-raw "$BODY"{
"ok": true,
"data": {
"seeded": true,
"already_seeded": false,
"orders_created": 3,
"esims_created": 3
}
}自动流量增长(drift)
已启用测试 eSIM 默认每 5 分钟增加用量,直到用完或到期,并在 75% 和 90% 触发 esim.usage_threshold。GET /v1/testmode/status 返回启用状态与间隔。
/v1/testmode/* 接口
仅在 api-testing.tongkhoesim.com 使用 tke_test_ 调用,不提供旧路径别名。
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /v1/testmode/status | 就绪状态、检查项、密钥/订单/eSIM 数量、钱包、Webhook 和 drift 状态 |
| POST | /v1/testmode/seed | 按组织幂等创建示例数据 |
| POST | /v1/testmode/reset | 软删除测试订单/eSIM,通过冲销分录归零钱包 |
| POST | /v1/testmode/esims/{order_item_id}/simulate | 模拟用量、状态、到期、限速及下次运营商拒绝 |
| POST | /v1/testmode/webhooks/fire | 立即向测试 Webhook 发送示例事件({event_type}) |
| POST | /v1/testmode/webhooks/fail-next | 强制后续 N 次投递返回 503,测试重试 |
| via | 触发时机 | ref_id 含义 |
|---|---|---|
open_api | 使用测试密钥直接调用 /v1 下单、充值、取消或模拟 | 调用密钥的 api_keys.id |
simulate | 成员或代理管理员通过控制台触发模拟 | 成员或管理员的用户 ID |
seed | 通过 /v1/testmode/seed 创建的数据 | 空 |
/v1/testmode/esims/{id}/simulate
test-mode-onlyPOST /v1/testmode/esims/{order_item_id}/simulate 可设置用量、状态、到期、带宽,或令下次充值/取消被模拟运营商拒绝一次。
#!/usr/bin/env bash
KEY_ID="tke_test_REPLACE_WITH_YOUR_KEY_ID"
SECRET="REPLACE_WITH_YOUR_SECRET"
ORDER_ITEM_ID="REPLACE_WITH_ORDER_ITEM_ID"
METHOD="POST"
REQ_PATH="/v1/testmode/esims/$ORDER_ITEM_ID/simulate"
BODY='{"data_used_mb":4096,"status":"enabled"}'
TS="$(date +%s000)"
NONCE="$(uuidgen)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')"
SIGNING="$METHOD"$'\n'"$REQ_PATH"$'\n'"$TS"$'\n'"$NONCE"$'\n'"$BODY_HASH"
SIG="$(printf '%s' "$SIGNING" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')"
curl -sS -X "$METHOD" "https://api-testing.tongkhoesim.com$REQ_PATH" \
-H "X-TKE-Key: $KEY_ID" \
-H "X-TKE-Timestamp: $TS" \
-H "X-TKE-Nonce: $NONCE" \
-H "X-TKE-Signature: $SIG" \
-H "Content-Type: application/json" \
--data-raw "$BODY"
# No Idempotency-Key required: state mutation is intrinsically idempotent.{
"ok": true,
"data": {
"order_item_id": "018f2b00-0000-7000-8000-000000000001",
"esim_state": "enabled",
"running_package_code": "TKE-VN-30D-X3K9F",
"running_started_at": "2026-07-11T07:00:00Z",
"running_expires_at": "2026-08-10T07:00:00Z",
"data_bytes": 5368709120,
"used_bytes": 4294967296,
"remaining_bytes": 1073741824,
"throttled": false,
"via": "simulate",
"last_via": "simulate",
"mode": "test",
"updated_at": "2026-07-11T09:15:00Z"
}
}测试模式错误码
| 错误码 | HTTP | 含义与解决方法 |
|---|---|---|
testmode.live_key_forbidden | 403 | 使用正式密钥调用 /v1/testmode/*,请改用同组织的 tke_test_。 |
testmode.invalid_scenario | 422 | 请求体为空、未提供字段,或状态不在 released/installed/enabled/disabled 中。 |
testmode.not_test_item | 422 | order_item_id 不属于测试订单,不可模拟修改正式数据。 |
testmode.no_sellable_package | 422 | 组织没有已定价套餐,密钥仍创建成功,仅示例生成失败。 |
partner.wrong_host | 403 | 密钥与主机不匹配,请切换到错误响应中指定的主机。 |
完整目录请见 错误码页面 。标准 /v1 认证流程在测试逻辑之前执行。
测试 Webhook:fire 与 fail-next
POST /v1/testmode/webhooks/fire 无需真实订单即可发送事件。请求体:{ "event_type": "order.completed", "data": {...} },省略 data 时自动生成示例。
POST /v1/testmode/webhooks/fail-next { "count": 1 } 强制后续 N 次投递 503,随后自动清除,无需关闭真实接收端。