Z Zise Developers 简体中文

Estimate a card top-up (no upstream call)

POST /v1/cards/{id}/topup-quotes scope: cards:read
On behalf of a member · x-on-behalf-of required

A read-only estimate: no funds locked, no order created, no upstream query. Checking the price should not create a pending quote with the upstream.

⚠ amount is the base amount: we apply markup and fees to calculate the payable customer_total. It is not the amount the user pays. customer_total > amount is normal.

⚠ Cross-currency card credit is only an estimate here, using 1:1. The actual exchange rate is supplied by the upstream when POST /v1/cards/{id}/topups is called. Label the estimate as Subject to the actual executed rate.

⚠ All four amounts in the response are integer strings in the smallest unit: customer_total / fee use ledger_scale for the source asset; arrival_amount uses card_scale for the card currency. The precisions differ; do not use one divisor for both.

We do not return the markup, since doing so would let anyone inspecting traffic derive upstream cost = displayed price ÷ (1+markup).

Prerequisites

  • The card belongs to this member and its product is enabled.
  • The amount is between the product's topup_min and per-transaction maximum.

Path Parameters

FieldTypeRequiredDescription
id string Required Card ID
FieldTypeRequiredDescription
x-on-behalf-of string Required The member on whose behalf to call.

Request Body

FieldTypeRequiredDescription
amount string Required Base amount, a decimal string in the source asset.USDT allows up to 6 decimal places: "100.00".
source_asset string Optional Debit asset. If omitted, selects an asset automatically according to the member's payment priority. ⚠ This is a preference, not an instruction: if the requested asset is disabled or outside the product's allowed list, we silently fall back to payment priority without an error. The returned source_asset identifies the asset actually selected for debit; read it rather than echoing your request.

Response

200Estimate result; this quote is not persisted and consumes no quota.
{
  "customer_total": "100500000",
  "arrival_amount": "100000000",
  "fee": "500000",
  "source_asset": "USDT",
  "ledger_scale": 6,
  "card_currency": "USD",
  "card_scale": 6,
  "note": "跨币种到卡额以上游实际成交为准"
}
400product_not_available: the product is disabled or no eligible debit asset is available. state_invalid: the card status does not allow this action. ⚠ Below-minimum amounts, amounts exceeding the per-transaction limit, and malformed amount strings currently return 500 api_error: specific internal codes have not yet been registered in the public catalog. This is a common case. Treat 500 as potentially caused by your input rather than retrying indefinitely.
404not_found: the card does not exist or does not belong to this member.
Request
curl -X POST 'https://api.zinfra.vip/v1/cards/{id}/topup-quotes' \
  -H 'x-auth-token: Bearer $TOKEN' \
  -H 'x-on-behalf-of: $MEMBER_ID' \
  -H 'content-type: application/json' \
  -d '{
    "amount": "100.00",
    "source_asset": "USDT"
  }'
const res = await fetch("https://api.zinfra.vip/v1/cards/{id}/topup-quotes", {
  method: "POST",
  headers: {
    "x-auth-token": "Bearer $TOKEN",
    "x-on-behalf-of": "$MEMBER_ID",
    "content-type": "application/json",
  },
  body: JSON.stringify({
    "amount": "100.00",
    "source_asset": "USDT"
  }),
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();
import requests

res = requests.post(
    "https://api.zinfra.vip/v1/cards/{id}/topup-quotes",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-on-behalf-of": "$MEMBER_ID",
        "content-type": "application/json",
    },
    json={
      "amount": "100.00",
      "source_asset": "USDT"
    },
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()
req, _ := http.NewRequest("POST", "https://api.zinfra.vip/v1/cards/{id}/topup-quotes",
    strings.NewReader(`{
  "amount": "100.00",
  "source_asset": "USDT"
}`))
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
req.Header.Set("content-type", "application/json")
res, err := http.DefaultClient.Do(req)
// Decode amount fields as string, not float64.
HttpRequest req = HttpRequest.newBuilder()
    .uri(URI.create("https://api.zinfra.vip/v1/cards/{id}/topup-quotes"))
    .header("x-auth-token", "Bearer $TOKEN")
    .header("x-on-behalf-of", "$MEMBER_ID")
    .header("content-type", "application/json")
    .method("POST", HttpRequest.BodyPublishers.ofString("""
{
  "amount": "100.00",
  "source_asset": "USDT"
}
"""))
    .build();
// Use String / BigDecimal for amounts, not double.
$ch = curl_init('https://api.zinfra.vip/v1/cards/{id}/topup-quotes');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'x-auth-token: Bearer $TOKEN',
    'x-on-behalf-of: $MEMBER_ID',
    'content-type: application/json',
  ],
  CURLOPT_POSTFIELDS => <<<'JSON'
{
  "amount": "100.00",
  "source_asset": "USDT"
}
JSON,
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
200
{
  "customer_total": "100500000",
  "arrival_amount": "100000000",
  "fee": "500000",
  "source_asset": "USDT",
  "ledger_scale": 6,
  "card_currency": "USD",
  "card_scale": 6,
  "note": "跨币种到卡额以上游实际成交为准"
}