> ## Documentation Index
> Fetch the complete documentation index at: https://ownpay.org/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> OwnPay is licensed under AGPL-3.0 and is completely free - no licensing fees.
> Production docs URL: https://ownpay.org/docs - append .md to any page URL for clean markdown.
> OwnPay requires PHP 8.3+, MySQL/MariaDB, and Redis.
> MCP server available at https://ownpay.org/docs/mcp for programmatic documentation queries.
> Use root-relative links (e.g. /quickstart) for internal navigation - do NOT include /docs prefix.
> Plugin development: consult /developer/plugin-types/ pages for correct interfaces and manifests.

# Double-Entry Ledger - Built-in Accounting for Payments

> OwnPay's double-entry bookkeeping records balanced journal entries per brand for audit-ready reconciliation, transfers, fees, and financial reports.

How does OwnPay track every money movement with accounting-grade precision? OwnPay uses a double-entry ledger bookkeeping engine for bulletproof financial audit readiness. All payments, fees, and transfers are recorded using balanced debits and credits.

## Ledger tables

* `op_ledger_accounts` - Account definitions (Cash, Merchant Payable, Platform Fee Revenue)
* `op_ledger_transactions` - Journal entries linking to payment events
* `op_ledger_entries` - Individual debit and credit line items

## Journal balance constraint

Every ledger entry must post balanced debits and credits. The system validates using `bccomp()` at 4-decimal precision and throws `InvalidArgumentException` on unbalanced journals.

## Account directionality

Balances adjust according to standard GAAP accounting rules:

| Account Type                 | Debits       | Credits      |
| ---------------------------- | ------------ | ------------ |
| Asset / Expense              | Increase (+) | Decrease (-) |
| Liability / Equity / Revenue | Decrease (-) | Increase (+) |

## Brand isolation

Ledger accounts are strictly bound to a brand/merchant context:

```php theme={null}
$account = $ledgerRepo->findOrCreateAccount($name, $type, $currency, $merchantId);
```

## Double-post prevention

The system uses `SELECT ... FOR UPDATE` on `(merchant_id, reference_type, reference_id, description)` to guard against double-posting.

## Reconciliation

### Daily reconciliation

1. Compare OwnPay transactions with gateway
2. Verify amounts match
3. Check for discrepancies
4. Report any mismatches

### Reconciliation tool

```bash theme={null}
php public/index.php admin/reconcile --merchant=YOUR_MERCHANT_ID
```

## Best practices

* Never manually modify `op_ledger_accounts.balance`
* Always go through `LedgerService` for ledger operations
* Run daily balance verification checks
* Monitor the `BalanceVerificationJob` alerts

## Further reading

* [Balance verification](/user-guide/reports-finance/balance-verification) - Run reconciliation audits from the admin dashboard
* [Reports](/user-guide/reports-finance/reports) - Generate financial reports and analytics
* [Transactions](/user-guide/payments/transactions) - View individual payment details and ledger entries


## Related topics

- [Ledger and Accounting - Double-Entry Journal Entries](/docs/user-guide/payments/ledger.md)
- [Features and Capabilities](/docs/resources/features.md)
- [OwnPay Architecture: PHP Core, Middleware, and Plugins](/docs/resources/architecture.md)
- [Glossary - Payment Terms and OwnPay Terminology Reference](/docs/resources/glossary.md)
- [Node.js SDK - TypeScript-First Payment Integration](/docs/developer/integration/nodejs.md)
