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"
}
}
| Field | Meaning |
|---|---|
status | The gateway's verdict. Fulfil on SUCCESS / ACCEPTABLE; withhold on MISMATCH / EXPIRED; wait on PENDING. |
isVerified, verifiedAt | Whether (and when) this payment has been verified. Flips on the first verify of a determined payment. |
amount, currencyCode, amountUsd | Your own invoice figures — use them to sanity-check the order you're about to fulfil. |
expectedAmount, receivedAmount, assetSymbol | The 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, receivedAmountUsd | The 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. |
deviationUsd | Signed 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. |
txHash | The 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.
verifiedAtkeeps the timestamp of the first verification. - Verifying a
PENDINGpayment is a no-op — you getstatus: "PENDING"withisVerified: 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.
Recommended flow
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)
Related
- Reconciliation — catching payments a webhook never landed for.
- Verify payment reference — full field and error tables.