Docs · Reconciliation
Payment reconciliation with ArosaPay
Reconciliation connects your own order records to ArosaPay transaction and settlement records, so that for every order you can compare what you expected with what actually happened.
What reconciliation means here
A payment is reconciled when one of your orders can be tied to exactly one ArosaPay transaction, and the amount recorded as settled matches the amount you expected after fees and any approved adjustment. Anything that does not tie out is a reconciliation exception — something to investigate, not a transaction state.
Which identifiers does each transaction carry?
Every ArosaPay transaction carries three identifiers plus a settlement state. Together they let you post to your ledger without guessing.
A stable string you attach at creation. Idempotent — resubmitting the same reference returns the existing transaction.
Your internal order ID, propagated on every webhook and export row.
ArosaPay's canonical UUID for the transaction (AP-prefixed for display).
The reconciliation-friendly settlement state: pending, released, refunded, or partial.
Where do the identifiers show up?
The same three identifiers appear in every place you can read transaction data, so one reconciliation key works everywhere.
Every event carries merchant_reference and arosapay_transaction_id in the payload.
Search transactions by any of the three identifiers.
Payout and transaction exports include all three columns plus settlement_status.
Transaction and payout endpoints echo all three fields.
The six-step reconciliation workflow
Run the same sequence every reconciliation cycle, whether you reconcile daily from webhooks or weekly from exports.
- 01Preserve your own reference
Attach merchant_reference (and merchant_order_id where you have one) when the transaction is created. Store both against the order in your system before the buyer pays, so no record can exist on one side only.
- 02Match the order to the ArosaPay transaction
Pull the transaction by merchant_reference from a webhook event, the API, or a CSV export, and confirm it resolves to exactly one arosapay_transaction_id. A reference resolving to none, or your books holding two orders for one reference, is an exception to log immediately.
- 03Follow the financial state, not the event count
Read the transaction's current state rather than counting webhooks. States such as release_pending, settlement_released, partial_resolution, refunded and under_review each imply a different expected movement, and delivery is at-least-once so events can repeat.
- 04Calculate the expected settlement
Take the gross amount, subtract the ArosaPay fee (2.9% + KES 30 per completed transaction · min KES 100), then apply any approved adjustment such as a refund or partial resolution. The result is the expected net for that transaction.
- 05Match the settlement record
Compare the expected net against the settled amount on the payout or settlement record, matching on the settlement reference. Group by settlement reference when several transactions settle together, so a batch total is explained line by line.
- 06Investigate every difference
Post matched lines and route the rest to an exception list with the reason attached. Do not adjust your ledger to force a match — record the difference, resolve the cause, then post.
Worked examples
Both examples use illustrative figures. The fee shown is calculated from ArosaPay's published pricing rather than typed by hand.
- Your order
- ORD-20481
- merchant_reference
- ORD-20481
- arosapay_transaction_id
- AP-7C41F2
- Gross amount
- KES 25,000
- ArosaPay fee
- − KES 755
- Adjustments
- KES 0
- Expected net
- KES 24,245
- Settlement state
- settlement_released
- Settlement reference
- STL-2026-0311-004
- Settled amount
- KES 24,245
Result: matched. Expected net equals the settled amount, so the line posts.
- Your order
- ORD-20482
- merchant_reference
- ORD-20482
- arosapay_transaction_id
- AP-91B0D7
- Gross amount
- KES 25,000
- ArosaPay fee
- − KES 755
- Adjustments
- Not yet identified
- Expected net
- KES 24,245
- Settlement state
- partial_resolution
- Settlement reference
- STL-2026-0311-004
- Settled amount
- KES 22,745
Result: exception. The settled amount is KES 1,500 lower than expected, so the line is held for investigation.
How to classify an exception
Most differences fall into one of six causes. Classify before you adjust anything.
Your expected net used a rate or minimum that differs from the fee actually charged on the transaction.
A refund or partial resolution split the amount, so one merchant_reference produces two movements.
The transaction was created without your merchant_reference, or your books hold a different reference for the same order.
The transaction is still in a pre-settlement state such as release_pending, so no settled amount exists to match.
A retry created a second row in your system, or an event was applied twice on your side. ArosaPay treats merchant_reference as idempotent.
The transaction is in under_review while a dispute or risk check completes, and the outcome will determine the final movement.
Best practice
Which identifier should you key on?
Never key on arosapay_transaction_id alone. Attach your own merchant_reference at creation and reconcile from there. It survives your migrations and matches your books.
See webhook envelopeDeveloper integration
ArosaPay reconciliation becomes easier when transaction references and settlement events are preserved consistently through the integration.
Store your own order reference when the transaction is created — for example merchant_reference: ORD-20481 — and keep it against the order in your system.
Use ArosaPay webhook events to keep your system synchronised with authoritative transaction and settlement changes rather than polling.
Use the API reference for the actual request fields, identifiers and supported integration contracts.
- Merchant order — ORD-20481
- ArosaPay transaction — AP-TXN-84721
- Webhook events
- Settlement reference — STL-2026-0311-004
- Reconciliation result
Conceptual only. The identifiers above are illustrative; the API reference defines the actual field names and formats.
Reconciliation questions
What does it mean to reconcile payments?
Reconciling payments means comparing your own order records against the payment provider's transaction and settlement records, so that for every order you can show what was expected and what actually happened. With ArosaPay you compare your order to the ArosaPay transaction, its settlement state, and the amount recorded as settled.
How do I reconcile online payments against my orders?
Attach your own merchant_reference when the transaction is created, then match on it everywhere: webhook payloads, API responses, the dashboard, and CSV exports. For each match, compare gross amount, ArosaPay fee, any approved adjustment, and the resulting expected net against the settlement record.
Which identifier should I reconcile on?
Reconcile on merchant_reference. You attach it at creation, it survives your own migrations, and it is echoed on every webhook, API response, and export row. Use arosapay_transaction_id as a secondary key for support conversations.
What records should I keep in order to reconcile?
Keep your order ID and merchant_reference, the arosapay_transaction_id, the gross amount and currency, the ArosaPay fee, any approved adjustment, the expected net, the settlement reference, and the settlement timestamps. Those fields are enough to explain any difference without contacting support.
What should I do when a settlement does not match my expected amount?
Treat it as a reconciliation exception and investigate rather than adjusting your books. The usual causes are a fee assumption that differs from the fee actually charged, an approved adjustment such as a partial resolution or refund, a missing or mismatched merchant_reference, a transaction that has not finished settling yet, or a duplicate record on your side.
How is a partial resolution reflected in reconciliation?
A partial resolution splits the transaction amount between the buyer and the merchant. The settlement_status becomes partial, and the settled and refunded amounts are reported separately so your ledger can post two movements against one merchant_reference.
Can I reconcile M-Pesa and card payments the same way?
Yes. The payment method the buyer used does not change the reconciliation keys: whether the buyer paid by M-Pesa or by card, the transaction carries the same merchant_reference, arosapay_transaction_id, and settlement_status, and settles into the same records.
What happens if the same webhook is delivered twice?
Webhook delivery is at-least-once, so treat every event as replayable. Store the event id and ignore an event you have already applied, then reconcile the resulting state from merchant_reference rather than from the number of events received.
Which columns does the CSV export include?
Transaction and payout exports include merchant_reference, merchant_order_id, arosapay_transaction_id, and settlement_status, alongside amount, currency, fee, and the timestamps for creation and settlement.
Is resubmitting the same merchant_reference safe?
Yes. merchant_reference is idempotent: resubmitting it returns the existing transaction instead of creating a duplicate, which makes retries after a network timeout safe.
Keep reading
- How ArosaPay safeguards a payment until confirmation — the lifecycle behind each settlement state.
- What protected checkout means for buyers and sellers — why funds are released on confirmation.
- Protected checkout for merchants — how sellers receive settlement.
- Webhook events and delivery guarantees — the payloads you reconcile from.
- Transaction and payout API reference — the fields returned on every read.
- ArosaPay pricing and fees — the fee applied when you calculate expected net.
Related developer documentation
- Webhooks — receive authoritative transaction lifecycle events.
- API reference — review transaction and identifier fields.
- How protected checkout works — understand the product lifecycle behind the states.