Crypto Payment Reconciliation: Match Stablecoin Invoices to Orders

Crypto Payment Reconciliation: Match Stablecoin Invoices to Orders

A card payment reaches your books through an acquirer report that already names the order. A USDC transfer reaches you with an address, an amount and a transaction hash, and nothing else. Crypto payment reconciliation works when the invoice carries the business reference, because the transfer cannot.

This guide is for finance and operations teams at merchants, PSPs and EMIs that take stablecoins and settle in euros. It lists the fields each invoice record needs, how to treat partial and wrong-coin payments, how webhooks and the API feed the ledger, and which EU rules decide how long you keep it all.

Video summary, 119 seconds.

Key moments

In short

  • An ERC-20 transfer carries only sender, recipient and amount. The order reference has to live on the invoice, not in the payment.
  • Price the invoice in EUR and book that euro figure. The crypto amount is a derived number you keep for the audit trail.
  • Most of the work sits in the exceptions: partial payments, expired invoices, the wrong coin. Review them daily, not at month-end.
  • Use the webhook as the trigger, re-read the invoice by ID as the truth, and pull the invoice list nightly as the safety net and the source of your CSV export.

Why is crypto payment reconciliation harder than cards?#

Crypto payment reconciliation is harder because a blockchain transfer has no field for your order number. On Ethereum, the ERC-20 token standard (EIP-20) defines the transfer event as `Transfer(address _from, address _to, uint256 _value)`: who sent, who received, how much. There is no memo, no invoice number and no customer name.

Bank and card payments are built the other way round. A SEPA transfer has a remittance field for the payer to fill in, and an acquirer's settlement report lists each card transaction against your merchant reference. Your accounting system matches on that reference.

With stablecoins, three more things get in the way:

  • The amount is rarely round. A EUR 250 invoice priced in USDC becomes a figure like 279.2846 USDC, so two invoices of similar size can look alike.
  • Payment is not final at once. Ethereum produces a block every 12 seconds, but according to ethereum.org (page updated October 2026) "it takes about 15 minutes for an Ethereum block to finalize." A transfer can be visible and still not count.
  • Payers make mistakes. They send too little, send twice, send a different coin or pay after the invoice has expired.

So the link has to exist before the payer pays: one invoice per order, carrying your order ID and its own payment address, so each incoming transfer lands on a record that already knows what it is for.

Which fields does each crypto invoice record need?#

Every invoice needs a fixed set of fields that tie four things together: your order, the euro price, the on-chain transfer and the final status. If one is missing, someone ends up matching by hand. The table below uses the field names of the ItisPay invoice API; other providers use different names for the same ideas.

FieldWhat it holdsWhy accounting needs it
invoice_idUnique ID the provider assignsPrimary key for every lookup and every ledger row
order_idYour order or contract number, unique per orderThe link to your ERP, shop or CRM; the reference cards give you for free
order_nameShort description of the orderLets a person check an exception without opening the shop
fiat_amount, fiat_currencyPrice in EURThe revenue figure you book
currency, crypto_amountCoin invoiced and the amount dueShows what the payer was asked to send
actual_crypto_amount_paidWhat actually arrivedDetects underpayment and overpayment
currency_paidCoin used, if different from the one invoicedFlags wrong-coin payments for review
network, addressBlockchain network and payment addressProves where the money was sent; needed for any trace or refund
transactions (tx_hash, amount, status)One line per incoming transferOne invoice can receive several transfers; each hash is your on-chain evidence
statusnew, pending, completed, expired, cancelled, paid_partialDecides whether you book, ship, chase or close
created_at, updated_at, expires_atTimestampsCut-off at month-end and proof of when the order was paid

Two of these do most of the work. `order_id` is the reference that a blockchain transfer cannot carry, so set it from your own system and make it unique per order, never per customer. The `transactions` list matters because an invoice is not always paid in one go: each transfer gets its own row with a hash and a status, so you can see which transfers make up the amount paid.

Store the crypto amount with all its decimals in a separate column. Your general ledger works in euro cents; the crypto columns are evidence, not book values.

ItisPay invoice list showing order number, status, USDC amount, EUR sum, share received, network and address for each order (demo data)

Which euro amount goes into the books?#

Book the euro price on the invoice. When the invoice is issued in EUR, the provider converts it to a crypto amount at the current rate, and the payer sends that amount. If the payment is settled to euros on your account, the euro figure on the invoice is your revenue and the crypto amount is a technical detail of how it arrived.

This is the main reason teams that accept crypto payments automatically with conversion to EUR rarely need crypto accounting at all: there is no coin on the balance sheet to value at month-end.

It changes if you keep the stablecoins. The IFRS Interpretations Committee decided in June 2019 that cryptocurrency holdings fall under IAS 2 (inventory) when held for sale in the ordinary course of business, and IAS 38 (intangible assets) otherwise. Its definition of a cryptocurrency excludes assets that create a contract between the holder and another party. USDC is different: Circle states it is "redeemable 1:1 for US dollars", and since July 2024 Circle issues USDC and EURC in the EU as e-money tokens under an electronic money licence from the French regulator ACPR. How to classify a held stablecoin is a question for your auditor. Settling to EUR on receipt keeps it off the table.

How do you handle partial payments and wrong coins?#

Treat every invoice that is not `completed` as an exception with an owner and a deadline. Most crypto payment reconciliation effort goes here; a fully paid invoice matches itself.

Here is what each status means for finance, using the ItisPay statuses:

StatusWhat happenedWhat finance doesShip or release?
newInvoice open, nothing receivedNothing to bookNo
pendingA transfer is seen, not final yetWait for the status to changeNo
completedPaid in fullBook revenue at the EUR price, release the orderYes
paid_partialPart of the amount arrivedAsk for the rest, refund, or close by agreementDepends
expiredTime ran out without full paymentClose the order; check for late transfersNo
cancelledYou voided the invoiceNo entry; reissue if the customer still wants to payNo

Partial payments#

A partial payment usually means the payer's wallet or exchange took a fee from the amount, or the payer typed it wrong. ItisPay records each transfer separately, so a second transfer can complete the invoice. The invoice also has an `allowed_error_percent` setting: a small tolerance, set per invoice, so a payment a fraction short still counts as paid. Set it once as a policy, for example a level you would also write off on a bank transfer, and do not change it case by case.

If the rest never arrives, decide within a fixed number of days: accept the shortfall and write it off, refund what came in, or cancel the order. Write the decision on the invoice record so the auditor sees why a paid order has less money behind it.

Wrong coin or late payment#

When a payer sends a different stablecoin from the one invoiced, the `currency_paid` field shows it. Do not book it automatically: check the amount and the euro value, then accept it or refund it. A transfer to an expired invoice is similar. The money is on-chain, but your order is closed, so someone must decide whether to reopen the order or return the funds. If an order is voided on your side, cancel the invoice through the API (a status update), so your records show it as closed before any late payment arrives.

How should webhooks and the API feed crypto payment reconciliation?#

Use three layers: the webhook tells you something changed, a fresh read of the invoice tells you what is true, and a nightly pull of all invoices catches anything the first two missed.

  1. Each invoice can carry a `callback_url`; the provider sends status updates there. Treat the message as a trigger, not as proof.
  2. On each webhook, call `GET /invoices/{invoice_id}` and act on the status and amounts in that response. This protects you against forged or replayed calls to your endpoint, since the API call needs your API key.
  3. Make the handler idempotent: the same status can arrive twice. Key your ledger entry on `invoice_id` plus status, so a repeat changes nothing.
  4. Every night, list invoices sorted by `updated_at`, newest first, and page back to your last run; also re-check every invoice still `new`, `pending` or `paid_partial`. Compare them with your ledger. Anything paid in the provider's records but open in yours is an exception.

For testing, ItisPay has an endpoint that simulates a status change for an invoice, and one that resends the latest webhook by `invoice_id` or `order_id`. Use the resend when your server was down. Teams that route several payment methods through one payment orchestration layer apply the same pattern to every rail.

Five steps that match a stablecoin payment to an order: invoice with order ID, on-chain transfer, webhook, re-read by invoice ID, ledger row in EUR

How do you build a CSV export for accounting?#

Build the export from the invoice list API, one row per invoice plus one row per transfer. In ItisPay, `GET /invoices` filters by status, coin and creation date, sorts by date or amount, and returns up to 100 invoices per page with a page count. A small script can walk the pages for a day or a month and write the columns from the field table above.

Two files work better than one:

  • `invoices.csv`: invoice_id, order_id, fiat_amount, fiat_currency, currency, crypto_amount, actual_crypto_amount_paid, status, created_at, updated_at. This is what the accountant imports and matches against sales.
  • `transfers.csv`: invoice_id, tx_hash, amount, currency, transfer status, time. This is the evidence file for the auditor.

Import the invoice file into your accounting tool as you would a card settlement report: one line per order, in euros, with the order ID as the match key.

Which EU rules shape crypto payment records?#

Three EU rules set the context. None of them makes a merchant a crypto-asset service provider (CASP), but they decide which data exists and how long it is kept.

  • Transfer of Funds Regulation (EU) 2023/1113. Since 30 December 2024, the crypto-asset service provider of the payer must make sure each transfer is accompanied by information on the payer and the beneficiary, such as names and ledger addresses (Article 14). The data travels alongside the transfer, not inside it. For transfers above EUR 1,000 to or from a self-hosted wallet, the provider on the relevant side must assess whether that wallet belongs to its own customer (Articles 14 and 16). This is the licensed provider's job, and it is one reason a payment can sit in review.
  • Record keeping. Under the current AML Directive (EU) 2015/849, Article 40, obliged entities keep transaction records for five years after the business relationship ends or the transaction takes place. The new AML Regulation (EU) 2024/1624 keeps the five-year period (Article 77) and applies from 10 July 2027.
  • DAC8, Directive (EU) 2023/2226. From 1 January 2026, crypto-asset service providers report their users' crypto transactions to tax authorities. Your own records should agree with what your provider reports.

If you work through a licensed partner, the partner carries the CASP obligations under MiCA and these rules, as explained in our guide to the MiCA CASP licence. You still keep your order records and your side of the trail.

Where does crypto reconciliation still need a person?#

Automation handles the paid-in-full case. The rest needs a named owner and a short written policy.

Where it does not help

  • Refunds are new outgoing transfers, not reversals. They need a payout to an address or IBAN, and their own records.
  • A payer who sends from an exchange account cannot always be identified by the sending address. Match on the invoice, never on the sender.
  • Payments to an expired invoice, duplicate payments and wrong coins all need a human decision.
  • Network fees and rounding make small differences. Decide a tolerance once and stick to it.

A daily crypto payment reconciliation routine in five steps#

A short daily routine keeps month-end close to a formality:

  1. Pull the invoices created or updated since the last run from the API and update the invoice and transfer files.
  2. Check that every `completed` invoice has a matching paid order in your shop or ERP, and the other way round.
  3. Work the exception list: `paid_partial`, payments to expired invoices, wrong coins. Close each with a written decision.
  4. Match the euro settlement on your bank account against the sum of completed invoices for the period. Settlement to an IBAN is where virtual IBANs help separate flows by business line.
  5. Archive both files for at least five years.

If you want to accept stablecoins and receive euros without building this stack yourself, ItisPay provides the API, invoicing and reconciliation data: invoice and order IDs, per-transfer hashes, statuses and webhooks. Licensing, KYC and settlement to your IBAN run through our EU-licensed CASP + Payment Institution partner, and most teams go from KYB to first transaction in under a week.

This article is for informational purposes only and does not constitute legal or regulatory advice.

Frequently Asked Questions

Can a customer put an order number in a USDC transfer?

Not on a standard ERC-20 transfer: it carries only sender, recipient and amount. Put the order number on the invoice and give each invoice its own payment address.

What happens if a customer pays the same invoice twice?

The second transfer shows up as an extra transaction on the invoice, so the paid amount is higher than the price. Treat it as an overpayment exception: confirm with the customer, then refund the extra amount as a new outgoing transfer and record it against the same invoice.

Should we record the crypto amount or the euro amount?

Book the euro price from the invoice when the payment is settled to euros. Keep the crypto amount, coin, network and transaction hash in separate columns as evidence. If you hold the stablecoins instead, agree the accounting treatment with your auditor first.

How long must crypto payment records be kept?

Plan for at least five years. That is the period EU anti-money-laundering rules set for transaction records kept by obliged entities, and national tax law may require longer. Keep the invoice file, the transfer file and the decisions on exceptions together.

Can stablecoin payments go into Xero, NetSuite or QuickBooks?

Yes, as an imported file. Export one line per paid invoice in euros with your order ID, and import it the same way you import a card settlement report. The transaction hashes go into a separate evidence file.

Nik Perkov

Nik Perkov

Founder, ItisPay

Share this article:

The newsletter for crypto entrepreneurs

Join founders and innovators exploring crypto payments. Get insights, product updates, and stories from businesses building the future of digital commerce.

Unsubscribe anytime. By entering your email, you agree to receive marketing emails from ItisPay. By proceeding, you agree to the Terms and Conditions and Privacy Policy.

Ready to launch stablecoin-first payments under your brand?

Stablecoin-first infrastructure with 1:1 EUR/USD backing. Deploy in 1 week. Built for high-volume PSPs and neobanks with zero volatility risk.