Z Zise Developers 简体中文

Card issuance invoice: pricing and eligibility for this product and member

GET /v1/cards/products/{id}/quote scope: cards:read
On behalf of a member · x-on-behalf-of required

Use this endpoint for the card application confirmation page. Without it, end users would not see the price until POST /v1/cards/applications had already debited their funds.

Its role differs from GET /v1/cards/products: that endpoint is the catalog, without member context, for the available-cards screen; this endpoint is the invoice.

── Keep issue_fee and shipping_fee separate; do not combine them into one price ──

  • Show the fees as separate rows. If shipping is hidden in the issuance fee, users see a physical card priced at 20 USD without knowing that part of it pays for shipping.
  • The Free shipping label must depend on shipping_fee == 0. Hardcoding it promises something that our administration settings can change at any time.

For virtual cards, shipping_fee is always "0" because nothing is shipped.

── shipping_by_address: shipping fees depend on the destination country ── Present only for physical cards. When a user changes the address on the invoice page, update the shipping fee and total immediately, without another request.

The shipping country is used only to calculate shipping fees; it does not affect issuance country restrictions. Any existing address belonging to the member may be selected. In shipping_by_address, deliverable is always true and reason is empty. Product country allowlists and blocklists check only the KYC used for this application (standard or quick KYC), for both virtual and physical cards. If the KYC is restricted, product-level can_apply is false.

── Read can_apply together with reason ── A bare false would leave you with only a generic Unavailable message, but these cases require different actions: missing KYC requires identity verification; card_address_required requires adding a shipping address; upstream unavailability requires a Try again later message.

Path Parameters

FieldTypeRequiredDescription
id string Required Card product ID returned by GET /v1/cards/products. Accepted with or without the cpd_ prefix.

Query Parameters

FieldTypeRequiredDescription
quick_kyc_binding_id string Optional A quick KYC binding ID belonging to the current member, consistent with the card application. L0 members may explicitly select it for pricing and eligibility checks against that profile; it does not change their own verification level.
FieldTypeRequiredDescription
x-on-behalf-of string Required

Response

200OK
{
  "product_id": "cpd_12",
  "form_factor": "physical",
  "fee_asset": "USDT",
  "ledger_scale": 6,
  "display_scale": 2,
  "issue_fee": "20.00",
  "shipping_fee": "8.00",
  "can_apply": true,
  "reason": "",
  "shipping_by_address": [
    {
      "address_id": "sad_6b1f0c72-9a3e-4d15-8f27-c4e0b9a31d68",
      "country": "HK",
      "is_default": true,
      "shipping_fee": "8.00",
      "total": "28.00",
      "deliverable": true,
      "reason": ""
    }
  ]
}
404not_found: the product does not exist, is disabled, or is not available to you.
Request
curl -X GET 'https://api.zinfra.vip/v1/cards/products/{id}/quote' \
  -H 'x-auth-token: Bearer $TOKEN' \
  -H 'x-on-behalf-of: $MEMBER_ID'
const res = await fetch("https://api.zinfra.vip/v1/cards/products/{id}/quote", {
  method: "GET",
  headers: {
    "x-auth-token": "Bearer $TOKEN",
    "x-on-behalf-of": "$MEMBER_ID",
  },
});
// Keep monetary amounts as strings, never numbers.
const data = await res.json();
import requests

res = requests.get(
    "https://api.zinfra.vip/v1/cards/products/{id}/quote",
    headers={
        "x-auth-token": "Bearer $TOKEN",
        "x-on-behalf-of": "$MEMBER_ID",
    },
)
# Use Decimal(str(...)) for amounts, not float.
data = res.json()
req, _ := http.NewRequest("GET", "https://api.zinfra.vip/v1/cards/products/{id}/quote",
    nil)
req.Header.Set("x-auth-token", "Bearer $TOKEN")
req.Header.Set("x-on-behalf-of", "$MEMBER_ID")
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/products/{id}/quote"))
    .header("x-auth-token", "Bearer $TOKEN")
    .header("x-on-behalf-of", "$MEMBER_ID")
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
// Use String / BigDecimal for amounts, not double.
$ch = curl_init('https://api.zinfra.vip/v1/cards/products/{id}/quote');
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    'x-auth-token: Bearer $TOKEN',
    'x-on-behalf-of: $MEMBER_ID',
  ],
]);
$res = curl_exec($ch);
// Use bcmath / strings for amounts, not floatval.
200
{
  "product_id": "cpd_12",
  "form_factor": "physical",
  "fee_asset": "USDT",
  "ledger_scale": 6,
  "display_scale": 2,
  "issue_fee": "20.00",
  "shipping_fee": "8.00",
  "can_apply": true,
  "reason": "",
  "shipping_by_address": [
    {
      "address_id": "sad_6b1f0c72-9a3e-4d15-8f27-c4e0b9a31d68",
      "country": "HK",
      "is_default": true,
      "shipping_fee": "8.00",
      "total": "28.00",
      "deliverable": true,
      "reason": ""
    }
  ]
}