txid_inBare transaction hash
Never parse a colon-delimited output index from this field.
Guides
Connect the Merchant API from PHP or Node.js, then apply the small chain-family differences that matter to your integration.
Integration path
Use https://api.cryptpays.com/api as the production API base. Chain-specific behavior changes finality, priority, and transaction identity—not the overall flow.
Server integration
Discover current metadata, create an amount-independent address, persist its configuration, and route only a verified confirmed callback into transactional fulfillment.
This runnable PHP example requests current metadata, then explicitly selects JSON POST callbacks. Omitting post keeps the API’s GET callback default.
<?php
function cryptPayJson(string $method, string $url, ?array $body = null): array
{
$curl = curl_init($url);
$options = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => ['Accept: application/json'],
];
if ($method === 'POST') {
$options[CURLOPT_POST] = true;
$options[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json';
$options[CURLOPT_POSTFIELDS] = json_encode($body, JSON_THROW_ON_ERROR);
}
curl_setopt_array($curl, $options);
$responseBody = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
if (!is_string($responseBody) || $status !== 200) {
throw new RuntimeException('CryptPays request failed with HTTP ' . $status);
}
return json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);
}
$apiBase = 'https://api.cryptpays.com/api';
$ticker = 'bep20_usdt';
$callbackUrl = 'https://merchant.example/payments/callback?order_id=123';
$addressOut = '0x1111111111111111111111111111111111111111';
$info = cryptPayJson('GET', "$apiBase/$ticker/info");
$payment = cryptPayJson('POST', "$apiBase/$ticker/create", [
'callback_url' => $callbackUrl,
'address_out' => $addressOut,
'post' => 1,
'json' => 1,
]);
// Persist the order reference, ticker, exact callback URL, address_in,
// address_out, and minimum_transaction_coin snapshot as decimal strings.
printf("Send %s to %s on %s\n", $ticker, $payment['address_in'], $info['ticker']);Use the copy-ready PHP verifier to read the raw request, enforce the 300-second window, and verify RSA-SHA256 before parsing. Then apply this production flow transactionally:
Conceptual lifecycle logic
uuid at pending stage; never fulfill.result="sent" and pending=0.coin; advance pending → sent; set fulfilled_at once.Persist this state in shared durable storage. A process-local array is not safe, and seeing a UUID during pending must not suppress its later confirmed event.
Server integration
Use the built-in fetch API for discovery and creation. On the callback route, raw-body middleware must run before JSON or form parsing.
const apiBase = 'https://api.cryptpays.com/api';
const ticker = 'bep20_usdt';
const callbackUrl = 'https://merchant.example/payments/callback?order_id=123';
const infoResponse = await fetch(`${apiBase}/${ticker}/info`);
if (!infoResponse.ok) throw new Error(`Info failed: HTTP ${infoResponse.status}`);
const info = await infoResponse.json();
const createResponse = await fetch(`${apiBase}/${ticker}/create`, {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json' },
body: JSON.stringify({
callback_url: callbackUrl,
address_out: '0x1111111111111111111111111111111111111111',
post: 1,
json: 1,
}),
});
if (!createResponse.ok) throw new Error(`Create failed: HTTP ${createResponse.status}`);
const payment = await createResponse.json();
// Persist callbackUrl, info.ticker, payment.address_in, payment.address_out,
// and payment.minimum_transaction_coin using decimal-safe string handling.
console.log({ ticker: info.ticker, addressIn: payment.address_in });The validated Express verifier provides the complete canonicalization and node:crypto code. Its middleware order is essential:
// Register this route before express.json() or express.urlencoded().
app.all('/payments/callback', express.raw({ type: () => true }), async (req, res) => {
// Verify timestamp + signature over method, host, req.originalUrl,
// and the exact req.body Buffer. Parse only after verification passes.
const payload = await verifyAndParseCryptPaysCallback(req);
if (payload.result === 'sent' && Number(payload.pending) === 0) {
// In one durable DB transaction: validate order + coin, allow pending → sent,
// and fulfill this confirmed UUID only if fulfilled_at is still empty.
await processConfirmedPaymentIdempotently(payload);
}
return res.sendStatus(200);
});Integration skeleton: use the complete verifier linked above for verifyAndParseCryptPaysCallback and implement the durable merchant transaction in your own storage layer. Do not replace it with an in-memory Set.
Chain guide
Prefer canonical tickers returned by the live catalog. Verified formats include erc20_eth, erc20_usdt, bep20_bnb, bep20_usdt, polygon_pol, and polygon_usdt; runtime enablement may differ.
Shared address, independent payments
Deposits are never combined merely because they use the same address.
/create does not update address_out, confirmations, priority, transport, pending, or conversion settings.priority=default only; explicit slow or fast returns 1103 UNSUPPORTED_PRIORITY.Chain guide
BTC, LTC, BCH, DOGE, and ZEC use network-specific address families. A callback URL already associated with BTC cannot be reused to create an LTC, BCH, DOGE, or ZEC address.
txid_inNever parse a colon-delimited output index from this field.
voutUse (txid_in, vout) as the incoming on-chain identity because one transaction can contain multiple outputs.
confirmations is the incoming customer-deposit count, never the outbound-forward count. You may request a higher threshold within current operator limits; a lower request cannot reduce the network default.
| Family | Supported priorities | Merchant note |
|---|---|---|
| BTC | slow, default, fast | Selected tier affects forwarding network cost. |
| LTC | slow, default, fast | Uses Litecoin-specific network configuration. |
| BCH | slow, default, fast | Uses Bitcoin Cash-specific network rules. |
| DOGE | slow, default, fast | A Dogecoin-specific fee floor still applies. |
| ZEC | default only | ZIP-317 conventional fees do not expose artificial tiers. |
The public minimum remains based on default/medium economics even when a wallet selects slow or fast. Consult live metadata rather than hardcoding a threshold.
Chain guide
Use sol_sol for native SOL and tickers such as sol_usdc for enabled SPL assets. The live catalog is authoritative for availability.
Pending/not final uses confirmations=0. Confirmed/final uses confirmations=1. Numeric confirmations supplied to /create do not change this model, and slot distance is not exposed.
slow, default, and fast are supported and affect the actual forwarding priority and network cost. Internal compute-unit pricing is operator-controlled and not part of the public contract.
Every incoming SOL or SPL deposit is one independent payment with its own UUID, accounting, forward, and callback. Earlier wallet or token balances are not added to a later deposit’s principal, and quick successive deposits remain separate.
Applicable Solana network and rent-related recovery can contribute to fee_coin; fee remains the contracted CryptPays percentage. Reconcile using value_coin = value_forwarded_coin + fee_coin.
Chain guide
trc20_trx is the canonical native format. A token ticker such as trc20_usdt is usable only when that token appears in the live enabled catalog.
priority=default only. Explicit slow or fast is rejected./api/cryptocurrencies.No resource-management or delegation setting is required from merchants.
Migration
CryptPays follows a familiar keyless payment-address and callback model, but migration requires an explicit contract review. Check your existing integration against the CryptPays requirements below rather than assuming an upstream field or behavior is identical.
| Area | CryptPays requirement | What to check in your existing integration |
|---|---|---|
| Ticker | Use the canonical ticker returned by CryptPays discovery. | Do not assume an older ticker string is identical. |
| Address identity | Callback URL plus address family controls compatible reuse. | Use a new callback URL for an incompatible family or new configuration. |
| Confirmed event | Fulfill on result=sent, pending=0. | Do not wait for another terminal wire value. |
| Acknowledgement | Return exactly HTTP 200; body is ignored. | Remove any dependency on a special response body. |
| Signature | Verify the RSA-SHA256 canonical request and timestamp first. | Preserve method, host, exact path/query, and raw body. |
| Idempotency | Track UUID plus lifecycle transition; fulfill confirmed once. | Allow the same UUID to advance from pending to sent. |
| Accounting | fee is percentage; fee_coin is total deduction. | Reconcile with the exact callback invariant. |
| Price/conversion | Quotes and conversion keys use USDT. | Do not interpret them as fiat or multi-fiat output. |
| UTXO identity | Use bare txid_in plus separate vout. | Review the deduplication key. |
| Transport | GET is default; explicitly set post=1, json=1 for JSON POST. | Do not infer delivery method from the method used to call /create. |
| Minimum | Read current values from discovery or /info. | Remove hardcoded thresholds. |
Go-live review
/api/cryptocurrencies for current supported assets./info for current minimum and default metadata./create registers an expected amount.post=1, json=1 explicitly when JSON POST is desired.result=sent, pending=0.txid_in plus vout for UTXO identity.