J Reconcile payments#

All payments flowing in and out of your financial systems must align with the balances shown in your product. For correct balances, update your ledger for every event in your product workflow and payments processor.

Payment workflows occur in two phases, each with one or more steps:

  1. Initiation: Includes steps to prepare and initiate a payment, like recording a future payment, creating a payment batch, initiating a payment, or your counterparty initiating a payment to you.
  2. Settlement: Includes steps for a payment arriving at your bank and your post-processing. Multiple post-processing steps are required if you or your payment processor batch multiple initiations into a single settlement.

As a payment processes, you'll post to account combinations representing each step:

  • Cash accounts: track the location of settled funds. These balances should match balances at your bank or payment processor.
  • Offset accounts: track the purpose of settled funds, like an available user balance or revenue. These balances are expected to be non-zero once a payment finishes processing.
  • Clearing accounts: track pending and unsplit payments, like a pending user withdrawal or upcoming loan payment. These balances are expected to be zero once a payment finishes processing.

For complex payments, each additional step in either the initiation or settlement phase requires an additional clearing account and Ledger Entry type in your Schema.

a. Receive payment

#

Most payment settlements are single purpose and result in a single set of balance updates in your ledger. It's common to skip the initiation phase when receiving these payments. This happens when your counterparty initiates the payment and you don't know a payment is in-flight until it settles at your bank account.

Schema updates#

Add a cash account:

  • an asset Ledger Account with the linked flag set
  • represents the bank account that will receive the funds

Add a "reconcile_payment" Ledger Entry type to your Schema that posts to:

  • the cash account: updates the ledger with the new cash balance
  • one or more offset accounts: tracks the purpose of the payment e.g. updating a user's balance
Process#

When you receive a payment:

  1. Identify the source and nature of the payment.
  2. Sync the transaction: Use Links to sync the settlement to FRAGMENT, the transaction is unreconciled.
  3. Record the payment: Use reconcileTx to post the "reconcile_payment" Ledger Entry to reconcile the transaction.

To automate this process, you'll need to correlate each payment to a product workflow, like which user topped up or which invoice was paid. You can use a trace ID from the payment sender, transaction descriptions or single-purpose payment routes, like VBANs or virtual card numbers, to determine the correct Ledger Entry Type to post and its parameters.

b. Receive bulk payment

#

If you receive a payment that settles multiple product workflows, you'll need to split the payment using a clearing account. This account holds funds during the splitting process and ensures all splits add up to the received amount.

Schema updates#

Add a cash account:

  • an asset Ledger Account with the linked flag set
  • represents the bank account that will receive the funds

Add a clearing account:

  • a liability Ledger Account with the clearing and template flags set
  • use the payment ID as the template value
  • tracks unsplit funds for each payment
  • the balance will be zero once the payment has been fully processed

Add a "received_bulk_payment" Ledger Entry type that posts to:

  • the cash account: updates the ledger with the new cash balance
  • the clearing account: sets the initial balance of the clearing account to the total amount received

A "split_bulk_payment" Ledger Entry type that posts to:

  • the clearing account: each split will reduce the balance, until it is zero
  • one or more offset accounts: assigns a portion of payment amount to each product workflow
Process#

When you receive a bulk payment:

  1. Identify the source and nature of the payment. You'll need settlement data from the payment sender to understand the individual items included in the payment.
  2. Sync the transaction: Use Links to sync the settlement to FRAGMENT, the transaction is unreconciled.
  3. Record the payment: Use reconcileTx to post a "received_bulk_payment" Ledger Entry against the synced transaction. This reconciles the transaction and creates a pending clearing account.
  4. Split the payment: Use addLedgerEntry to post a "split_bulk_payment" Ledger Entry per item in the payment. Once this process is completed, the clearing account is "cleared".
Multi-step#

Some bulk settlements are batches of batches. For example with network-level card ledgering, you receive multiple "clearings" per day and a settlement once a day. Each day, you'll receive one settlement file, that breaks down the total payment per clearing, and multiple clearing files that break down each "clearing" into individual card transactions.

In this case, you'll have a three step settlement phase:

  • "received_bulk_payment": posts between a cash account and a clearing account for each settlement
  • "split_settlement": posts between the clearing account for a settlement and a clearing account for each clearing file
  • "split_clearing_file": posts between the clearing account for a clearing file and offsets accounts for each item in the clearing file

c. Send payment

#

Sending a payment that has a single purpose requires two phases: initiation and settlement.

Schema updates#

Add a cash account:

  • an asset Ledger Account with the linked flag set
  • represents the bank account from which funds will be sent

Add a clearing account:

  • a liability Ledger Account with the clearing and template flags set
  • use the payment ID as the template value
  • tracks initiated but unsettled payments
  • the balance will be zero once the payment has settled

Add an "initiate_payment" Ledger Entry type that posts to:

  • one or more offset accounts: updates accounts that track settled funds, like a user's available funds
  • the clearing account: sets the balance to the payment amount

Add a "settle_payment" Ledger Entry type that posts to:

  • the clearing account: reduces the balance to zero
  • the cash account: updates the ledger to reflect funds leaving your bank
Process#

When sending a payment:

  1. Record initiation: Use addLedgerEntry to post the "initiate_payment" Ledger Entry. This creates a pending clearing account and updates your product balances.
  2. Initiate the payment: Send the payment to your bank or payment processor.
  3. Wait for settlement: Your bank will process the payment and create a transaction in your bank account.
  4. Sync the transaction: Use Links to sync the settlement to FRAGMENT.
  5. Record settlement: Use reconcileTx to post the "settle_payment" Ledger Entry. This reconciles the transaction and clears the pending payment.

If a payment is cancelled after initiation but before settlement, you can either reverse the initiation entry or post a new entry with opposite amounts.

If a payment reverses after settlement, the bank or payment processor creates a reversal transaction. Sync this transaction and reconcile it with a Ledger Entry that posts opposite amounts to the original settlement.

d. Send bulk payment

#

If you initiate multiple payments that you batch into a single settlement, you'll need two clearing accounts. The first tracks individual payments. The second tracks the batch once initiated.

Schema updates#

Add a cash account:

  • an asset Ledger Account with the linked flag set
  • represents the bank account from which funds will be sent

Add a pending initiation clearing account:

  • a liability Ledger Account with the clearing and template flags set
  • tracks each payment before it joins a batch
  • generate a payment ID for the template value
  • the balance will be zero once the payment joins a batch

Add a pending settlement clearing account:

  • a liability Ledger Account with the clearing and template flags set
  • use the batch ID as the template value
  • tracks the batch after initiation but before settlement
  • the balance will be zero once the batch settles

Add a "initiate_payment" Ledger Entry type that posts to:

  • the pending initiation clearing account: holds each payment amount until it joins a batch
  • one or more offset accounts: updates accounts that track settled funds, like a user's available funds

Add a "prepare_batch" Ledger Entry type that posts to:

  • the pending initiation clearing account: reduces the balance to zero for each payment in the batch
  • the pending settlement clearing account: adds each payment amount to the total batch amount

Add a "settle_batch" Ledger Entry type that posts to:

  • the pending settlement clearing account: reduces the balance to zero
  • the cash account: updates the ledger to reflect the batch leaving your bank
Process#

When sending a bulk payment:

  1. Record each payment: Use addLedgerEntry to post a "initiate_payment" Ledger Entry for each payment. This creates pending clearing accounts and updates your product balances.
  2. Collect payments into a batch: Use addLedgerEntry to post a "prepare_batch" Ledger Entry. This clears the individual payment clearing accounts and creates a batch clearing account.
  3. Initiate the batch: Send the batch through your bank or payment processor.
  4. Wait for the bank to process the batch. This will create a transaction at the bank.
  5. Sync the transaction: Use Links to sync the settlement to FRAGMENT.
  6. Record settlement: Use reconcileTx to post the "settle_batch" Ledger Entry. This reconciles the transaction and clears the batch clearing account.

If a payment is cancelled before joining a batch, reverse the "initiate_payment" entry or post a new entry with opposite amounts.

If a payment is cancelled after joining a batch but before settlement, reverse both the "initiate_payment" and "prepare_batch" entries, or post new entries with opposite amounts.

If a batch reverses after settlement, the bank or payment processor will create a reversal transaction. Sync this transaction and reconcile it with a Ledger Entry that posts opposite amounts to the original settlement. You'll also need to reverse the "initiate_payment" entries so that the payment clearing accounts are pending again.

e. Pending payments

#

You can monitor pending payments in two ways:

  • unreconciled transactions for payments you've received but not yet recorded
  • clearing accounts for recorded payments waiting for the next step in a workflow
Unreconciled Txs#

Transactions synced from a Link but not yet reconciled to a Ledger Entry represent payments you've received but haven't recorded in your ledger.

Use the unreconciledTxs field to find these payments:

query UnreconciledPayments {
  ledgerAccount(
    ledgerAccount: {
      path: "assets/operating"
      ledger: { ik: "ledger_123" }
    }
  ) {
    unreconciledTxs {
      nodes {
        id
        description
        amount
        externalId
      }
    }
  }
}

See Integrate a bank for more details.

Clearing accounts#

Clearing accounts represent temporary states in payment flows. FRAGMENT tracks the status of each clearing account to help you monitor payments waiting for the next step in a workflow.

Each clearing account has a clearingStatus field that indicates whether the account has a pending balance:

  • pending: The account has a non-zero balance. The payment is waiting for the next step in the workflow.
  • cleared: The account has a zero balance. The payment workflow is complete.

For multi-currency clearing accounts, the status is pending if any currency has a non-zero balance. Once you deploy a Ledger, the clearing property on a Ledger Account is immutable.

Use the clearingStatus filter to find pending payments:

query PendingPayments {
  ledger(ledger: { ik: "ledger_123" }) {
    ledgerAccounts(
      filter: {
        clearingStatus: { equalTo: pending }
      }
    ) {
      nodes {
        path
        clearingStatus
        ownBalance
      }
    }
  }
}

Combine the clearingStatus filter with a path filter to find pending payments for a specific workflow. For non-template clearing accounts, use an exact path match:

query PendingBulkReceivables {
  ledger(ledger: { ik: "ledger_123" }) {
    ledgerAccounts(
      filter: {
        clearingStatus: { equalTo: pending }
        path: { matches: "liabilities/bulk_receivables" }
      }
    ) {
      nodes {
        path
        clearingStatus
        ownBalance
      }
    }
  }
}

For template clearing accounts, use a wildcard in the path:

query PendingWithdrawals {
  ledger(ledger: { ik: "ledger_123" }) {
    ledgerAccounts(
      filter: {
        clearingStatus: { equalTo: pending }
        path: { matches: "liabilities/pending_withdrawals:*" }
      }
    ) {
      nodes {
        path
        clearingStatus
        ownBalance
      }
    }
  }
}

The clearingStatus filter only accepts the path filter as an additional filter. Other filters will return an error.