Skip to main content
Learn how to implement merchant payment acceptance patterns in Formance Ledger for card transactions, real-time authorization, and settlement flows.
This guide demonstrates practical implementation patterns for payment acceptance operations using Formance Ledger. Whether you’re building a wallet top-up system, marketplace, or payment platform, these patterns help you manage real-time card authorizations and asynchronous settlement.

What is Payment Acceptance?

Payment acceptance is the process of receiving payments from customers, particularly through card payments. As a merchant or acceptor, you integrate with payment service providers (PSPs) to enable customers to pay with their cards.

Payment Flow Overview

1

Payment Initiation

Customer initiates a payment for a defined amount in a specific currency using their card as the payment instrument.Integration Types:
  • Hosted Payment Page (HPP): PSP-hosted checkout page
  • Tokenization: Securely store card details for future use
  • API Integration: Direct API calls for recurring or one-click payments
2

Authorization

The card issuer approves the transaction and agrees to eventually pay your acquirer. This creates a real-time promise that you can rely on for immediate user experience.Authorization Types:
  • CiT (Cardholder-initiated Transaction): Customer actively participates
  • MiT (Merchant-initiated Transaction): Automated charges using stored cards
3

Capture (Optional)

Request the issuer to finalize the payment. Capture can be:
  • Automatic: PSP captures at end of day
  • Explicit: You control when to capture
  • Partial: Capture less than authorized amount
  • Multiple: Capture in installments (for some card schemes)
4

Settlement

The acquirer settles funds to your bank account, typically T+1 to T+3 days after authorization.

Focus: Wallet Top-Up Use Case

This guide focuses on a straightforward wallet top-up scenario where customers add funds to their account balance. Key characteristics:
  • Immediate Availability: Funds are available to the customer as soon as the card is authorized
  • Acquirer Promise: Authorization is treated as an asset—a binding promise from the acquirer
  • Automatic Capture: All authorizations are considered captured for immediate booking
  • Asynchronous Settlement: Bank settlement occurs days later, requiring careful tracking
The real-time aspect enables excellent user experience—customers can use funds immediately. However, you must manage the asynchronous nature of acquirer settlement on the backend.

Implementation Guide

This section demonstrates how to model payment acceptance operations in Formance Ledger for wallet top-ups.

System Actors

  • Acquirers
  • Banks
  • End Users (Clients)
  • Platform
Normal Debit Accounts - Hold real-time payment promises.Financial institutions providing merchant services, including:
  • Card transaction underwriting and risk management
  • Real-time authorization processing
  • Bulk settlement (gross or net of fees)
  • Currency conversion services
Key Concept: A captured authorization is treated as an asset—the acquirer’s binding promise to pay. This is as reliable as a bank statement for immediate booking.Multi-Acquirer Support: Use multiple acquirers (Stripe, Adyen, etc.) with payment orchestration. Formance allows centralized tracking across all providers.Account structure:
@acquirers:{acquirer_id}:main
Similar to Omnibus accounts, but the acquirer holds a real-time payment promise rather than settled funds.

Balances & Periods

A balance of an asset in an account is the result of the sum of all incoming volumes subtracted by the sum of all outgoing volumes. Here we will have to deal with a real-time aspect we want to handle in order to have the best user experience, where the end user has immediately their balance/top-up amounts at their disposal, even if the amount are net yet settled into your accounts. As you base yourself on binding technical payment signals (aut+capture), the settlement arrives at a later data and clears the amounts due by the acquirer. Handling both of these aspects in the Formance Ledger will allow you to have an instant and clear view of where the assets are, and how they are moving around.

Transaction Patterns

Below are example Numscripts that capture the intent of the movements and bookings needed to handle payment acceptance flows. This is merely an example and your business might need different implementations, additional details, different steps and events to handle, etc…
Download the T-Account Movements Excel Template to visualize all accounting entries with detailed T-account diagrams for each payment acceptance flow described in this guide.
Zoom 🔍

Card Authorization - Gross Top-Up

When a card authorization is successful and captured, you make the full gross amount immediately available to the end user. The acquirer has provided a real-time promise to settle, which you treat in the ledger as an amount that the acquirer owes you in a normal debit account.

Variables

  • $asset (asset): asset in UMN
  • $amount (number): Transaction amount (gross)
  • $acquirer_id (account): Acquirer account identifier
  • $client_id (account): Client account identifier
  • $platform_name (account): Platform account identifier
  • $authorization_id (string): Unique authorization reference

Accounts Used

  • @acquirers:$acquirer_id:main : Account holding the acquirer promise (normal debit)
  • @clients:$client_id:main : Client wallet account (normal credit - liability)
  • @platform:$platform_name:fees : Platform expense account for fees paid

Numscript

vars {
    asset $asset
    number $amount
    account $acquirer_id
    account $client_id
    account $platform_name
    string $authorization_id
}

send [$asset $amount] (
    source = @acquirers:$acquirer_id:main allowing unbounded overdraft
    destination = @clients:$client_id:main
)



set_tx_meta("authorization_id", $authorization_id)
set_tx_meta("type", "card_authorization_gross_topup")

Example Usage

EUR card top-up authorization - Try in Playground →
{
    "variables": {
        "asset": "EUR/2",
        "amount": "10000",
        "acquirer_id": "stripe",
        "client_id": "user123",
        "platform_name": "myplatform",
        "authorization_id": "auth_abc123xyz789"
    }
}
USD card top-up authorization - Try in Playground →
{
    "variables": {
        "asset": "USD/2",
        "amount": "5000",
        "acquirer_id": "adyen",
        "client_id": "user456",
        "platform_name": "myplatform",
        "authorization_id": "auth_def456abc123"
    }
}

Acquirer Settlement to Bank

When the acquirer settles funds to your bank account (typically in bulk, daily or weekly), they settle the net amount after deducting their fees. You receive the net settlement in your bank account and need to cover the fees from your platform account to balance the acquirer account.

Variables

  • $asset (asset): asset in UMN
  • $net_amount (number): Net settlement amount received in bank
  • $fee_amount (number): Fee amount deducted by acquirer
  • $acquirer_id (account): Acquirer account identifier
  • $bank_number (account): Bank account identifier
  • $platform_name (account): Platform account identifier
  • $settlement_ref (string): Settlement reference from acquirer

Accounts Used

  • @acquirers:$acquirer_id:main : Acquirer account (normal debit)
  • @banks:$bank_number:main : Bank account (normal debit)
  • @platform:$platform_name:fees : Platform expense account for fees

Numscript

vars {
    asset $asset
    number $net_amount
    number $fee_amount
    account $acquirer_id
    account $bank_number
    account $platform_name
    string $settlement_ref
}

send [$asset $net_amount] (
    source = @banks:$bank_number:main allowing unbounded overdraft
    destination = @acquirers:$acquirer_id:main
)

send [$asset $fee_amount] (
    source = @platform:$platform_name:fees allowing unbounded overdraft
    destination = @acquirers:$acquirer_id:main
)

set_tx_meta("settlement_ref", $settlement_ref)
set_tx_meta("type", "acquirer_settlement")

Example Usage

EUR settlement from Stripe to bank - Try in Playground →
{
    "variables": {
        "asset": "EUR/2",
        "net_amount": "9750",
        "fee_amount": "250",
        "acquirer_id": "stripe",
        "bank_number": "FR7630004028379876543210943",
        "platform_name": "myplatform",
        "settlement_ref": "po_1pkJxL2eZovKypr4123456789"
    }
}
USD settlement from Adyen to bank - Try in Playground →
{
    "variables": {
        "asset": "USD/2",
        "net_amount": "4850",
        "fee_amount": "150",
        "acquirer_id": "adyen",
        "bank_number": "021000089:123456789",
        "platform_name": "myplatform",
        "settlement_ref": "SETTLE_20240115_000123"
    }
}

Card Refund

When processing a refund for a previously captured card payment, you reverse the original booking flow.

Variables

  • $asset (asset): asset in UMN
  • $amount (number): Refund amount
  • $fee_refund (number): Fee refund amount (if applicable)
  • $acquirer_id (account): Acquirer account identifier
  • $client_id (account): Client account identifier
  • $platform_name (account): Platform account identifier
  • $refund_id (string): Unique refund reference
  • $original_authorization_id (string): Reference to original authorization

Accounts Used

  • @acquirers:$acquirer_id:main : Acquirer account
  • @clients:$client_id:main : Client wallet account
  • @platform:$platform_name:fees : Platform fee account

Numscript

vars {
    asset $asset
    number $amount
    account $acquirer_id
    account $client_id
    string $refund_id
    string $original_authorization_id
}

send [$asset $amount] (
    source = @clients:$client_id:main 
    destination = @acquirers:$acquirer_id:main
)



set_tx_meta("refund_id", $refund_id)
set_tx_meta("original_authorization_id", $original_authorization_id)
set_tx_meta("type", "card_refund")

Example Usage

EUR card refund - Try in Playground →
{
    "variables": {
        "asset": "EUR/2",
        "amount": "10000",
        "acquirer_id": "stripe",
        "client_id": "user123",
        "refund_id": "re_1OvWAPZ87GFIB715J",
        "original_authorization_id": "auth_abc123xyz789"
    }
}
Ensure the client has sufficient balance before processing the refund. The refund will debit their account and credit the acquirer, who will then process the refund back to the cardholder’s card. So make sure you execute this before sending the refund instruction to the acquirer.

Chargeback

When a chargeback occurs, the acquirer debits your account and you must debit the client’s account.

Variables

  • $asset (asset): asset in UMN
  • $amount (number): Chargeback amount
  • $chargeback_fee (number): Chargeback fee charged by acquirer
  • $acquirer_id (account): Acquirer account identifier
  • $client_id (account): Client account identifier
  • $platform_name (account): Platform account identifier
  • $chargeback_id (string): Unique chargeback reference
  • $original_authorization_id (string): Reference to original authorization

Accounts Used

  • @acquirers:$acquirer_id:main : Acquirer account
  • @clients:$client_id:main : Client wallet account
  • @platform:$platform_name:chargeback_fees : Platform expense for chargeback fees

Numscript

vars {
    asset $asset
    number $amount
    number $chargeback_fee
    account $acquirer_id
    account $client_id
    account $platform_name
    string $chargeback_id
    string $original_authorization_id
}

send [$asset $amount] (
    source = @clients:$client_id:main allowing unbounded overdraft
    destination = @acquirers:$acquirer_id:main
)

send [$asset $chargeback_fee] (
    source = @platform:$platform_name:chargeback_fees allowing unbounded overdraft
    destination = @acquirers:$acquirer_id:main
)

set_tx_meta("chargeback_id", $chargeback_id)
set_tx_meta("original_authorization_id", $original_authorization_id)
set_tx_meta("type", "chargeback")

Example Usage

USD chargeback with fee - Try in Playground →
{
    "variables": {
        "asset": "USD/2",
        "amount": "5000",
        "chargeback_fee": "1500",
        "acquirer_id": "adyen",
        "client_id": "user456",
        "platform_name": "myplatform",
        "chargeback_id": "CB_20240115_000123",
        "original_authorization_id": "auth_def456abc123"
    }
}
Chargebacks can result in negative balances if the client has already spent the funds. Your business logic should handle these scenarios, potentially creating a debt recovery process or reserving funds for dispute resolution.

Key Differences from Omnibus Accounts

Payment acceptance builds on omnibus account patterns but introduces unique real-time characteristics:
  • Timing & Availability
  • Asset Flow
  • Fee Management
  • Dispute Handling
Payment Acceptance:
  • Funds available immediately upon authorization
  • Based on acquirer’s real-time promise
  • Customer can spend before settlement
Omnibus Accounts:
  • Funds available upon bank settlement
  • Based on confirmed wire transfer
  • Settlement-driven availability

Advanced Ledger Features

These examples use Numscript features available in Ledger v2.3+. Ensure your deployment runs a compatible version.