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.
External payment allocations are created directly without a transaction, using
external_payment: true. They reduce the invoice balance and update the invoice status just
like regular Payload payments, but represent funds received through external channels.
Prerequisites
Before recording external payments, ensure you have:
Create invoices
Invoices must exist in unpaid or partially_paid status to record external payments.
Understand payment allocations
Learn how payment allocations link payments to invoices and track payment history.
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
Payment Allocation Types: External payments use the same payment allocation mechanism as
Payload-processed payments. The only difference is the external_payment: true flag and the
absence of a transaction_id. Both types appear in the invoice's payment history.
Recording External Payments
Track payments from any external source using the same payment allocation pattern.
What happens when external payments are recorded:
- Payment allocation is created with
external_payment: true - Invoice balance is reduced by the payment amount
- Invoice status updates to
paidorpartially_paid - Payment appears in invoice's
paymentsarray
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
Flexible Tracking: Use the attrs field to store any payment details relevant to your
business. This works for checks, wire transfers, cash, third-party processors, or any other
payment type.
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}")Payment Analytics: Use external payment queries to generate reports on payment method preferences, track cash flow by payment type, and identify trends in how customers prefer to pay. This data can inform business decisions about payment options to offer.
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"])Payment Corrections: Deleting an external payment allocation automatically restores the invoice balance and updates the invoice status. Always add notes explaining why payments were deleted for audit trail purposes.
Schema Reference
Fields relevant to external payment recording:
Payment Allocation Fields
invoice_id ID of Invoice^inv_[A-Za-z0-9]+$amountexternal_paymenttransaction_id ID of Transaction^txn_[A-Za-z0-9]+$attrsInvoice Payment Tracking
statusdraft, open, unpaid, partially_paid, paid, closed, synctotalsbalance_duepaidsubtotaltaxtotalpaymentsPaymentAllocationNext 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
- Payment API - Process invoice payments via API
- Creating Invoices - Build invoices with line items
- Payment Allocations - How payment allocations work
- Invoice Statuses - Invoice lifecycle management
- Webhook Events - Automate payment workflows
- Payment Allocations API Reference - Complete API documentation