Bill
External Payments

Recording External Invoice Payments

Track payments made outside Payload to maintain complete invoice records


External payments allow you to record invoice payments that were made outside the Payload platform—such as checks, wire transfers, cash, or payments processed through other systems. By recording these external payments as payment allocations, you maintain complete and accurate invoice tracking, update invoice statuses automatically, and preserve a full audit trail of all payments regardless of how they were received.

Prerequisites

Before recording external payments, ensure you have:


When to Use External Payments


Use external payment recording when customers pay through channels outside Payload.

Benefits of external payment tracking

  • Complete Records: Maintain full payment history across all payment channels
  • Audit Trail: Store detailed information about external payment sources
  • Reconciliation: Match external payments to invoices programmatically
  • Customer Service: Provide customers with complete payment history

Common use cases

  • Check Payments: Customer mails a check to your address
  • Wire Transfers: Customer initiates bank wire or bank transfer directly
  • Cash Payments: Customer pays in person with cash at point of sale

Recording External Payments


Track payments from any external source using the same payment allocation pattern.

import payload
 
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Record a check payment received outside Payload
 
invoice = pl.Invoice.get('inv_abc123')
 
# Create external payment allocation for the check
allocation = pl.PaymentAllocation.create(
    invoice_id=invoice.id,
    amount=invoice.totals['balance_due'],
    external_payment=True,  # Indicates payment was made externally
    attrs={
        'payment_method': 'check',
        'check_number': 'CHK-5678',
        'received_date': '2024-01-15',
    },
)
 
print(f'External check payment recorded: {allocation.id}')
print(f'Invoice ID: {allocation.invoice_id}')
print(f'Amount: {allocation.amount}')
 
# Check updated invoice status
updated_invoice = pl.Invoice.get(invoice.id)
print(f'Invoice status: {updated_invoice.status}')
print(f'Remaining balance: {updated_invoice.totals['balance_due']}')

What happens when external payments are recorded:

  1. Payment allocation is created with external_payment: true
  2. Invoice balance is reduced by the payment amount
  3. Invoice status updates to paid or partially_paid
  4. Payment appears in invoice's payments array

Using the attrs field:

The attrs object stores custom payment details. Include any fields relevant to your payment type:

  • payment_method - Type of payment (check, wire, cash, etc.)
  • check_number / reference_number - Payment reference
  • received_date - When payment was received
  • Any other details specific to your tracking needs

Querying External Payments


Find and analyze external payment records.

List all external payments

# Get all external payments
external_payments = pl.PaymentAllocation.filter_by(
    q="external_payment == true",
    order_by="desc(created_at)"
).all()
 
print(f"Found {len(external_payments)} external payments")
 
for payment in external_payments:
    print(f"Payment: {payment.id}")
    print(f"  Invoice: {payment.invoice_id}")
    print(f"  Amount: ${payment.amount}")
    print(f"  Method: {(payment.attrs or {}).get('payment_method')}")
    print(f"  Date: {(payment.attrs or {}).get('received_date')}")

Filter by payment method

# Find all check payments
check_payments = pl.PaymentAllocation.filter_by(
    q='external_payment == true && attrs[payment_method] == "check"',
    order_by="desc(created_at)"
).all()
 
print(f"Total check payments: {len(check_payments)}")
 
total_check_amount = 0
for payment in check_payments:
    total_check_amount += payment.amount
    print(f"Check {(payment.attrs or {}).get('check_number')}: ${payment.amount}")
 
print(f"Total received via check: ${total_check_amount}")

Query by date range

# Find external payments in a date range
start_date = "2024-01-01"
end_date = "2024-01-31"
 
monthly_externals = pl.PaymentAllocation.filter_by(
    q=f'external_payment == true && created_at >= "{start_date}" && created_at <= "{end_date}"',
    order_by="asc(created_at)"
).all()
 
print(f"External payments in January: {len(monthly_externals)}")
 
# Group by payment method
by_method = {}
for payment in monthly_externals:
    method = (payment.attrs or {}).get("payment_method", "unknown")
    by_method[method] = by_method.get(method, 0) + payment.amount
 
print("Breakdown by method:")
for method, total in by_method.items():
    print(f"  {method}: ${total}")

Find external payments for specific invoice

# Get all external payments for an invoice
invoice = pl.Invoice.get("inv_abc123")
 
external_payments = [
    p for p in invoice.payments
    if p.external_payment is True
]
 
print(f"External payments for {invoice.id}:")
for payment in external_payments:
    print(f"  {(payment.attrs or {}).get('payment_method')}: ${payment.amount}")

Handling External Payment Corrections


Update or void external payments when corrections are needed.

Update payment details

# Update external payment information
payment = pl.PaymentAllocation.get("item_abc123")
 
payment.update(
    attrs={
        "check_number": "CHK-5679"  # Corrected check number
    }
)
 
print("Payment details updated")

Void external payment

If a payment needs to be voided (check bounced, wire reversed, etc.):

# Delete the external payment allocation
payment = pl.PaymentAllocation.get("item_abc123")
 
print("Voiding external payment:")
print(f"  Invoice: {payment.invoice_id}")
print(f"  Amount: ${payment.amount}")
print(f"  Method: {(payment.attrs or {}).get('payment_method')}")
 
# Delete the allocation - this restores the invoice balance
payment.delete()
 
print("Payment allocation voided - invoice balance restored")
 
# Verify invoice balance updated
invoice = pl.Invoice.get(payment.invoice_id)
print("Updated invoice balance:", invoice.totals["balance_due"])

Schema Reference


Fields relevant to external payment recording:

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.
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.
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]+$
attrs
object
Custom attributes for extending the resource with additional key-value pairs. Maximum length is 255 characters when serialized.

Invoice Payment Tracking

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.
array
Collection of all payment allocations applied to this invoice. Each allocation represents a payment (or portion of a payment) that reduces the invoice balance. Expanded by default to show complete payment history for the invoice.

Next Steps

Enhance your external payment tracking with additional features


Process Payments Through Payload

Process payments programmatically with Payment API for automated billing workflows, enable autopay for recurring billing with Automatic Payments, and send payment links to customers with Payment Requests for self-service collection.

Manage Invoice Workflow

Generate invoices for customers with Creating Invoices, understand the complete invoice lifecycle with Invoice Statuses, and automate recurring billing with Billing Schedules.

Track and Report Payments

Monitor payment events in real-time with Webhook Events, track payment metrics and trends with Reporting Overview, and analyze payment data with Transaction Reports.


Related articles