Skip to main content

Verify Payments

Verification is the step that makes your integration safe. The callback is a forgeable browser redirect and the webhook is a thin trigger — neither carries amounts, and neither should be trusted for fulfilment. The verify endpoint is the single source of truth.

The call​

curl -X POST https://api.finomesh.com/api/v1/payments/{paymentId}/verify \
-H "X-Api-Key: $FINOMESH_API_KEY"

No request body. The payment must belong to your gateway — a foreign or unknown ID returns 404 not_found.

The response​

{
"success": true,
"data": {
"paymentId": "0d9f3a64-4f0c-4b6e-9f6e-2f4f4c1b8a21",
"status": "ACCEPTABLE",
"checkoutMode": "HOSTED",
"isVerified": true,
"verifiedAt": "2026-06-12T10:21:43Z",
"amount": "49.99",
"currencyCode": "USD",
"amountUsd": "49.99",
"txHash": "0xabc…",
"expectedAmount": "51.69",
"receivedAmount": "51.42",
"assetSymbol": "USDT",
"receivedAssetSymbol": "USDT",
"receivedAmountUsd": "51.42",
"deviationUsd": "-0.27"
}
}
FieldMeaning
statusThe gateway's verdict. Fulfil on SUCCESS / ACCEPTABLE; withhold on MISMATCH / EXPIRED; wait on PENDING.
isVerified, verifiedAtWhether (and when) this payment has been verified. Flips on the first verify of a determined payment.
amount, currencyCode, amountUsdYour own invoice figures — use them to sanity-check the order you're about to fulfil.
expectedAmount, receivedAmount, assetSymbolThe comparable crypto pair: what the customer was quoted (expectedAmount, in assetSymbol units) vs. what landed on-chain. receivedAmount is denominated in receivedAssetSymbol, not assetSymbol — they match unless the customer paid in a different asset than they selected. Present only once a resolving transaction exists (SUCCESS / ACCEPTABLE / MISMATCH); absent for EXPIRED and PENDING.
receivedAssetSymbol, receivedAmountUsdThe asset receivedAmount is actually denominated in, and its USD value. When receivedAssetSymbol differs from assetSymbol (a wrong-asset deposit), don't compare receivedAmount to expectedAmount unit-for-unit — compare receivedAmountUsd against amountUsd, or use deviationUsd directly.
deviationUsdSigned USD value of received − expected — negative for underpayment, positive for overpayment, "0" for an exact match. Always computed in USD, so it's safe to branch on even when the received asset differs from the quoted one.
txHashThe resolving on-chain transaction.

All monetary values are strings — parse them with a decimal type, never a float.

Semantics worth knowing​

  • Idempotent — call it as many times as you like; repeat calls return the current authoritative state. verifiedAt keeps the timestamp of the first verification.
  • Verifying a PENDING payment is a no-op — you get status: "PENDING" with isVerified: false. Poll-verify is therefore a valid (if blunt) integration style; the webhook is the push alternative.
  • Verification quiets the webhook — once a payment is verified, any still-undelivered webhook for it is skipped. If your callback handler verifies promptly, you will often never receive the (now redundant) webhook.
  • Ownership-scoped — the payment must belong to the API key's gateway, otherwise 404. Cross-tenant payment IDs cannot be probed.
on callback OR webhook (both give you paymentId):
resp = POST /payments/{paymentId}/verify

switch resp.data.status:
SUCCESS, ACCEPTABLE → mark order paid, fulfil
MISMATCH → hold order, alert staff (deviationUsd tells the story)
EXPIRED → cancel order
PENDING → do nothing yet (trigger was early or forged)