Bill
Paying Invoices
Paying Invoices via API

Paying Invoices via API

Process invoice payments programmatically with payment allocations


Invoice payments connect transactions to invoices, automatically reducing the invoice balance and updating its status. The Payload API uses payment allocations to link transactions to invoices, enabling full payments, partial payments, and even split payments across multiple invoices in a single transaction. You can also record external payments made outside the platform for complete invoice tracking.

Prerequisites

Before paying invoices, ensure you have:


When to Pay Invoices via API


Programmatic invoice payments are ideal for automated billing and integration scenarios.

Benefits of API payments

  • Automation: Process invoice payments automatically based on business logic
  • Batch Processing: Pay multiple invoices in a single transaction
  • Partial Payments: Accept installment payments toward large invoices
  • Payment Allocation: Split a single payment across multiple invoices
  • Custom Workflows: Integrate with internal systems and approval processes

Common use cases

  • Subscription Billing: Automatically charge invoices on due dates
  • Account Receivables: Process customer payments as they arrive
  • Payment Plans: Accept partial payments on installment schedules
  • Bulk Processing: Pay multiple outstanding invoices at once
  • External Payments: Record check or wire payments in the system
  • Integration: Connect to accounting or ERP systems
  • Customer Self-Service: Build payment portals and applications

Paying an Invoice in Full


Process a complete payment for an invoice's remaining balance.

import payload
 
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Pay an invoice in full
invoice = pl.Invoice.get('inv_abc123')
 
if invoice.status != 'paid':
    transaction = pl.Transaction.select('*', 'invoice_allocations').create(
        type='payment',
        amount=invoice.totals['balance_due'],  # Pay full balance
        sender={
            'account_id': invoice.payer['account_id'],
            'method_id': 'pm_card_789',  # Payment method to charge
        },
        invoice_allocations=[
            {
                'invoice_id': invoice.id,
                'amount': invoice.totals['balance_due'],
            }
        ],
    )
 
    print(f'Payment processed: {transaction.id}')
    print(f'Transaction status: {transaction.status}')
    print(f'Invoice paid: {transaction.invoice_allocations[0].invoice_id}')

This payment transaction includes:

  • type: 'payment' to indicate a payment transaction
  • amount matching the invoice's balance_due
  • payer.account_id identifying who is making the payment
  • payer.method_id specifying which payment method to charge
  • invoice_allocations array linking this payment to the invoice

The payment allocation contains:

  • invoice_id referencing the invoice being paid
  • amount specifying how much to apply to this invoice

What happens:

  1. Payment method is charged for the specified amount
  2. Transaction is created and processed
  3. Payment allocation links transaction to invoice
  4. Invoice balance is reduced by the payment amount
  5. Invoice status updates to paid (if balance reaches zero)

Understanding Payment Allocations


Payment allocations are the mechanism that connects transactions to invoices.

What are payment allocations?

A payment allocation represents a portion of a transaction's amount being applied to a specific invoice. Each allocation contains:

{
  invoice_id: "inv_abc123",  // Which invoice receives this payment
  amount: 149.00,            // How much to apply to this invoice
  transaction_id: "txn_xyz", // Which transaction provided the funds (auto-set)
  external_payment: false    // Whether payment was made outside Payload
}

How allocations work

When you create a payment transaction with invoice_allocations:

  1. Transaction processes - Payment method is charged
  2. Allocations created - One allocation per invoice in the array
  3. Invoice updated - Balance is reduced by allocation amount
  4. Status changes - Invoice moves to partially_paid or paid

Allocation rules

  • Amount validation: Allocation amount cannot exceed invoice's balance_due
  • Multiple allocations: One transaction can have many allocations
  • Same payer: All invoices must belong to the same payer account
  • Total must match: Sum of allocation amounts must equal transaction amount
  • Immutable: Allocations cannot be modified after creation

Viewing allocations

Allocations are automatically included when retrieving invoices:

invoice = pl.Invoice.get("inv_abc123")
 
# View all payments applied to this invoice
for allocation in invoice.payments:
    print("Amount:", allocation.amount)
    print("Transaction:", allocation.transaction_id)
    print("Date:", allocation.created_at)
    print("External?:", allocation.external_payment)

Making Partial Payments


Accept installment payments or partial amounts toward an invoice balance.

import payload
 
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Make a partial payment on an invoice
invoice = pl.Invoice.get('inv_abc123')
partial_amount = 50.00  # Pay $50 of the total balance
 
transaction = pl.Transaction.create(
    type='payment',
    amount=partial_amount,
    sender={
        'account_id': invoice.payer['account_id'],
        'method_id': 'pm_card_789',
    },
    invoice_allocations=[
        {'invoice_id': invoice.id, 'amount': partial_amount}  # Only pay partial amount
    ],
)
 
print(f'Partial payment processed: {transaction.id}')
print(f'Amount paid: {transaction.amount}')
 
# Check updated invoice status
updated_invoice = pl.Invoice.get(invoice.id)
print(f'Invoice status: {updated_invoice.status}')  # Will be 'partially_paid'
print(f'Remaining balance: {updated_invoice.totals['balance_due']}')
print(f'Total paid so far: {updated_invoice.totals['paid']}')

Partial payment behavior:

  • Invoice status becomes partially_paid (not paid)
  • totals.paid increases by the payment amount
  • totals.balance_due decreases by the payment amount
  • Additional payments can be made until balance reaches zero
  • Each payment creates a new allocation in the invoice's payments array

Use cases for partial payments:

  • Payment plans and installment billing
  • Deposits and progressive payments
  • Customer cash flow management
  • Milestone-based payments
  • Split payments from multiple payment methods

Paying Multiple Invoices


Process a single payment transaction that pays several invoices at once.

import payload
 
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Pay multiple invoices in a single transaction
invoice1 = pl.Invoice.get('inv_abc123')
invoice2 = pl.Invoice.get('inv_def456')
invoice3 = pl.Invoice.get('inv_ghi789')
 
# Calculate total payment amount
total_amount = (
    invoice1.totals['balance_due']
    + invoice2.totals['balance_due']
    + invoice3.totals['balance_due']
)
 
transaction = pl.Transaction.select('*', 'invoice_allocations').create(
    type='payment',
    amount=total_amount,
    sender={
        'account_id': invoice1.payer['account_id'],  # Same payer for all invoices
        'method_id': 'pm_card_789',
    },
    invoice_allocations=[
        {'invoice_id': invoice1.id, 'amount': invoice1.totals['balance_due']},
        {'invoice_id': invoice2.id, 'amount': invoice2.totals['balance_due']},
        {'invoice_id': invoice3.id, 'amount': invoice3.totals['balance_due']},
    ],
)
 
print(f'Payment processed: {transaction.id}')
print(f'Total amount: {transaction.amount}')
print(f'Invoices paid: {len(transaction.invoice_allocations)}')
 
# Display allocation details
for index, allocation in enumerate(transaction.invoice_allocations, 1):
    print(f'Invoice {index}: {allocation.invoice_id} - ${allocation.amount}')

Multiple invoice allocation rules:

  • All invoices must belong to the same payer account
  • Transaction amount must equal sum of all allocation amounts
  • Each invoice receives its allocated portion
  • Invoices update independently based on their allocations
  • One invoice can be paid in full while another receives partial payment

Benefits of batched payments:

  • Reduce payment processing fees (single transaction vs multiple)
  • Simplify customer experience (one charge vs many)
  • Improve reconciliation (one transaction to track)
  • Better for customer statement clarity

Common scenarios:

  • Paying all outstanding invoices for a customer
  • Monthly account settlement
  • Bulk invoice processing
  • Account balance clearance

Using Default Payment Methods


Charge a customer's default payment method without specifying method_id.

Auto-selecting payment methods

If you don't specify payer.method_id, Payload will attempt to use the payer account's default payment method:

transaction = pl.Transaction.create(
    type="payment",
    amount=149.0,
    sender={
        "account_id": "acct_customer123",
        # No method_id specified - uses default
    },
    invoice_allocations=[{"invoice_id": "inv_abc123", "amount": 149.0}]
)

Default payment method behavior

  • Has default: Transaction uses the default method automatically
  • No default: Transaction will fail with an error
  • Method invalid: Transaction fails if default method is expired or invalid
  • Autopay: Default method is used for automatic invoice payments

Setting default payment methods

Make a payment method the default for an account:

payment_method = pl.PaymentMethod.get("pm_card_789")
payment_method.update(
    account_defaults={
        "paying": "payments"
    }
)

Handling Payment Errors


Implement proper error handling for failed invoice payments.

Common payment failures

invoice = pl.Invoice.get("inv_abc123")
 
try:
    transaction = pl.Transaction.create(
        type="payment",
        amount=invoice.totals["balance_due"],
        sender={
            "account_id": invoice.payer["account_id"],
            "method_id": "pm_card_789"
        },
        invoice_allocations=[{"invoice_id": invoice.id, "amount": invoice.totals["balance_due"]}]
    )
 
    print("Payment successful:", transaction.id)
except Exception as error:
    print("Payment failed:", error)
 
    # Handle payment failures
    # - Notify customer of payment failure
    # - Request different payment method
    # - Suggest partial payment if applicable
    # - Log error for investigation
    # - Implement retry logic for transient failures

Invoice validation errors

  • Invoice already paid: Check status !== 'paid' before payment
  • Invalid allocation amount: Cannot exceed balance_due
  • Invoice in draft: Cannot pay draft invoices
  • Invoice closed: Cannot pay closed invoices

Payment method issues

  • Card expired: Update payment method before attempting payment
  • Insufficient funds: Suggest partial payment or different card
  • Payment declined: Request alternative payment method
  • Method not found: Verify method_id exists for payer

Schema Reference


The following fields are available for invoice payments:

Transaction Payment Fields

type
enum[string]Immutable
The type of transaction being processed. This determines the direction and nature of funds movement, such as payment collection, refund issuance, credit disbursement, or account funding operations.
Values: payment, deposit, withdraw, refund, payout
amount
number (double)
The monetary amount for this transaction in the currency of the processing account. This value is always positive and represents the total value being transferred, collected, or refunded. The amount is rounded to two decimal places for display.
sender
object
Information about the sender party in this transaction, including the account and payment method from which funds are being sent or debited. For payment transactions, this is the account being charged. For payout transactions, this is the processing account sending funds.
accountAccount
object
The expanded account object for the sender. When included, this provides the full details of the account sending funds in this transaction.
Visibility: explicit
account_id ID of Account
stringImmutable
The unique identifier of the account sending or providing funds for this transaction. This is an optional field that references the account object that owns the sender payment method. (ID prefix: acct)
Pattern: ^acct_[A-Za-z0-9]+$
object
The expanded payment method object used by the sender. When included, this provides the full details of the payment method from which funds are being debited.
method_id ID of PaymentMethod
stringImmutable
The unique identifier of the payment method used by the sender. This references the payment method objectfrom which funds are being pulled or debited for this transaction. If not provided the account default will be used. (ID prefix: pm)
Pattern: ^pm_[A-Za-z0-9]+$

Payment Allocation Fields

invoice_id ID of Invoice
string
The unique identifier of the invoice that this payment is being allocated to. The payment amount will be applied toward this invoice's balance, reducing the amount_due. Required for all payment allocations. (ID prefix: inv)
Pattern: ^inv_[A-Za-z0-9]+$
amount
number (double)
The payment amount being allocated to the invoice. This is how much of the payment is being applied toward this specific invoice. Cannot exceed the invoice's remaining balance_due. Required for all payment allocations.
transaction_id ID of Transaction
string
The unique identifier of the transaction that processed this payment. Links the payment allocation to the actual payment transaction. Required when external_payment is false. Must be null when external_payment is true (for payments made outside the platform). (ID prefix: txn)
Pattern: ^txn_[A-Za-z0-9]+$
external_payment
boolean
Flag indicating whether this payment was made outside the Payload platform. Set to true for payments made via cash, check, wire transfer, or other external methods that are being manually recorded. Set to false (or omit) for payments processed through Payload. When true, transaction_id must be null. When false, transaction_id is typically required.
attrs
object
Custom attributes for extending the resource with additional key-value pairs. Maximum length is 255 characters when serialized.

Invoice Payment Status

When payments are applied, invoices transition through these statuses:

status
enum[string]
The current status of this invoice. Possible values: "draft" (not yet published/sent), "open" (published and awaiting payment, same as unpaid), "unpaid" (published with no payments), "partially_paid" (some payment received but balance remains), "paid" (fully paid), or "closed" (manually closed/cancelled). Status transitions are restricted - for example, paid invoices cannot be modified.
Values: draft, open, unpaid, partially_paid, paid, closed, sync
totals
object
Financial summary of this invoice, including subtotal, tax, total amounts, and payment status. All fields are read-only and automatically calculated based on the line items and payments associated with this invoice. Provides a quick overview of the invoice's financial state.
balance_due
number (double)Read-only
The remaining unpaid balance on this invoice (total - paid). Read-only field that represents the outstanding amount the payer still owes. When this reaches zero, the invoice is fully paid.
paid
number (double)Read-only
The total amount that has been successfully paid toward this invoice. Read-only field that updates as payments are received and allocated to this invoice. Use this to track how much has been collected.
subtotal
number (double)Read-only
The subtotal of all line items on this invoice before tax is applied. This is the sum of all line item amounts (value * qty for each item). Read-only field that is automatically calculated based on the invoice items. Represents the pre-tax total.
tax
number (double)Read-only
The total tax amount for this invoice. Calculated by applying tax rates to taxable line items. Read-only field that updates when line items or tax rates change. This amount is added to the subtotal to get the total amount due.
total
number (double)Read-only
The total amount due for this invoice (subtotal + tax). This is the full amount the payer owes. Read-only field that is automatically calculated from line items and tax. Represents the complete invoice amount before any payments are applied.

Next Steps

Enhance your billing workflow with automated payment collection


Automate Payment Collection

Configure autopay for recurring billing with Automatic Payments to charge invoices automatically on their due date, create hosted payment pages for customers with Payment Portal for self-service payment management, and send payment request links with Payment Links for customer-initiated payments.

Manage Invoice Workflow

Track invoice progress with Invoice Statuses to understand the complete invoice lifecycle from draft through paid, automate workflows with payment events using Webhook Events for real-time payment notifications, and monitor payment processing with Transaction Status to track payment completion and failures.

Handle Payment Scenarios

Accept installment payments with Partial Payments for large invoices and payment plans, process multiple payments at once with Bulk Payments for batch payment processing, and record offline payments with External Payments for check and wire transfers.


Related articles