Estimate a card top-up (no upstream call)
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_minand per-transaction maximum.
Path Parameters
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Required | Card ID |
Request Headers
| Field | Type | Required | Description |
|---|---|---|---|
x-on-behalf-of |
string | Required | The member on whose behalf to call. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
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
{
"customer_total": "100500000",
"arrival_amount": "100000000",
"fee": "500000",
"source_asset": "USDT",
"ledger_scale": 6,
"card_currency": "USD",
"card_scale": 6,
"note": "跨币种到卡额以上游实际成交为准"
}product_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.not_found: the card does not exist or does not belong to this member.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.
{
"customer_total": "100500000",
"arrival_amount": "100000000",
"fee": "500000",
"source_asset": "USDT",
"ledger_scale": 6,
"card_currency": "USD",
"card_scale": 6,
"note": "跨币种到卡额以上游实际成交为准"
}