Skip to main content

    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.

    merchant_reference

    A stable string you attach at creation. Idempotent — resubmitting the same reference returns the existing transaction.

    merchant_order_id

    Your internal order ID, propagated on every webhook and export row.

    arosapay_transaction_id

    ArosaPay's canonical UUID for the transaction (AP-prefixed for display).

    settlement_status

    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.

    Webhooks

    Every event carries merchant_reference and arosapay_transaction_id in the payload.

    Dashboard

    Search transactions by any of the three identifiers.

    CSV exports

    Payout and transaction exports include all three columns plus settlement_status.

    API responses

    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.

    1. 01
      Preserve 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.

    2. 02
      Match 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.

    3. 03
      Follow 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.

    4. 04
      Calculate 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.

    5. 05
      Match 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.

    6. 06
      Investigate 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.

    Illustrative example — matched
    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.

    Illustrative example — exception
    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.

    Fee assumption differs

    Your expected net used a rate or minimum that differs from the fee actually charged on the transaction.

    Approved adjustment applied

    A refund or partial resolution split the amount, so one merchant_reference produces two movements.

    Missing or mismatched reference

    The transaction was created without your merchant_reference, or your books hold a different reference for the same order.

    Settlement not complete yet

    The transaction is still in a pre-settlement state such as release_pending, so no settled amount exists to match.

    Duplicate or missing record

    A retry created a second row in your system, or an event was applied twice on your side. ArosaPay treats merchant_reference as idempotent.

    Under review

    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 envelope

    Developer integration

    ArosaPay reconciliation becomes easier when transaction references and settlement events are preserved consistently through the integration.

    How the references connect
    1. Merchant order — ORD-20481
    2. ArosaPay transaction — AP-TXN-84721
    3. Webhook events
    4. Settlement reference — STL-2026-0311-004
    5. 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

    Related developer documentation