On this page
- Match by reference key, not by amount or memo: Fianto puts one on every session's price transfer and sweeps for it every 60 seconds.
- Check the token mint, your destination and the amount in base units: 25 USDC is "25000000" with USDC's 6 decimals.
- Fulfil only at finalized. Fianto marks the order PAID and sends order.paid then, trying each delivery up to 8 times over about 28 hours.
What does it take to confirm a crypto payment?
Confirming a crypto payment takes three separate checks, and skipping any one of them is how merchants ship goods that were never paid for. A screenshot from the customer proves none of them. A wallet notification proves only that something arrived.
- Match: find the one transfer that pays this order, among every transfer your wallet receives.
- Check: confirm it moved the right token, to your account, for the right amount.
- Wait: act only once the block holding it can no longer be rolled back.
Each check has a cheap way that breaks and a reliable way that takes a little setup.
How do you match a crypto payment to an order?
The reliable way to match a payment on Solana is a reference key: a unique address attached to the transfer that you can search for later. The two shortcuts both fail under load.
By amount
Matching by amount works until two customers buy the same thing. Adding odd cents to each order (25.0013, 25.0021) helps, but a customer who rounds, or pays from an exchange that deducts a withdrawal fee, breaks the match.
By memo
A memo is a short text note stored in the transaction. The Solana Pay specification defines it as a string that "must be included in an SPL Memo instruction in the payment transaction." It works when your software builds the transaction. When customers type it themselves, some forget it, mistype it, or use a wallet without a memo field, and anyone reading the chain can read it.
By reference key
The same spec says reference values go in "as read-only, non-signer keys" on the transfer instruction, and that "the getSignaturesForAddress RPC method can be used locate transactions this way." Because the key is unique per order, a lookup by that address returns exactly the transactions for that order.
Fianto gives every checkout session its own reference and appends it to the transfer of the price to your wallet. Every 60 seconds it looks up transfers carrying the reference of each open session, and keeps looking for 24 hours after a session closes. A payment found that way, even one Fianto did not broadcast itself, starts at CONFIRMED and goes through the same checks as the rest.
How do you check the token and amount of a USDC payment?
To check a USDC payment, read three fields from the transfer: the token mint, the destination account and the amount. A mint is the on-chain record that defines a token. Circle lists EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v as the mainnet address of USDC on Solana in its USDC contract addresses (checked 15 October 2026); a token with the same name and another mint is not USDC.
The destination is your USDC token account, not your wallet address itself. Solana's token docs explain that "a Token Account tracks individual ownership of tokens for a specific mint account for a specific owner." Amounts are integers in base units: USDC has 6 decimals, so 25 USDC is 25000000.
Fianto does this check before any money moves. It builds the transaction the customer signs, then compares the signed version with what it built, instruction by instruction: mint, decimals, source, destination, amount and reference. A transaction that differs is not broadcast. A transfer that carries a session's reference but does not match its terms, sent some other way, is recorded on the order as an unmatched transfer, up to 20 per session, and never counted as the payment.
When is a crypto payment on Solana safe to fulfil?
A Solana payment is safe to fulfil at the finalized commitment level, not at confirmed. The Solana RPC documentation defines confirmed as "a block directly voted on by a supermajority of stake, meaning more than two-thirds of the network's active stake," and finalized as "a block the cluster recognizes as finalized with maximum lockout". Its third level, processed, "can still be rolled back."
Fianto uses both moments for different people. The customer's checkout page shows the receipt at confirmed, so they are not left waiting. The order turns PAID, the payment SUCCEEDED, and the webhooks go out only at finalized. Why the two levels differ is explained in confirmed vs finalized on Solana.
How does Fianto tell your server that a payment arrived?
Fianto tells your server with a signed order. webhook, sent once all three checks have passed. The body carries your own order_, the amounts in base units and the Solana transaction signature, so you can look the transfer up on any explorer, as reading a Solana payment on an explorer shows. Shortened from the event reference, for a 25 USDC order with no Fianto fee, as during the beta:
{
"id": "evt_25eaf6aa00802f4e758d9dbfb332941a",
"type": "order.paid",
"data": {
"order_id": "order_1001",
"status": "PAID",
"amount": "25000000",
"fee_amount": "0",
"total_amount": "25000000",
"currency": "USDC",
"payment": { "id": "fian_pay_5caF...", "signature": "dbmKAFCA..." },
"paid_at": "2026-09-28T10:02:14.000Z"
}
}- Each request is signed the Standard Webhooks way, with
webhook-id,webhook-timestampandwebhook-signatureheaders and awhsec_secret. Verify before you trust it. - Delivery is at least once and unordered, so store
event.with the fulfilment and skip repeats.id - Any 2xx within 10 seconds counts. Otherwise Fianto tries each delivery up to 8 times over about 28 hours; after 5 exhausted deliveries in a row the endpoint is suspended and deliveries are held.
- Events are kept 90 days and can be listed with
GET /v1/eventsif your server was down.
What to grant and revoke on each event is set out in webhooks for access, and keys and routes on the developers page. A subscription creates no order, so order. never fires for one: subscriptions send subscription. and subscription. instead.
Fianto matches, checks and waits for finality, then signs one webhook. The merchant pays nothing.
See the hosted checkoutWhat if the money arrived but the order isn't paid?
Money can reach your wallet without a paid order in three situations, and Fianto records each one:
| Situation | What Fianto records | What you see |
|---|---|---|
| A payment finalizes after its session expired | The order still turns PAID, unless you opened a newer session for it | order., without checkout. |
| A second payment for an order already paid | An unmatched transfer on the order | order. and a row on Needs attention |
| A transfer with the wrong amount or fee | An unmatched transfer, or a FAILED payment whose USDC landed | A row under "Orders with an unmatched transfer" or "Failed payments where money moved" |
In the last two, the customer's USDC is in your wallet and Fianto cannot pull it back: there is no refund API, so a refund is a transfer you send yourself. Fianto cannot see that transfer either, so the row stays listed after you refund; keep your own note. The Needs attention docs describe the three tables, and chargebacks vs crypto refunds covers the refund itself.
Can you confirm crypto payments yourself without a gateway?
You can confirm crypto payments yourself with a Solana RPC endpoint and some code, and at a few payments a day it is a reasonable project. The loop looks like this:
- Generate a fresh reference key per order and put it on the transfer you ask the customer to sign.
- Poll
getSignaturesForAddressfor that key until a signature appears or the order expires. - Fetch the transaction at the finalized commitment and check its mint, your token account and the amount.
- Store the signature against the order, so one transfer can never pay two orders, and flag any second transfer for the same reference.
The work is in the edges: transfers that land after you gave up, an RPC provider that misses a poll, customers who pay twice. A crypto payment gateway handles those for you, and with Fianto the merchant pays nothing for it. If you would still rather build, start with the hosted checkout vs a bare wallet address comparison to see which parts you are taking on.