Source: https://cryptanio.com/zh/wiki/check-payment-api-without-webhook/
Language: zh

# 如何不使用 webhook，通过 API 检查付款

通过 API 在服务器端检查账单：付款状态、有限轮询、错误处理、订单仅履约一次以及重启后的恢复。

更新日期: 2026-11-14

无需 webhook，也可以从应用服务器检查付款：定期通过 Cryptanio API 读取账单，将其与订单核对，并在当前状态为 ` paid ` 时履行订单。这种定期查询称为轮询。它需要检查队列、错误处理以及防止重复履约的机制：仅有一次成功的 HTTP 响应并不能确认付款。

如果应用可以执行后台任务，但暂时不方便接收传入通知，可以采用这种方式。变化会在下一次读取时被发现；轮询不能提供即时通知。

## 需要什么 {#section-feb0880e2daf8027}

在自己的数据库中保存订单与[账单](<https://cryptanio.com/zh/wiki/shenme-shi-zhangdan/>)的关联：账单的 ` id `、项目、自定义的 ` reference `、商定价格以及运行模式。标识符应保存为字符串。要检查的是该订单对应的账单，而不是某笔碰巧金额相似的交易。

准备一个服务器端 API 密钥，具有 ` invoices ` 读取权限，以及 ` payments ` 服务和所需项目的访问权限。真实订单需要使用相应的正式运行模式。不要把密钥放在浏览器、买家链接或公开日志中。独立的核对进程只需要只读权限。

## 第1步：获取当前状态 {#section-d8c8f81c51a82e82}

此示例读取一张账单。请在服务器上设置 ` CRYPTANIO_API_KEY ` 和 ` CRYPTANIO_RESOURCE_ID `；后者是已保存的账单 ` id `，不是订单编号，也不是 [TXID](<https://cryptanio.com/zh/wiki/transaction-id-txid/>)。

```bash
#!/usr/bin/env bash
set -euo pipefail
: "${CRYPTANIO_RESOURCE_ID:?Set CRYPTANIO_RESOURCE_ID}"
: "${CRYPTANIO_API_KEY:?Set CRYPTANIO_API_KEY}"
urlencode() {
  local LC_ALL=C text="$1" char index
  for ((index=0; index<${#text}; index++)); do
    char="${text:index:1}"
    case "$char" in [a-zA-Z0-9.~_-]) printf '%s' "$char" ;; *) printf '%%%02X' "'$char" ;; esac
  done
}
response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT
transfer_status=0
http_status="$(curl \
  --silent --show-error --max-time 30 --request GET \
  --header "Authorization: Bearer $CRYPTANIO_API_KEY" \
  --header 'Accept: application/json' \
  "https://api.cryptanio.com/v1/invoices/$(urlencode "$CRYPTANIO_RESOURCE_ID")" \
  --output "$response_file" --write-out '%{http_code}')" || transfer_status=$?
if [[ "$http_status" == 2?? ]]; then
  cat "$response_file"
  exit "$transfer_status"
fi
printf 'HTTP %s\n' "$http_status" >&2
cat "$response_file" >&2
exit 1
```

[下载示例](</wiki/examples/ae4ad34d7f9814c6019ca3494a7e6c605b0f2488e3bd223a4a4ce34ae8c220da.sh>)

收到 HTTP 200 后，解析 JSON，并将 ` id `、` project_id `、` reference ` 和 ` sandbox ` 与自己的记录核对。真实订单应为 ` sandbox=false `。如果曾通过 ` invoice_currency ` 和 ` invoice_amount ` 保存价格，也请检查这两个字段。不属于该订单的账单，或来自其他模式的记录，都不应改变订单。

GET 返回的 ` checkout_url ` 链接用于查看。不要用它替换创建账单时从响应中保存的交互式链接：读取状态既不会创建新的付款，也不会签发新的交互式 checkout。

## 第2步：区分等待和付款完成 {#section-a8c2836253e85fca}

根据账单的 ` status ` 作出判断：

- ` pending `：正在等待付款；` seen `：已检测到转账，但付款尚未完成；
- ` partial `：部分金额已经确认，继续按照该付款的规则等待；
- ` underpaid `、` overpaid `、` late `：需要排查，并考虑 ` needs_decision ` 和卖家的决定；
- ` paid `：账单已记为付款，核实与订单的关联后，可以准备履约；
- ` expired `、` canceled `：结束买家当前的付款等待，但保留记录，供后续核对。

到期不代表不可能稍后到账。人工决定和交易回滚也可能改变后续处理。不要把某个状态认定为永久最终状态，也不要在页面关闭后删除订单与账单的关联。

已确认的交易与已付款的账单不是一回事。确认次数并不能证明所需资产、金额和收款信息恰好属于这个订单。具体区别见[订单仍未付款的检查指南](<https://cryptanio.com/zh/wiki/crypto-payment-order-unpaid/>)。

## 第3步：考虑金额和付款尝试 {#section-bdb25d930b5fb285}

更换付款方式时，同一张账单可能产生不同的付款尝试。对于已付款账单，请考虑 ` settled_attempt_id ` 和 ` settled_attempt `。排查历史记录时，检查 ` attempt_history.has_more ` 和后续游标；所选尝试的转账记录使用 ` payment_history `。第一页不一定包含完整历史。

` amount ` 是应发送的准确加密货币金额。` amount_raw ` 和 ` paid ` 是以资产最小单位表示的整数字符串，` decimals ` 指定小数位数。不要按浮点数进行比较，也不要像普通字符串一样按字典顺序比较。同时，您自己的金额检查不能代替账单状态和项目规则：多付款项可能被接受，也可能被提交审核。未经检查条件，也不应自行将不足额的付款相加。

## 第4步：限制轮询和重试 {#section-ba02a670ce7f4f6e}

将检查放入具有并发限制的服务器端队列。买家打开多个标签页不应产生多个独立的查询循环。请计算账户的整体请求量：不同 API 密钥和模式共享额度。当前限制见 [API 参考文档](<https://cryptanio.com/zh/docs.html#limits>)。

根据自己的流程设置等待时间，为其他请求保留余量。遇到网络错误或 HTTP 500\/503 时，使用有上限的重试，逐步增加等待时间，并加入随机抖动。遇到 HTTP 429 时降低整体请求量；如果收到 ` Retry-After `，按指定时间暂停。不要因为缺少该响应头就立即重试。每个请求都应有超时限制，每组重试也应有上限；达到上限后仍保留任务，以便稍后排查。

HTTP 401\/403 表示需要检查密钥和权限。遇到 404 时，检查 ID、项目和模式：访问限制可能隐藏账单。这些错误并不意味着客户没有支付任何款项。

## 第5步：持久化履约和恢复信息 {#section-7eb5f5836e7b8f57}

状态为 ` paid ` 时，以保持一致性的方式保存订单状态和履约任务。使用唯一的订单键，并防止并行重复处理。再次读取到 ` paid ` 不应再次提供访问权限或商品；交付后的故障也不应产生第二次履约。

将最后一次成功检查和下一项任务保存在持久化存储中。重启后恢复未完成的检查和任务，而不是只在打开页面时才开始流程。根据业务流程的期限限制当前付款等待；独立的后台核对仍需考虑延迟付款、决定和回滚，包括此前已付款的订单。在尚未执行的交付之前，再次确认是否仍可履约；对已经提供的访问权限，也应安排重新评估。

使用预先准备的响应检查重复的 ` paid `、超时、重启以及回滚后的状态转换。正确的结果是：订单只履行一次，不明确或存在争议的付款仍保留待查。之后可以在保留相同履约保护机制的前提下，用 [webhook](<https://cryptanio.com/zh/wiki/shenme-shi-webhook/>) 补充轮询。

标签: [付款](https://cryptanio.com/zh/wiki/tag/payments/), [API](https://cryptanio.com/zh/wiki/tag/api/)

## 相关文章

- [什么是付款账单](https://cryptanio.com/zh/wiki/shenme-shi-zhangdan/)
- [什么是 TXID？在哪里找到交易 ID](https://cryptanio.com/zh/wiki/transaction-id-txid/)
- [发送加密货币后，为什么订单仍显示未付款](https://cryptanio.com/zh/wiki/crypto-payment-order-unpaid/)
