Payments
Overview
Section titled “Overview”BoxBilling supports multiple payment providers through a pluggable adapter pattern. Each provider implements the same interface for checkout session creation, webhook verification, and payment status parsing.
Supported providers
Section titled “Supported providers”| Provider | Code | Checkout | Webhooks |
|---|---|---|---|
| Stripe | stripe | Stripe Checkout Sessions | Stripe-Signature header |
| Adyen | adyen | Adyen Sessions API | HMAC-SHA256 |
| GoCardless | gocardless | GoCardless flow | Webhook-Signature header |
| UCP | ucp | Universal Commerce Protocol | X-UCP-Signature header |
| Manual | manual | No checkout URL | HMAC-SHA256 |
Payment lifecycle
Section titled “Payment lifecycle” PENDING → PROCESSING → SUCCEEDED ↘ FAILED ↘ CANCELED SUCCEEDED → REFUNDEDPayment properties
Section titled “Payment properties”| Field | Type | Description |
|---|---|---|
id | uuid | Unique payment identifier |
invoice_id | uuid | Linked invoice |
customer_id | uuid | Customer who made the payment |
amount_cents | string | Payment amount in cents |
currency | string | 3-letter ISO currency code |
status | enum | pending, processing, succeeded, failed, refunded, canceled |
provider | string | Payment provider code |
provider_payment_id | string? | Provider’s payment identifier |
provider_checkout_id | string? | Provider’s checkout session ID |
provider_checkout_url | string? | Checkout URL for customer redirect |
failure_reason | string? | Reason for payment failure |
payment_metadata | object? | Arbitrary metadata from the provider |
completed_at | datetime? | When the payment completed |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
Creating a checkout session
Section titled “Creating a checkout session”To collect payment for an invoice, create a checkout session:
curl -X POST /v1/payments/checkout \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "invoice_id": "invoice-uuid", "provider": "stripe", "success_url": "https://yourapp.com/success", "cancel_url": "https://yourapp.com/cancel" }'The provider field defaults to stripe if not specified.
Response:
{ "payment_id": "payment-uuid", "checkout_url": "https://checkout.stripe.com/c/pay/...", "provider": "stripe", "expires_at": "2025-01-15T12:00:00Z"}Redirect the customer to checkout_url to complete payment.
Webhook handling
Section titled “Webhook handling”Payment providers send webhook notifications to update payment status. Configure the webhook URL per provider:
POST /v1/payments/webhook/{provider}The system:
- Verifies the webhook signature using the provider’s secret
- Parses the event payload
- Updates the payment status
- Updates the linked invoice status
- Sends BoxBilling webhooks (
payment.succeeded,payment.failed)
Provider-specific configuration
Section titled “Provider-specific configuration”Set in your environment:
stripe_api_key=sk_live_...stripe_webhook_secret=whsec_...Webhook events handled:
checkout.session.completed→succeededpayment_intent.succeeded→succeededpayment_intent.payment_failed→failedcheckout.session.expired→canceled
adyen_api_key=AQE...adyen_merchant_account=YourMerchantadyen_webhook_hmac_key=...adyen_environment=test # or "live"Event codes handled:
AUTHORISATION→succeededorfailedCAPTURE→succeededCANCELLATION→canceledREFUND→refunded
gocardless_access_token=...gocardless_webhook_secret=...gocardless_environment=sandbox # or "live"ucp_base_url=https://api.ucp.comucp_api_key=...ucp_webhook_secret=...ucp_merchant_id=...Recording manual payments
Section titled “Recording manual payments”For offline payments (bank transfers, checks, etc.), record a manual payment:
curl -X POST /v1/payments/record \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "invoice_id": "invoice-uuid", "amount": 5000, "currency": "USD", "reference": "CHK-12345", "notes": "Check payment received 2025-01-15" }'| Field | Type | Required | Description |
|---|---|---|---|
invoice_id | uuid | Yes | Invoice this payment is for |
amount | number | Yes | Payment amount (must be > 0) |
currency | string | No | 3-letter ISO code, defaults to USD |
reference | string | No | External reference (check number, wire transfer ID) |
notes | string | No | Optional notes about the payment |
You can also mark an existing pending payment as paid:
POST /v1/payments/{id}/mark-paidThis updates the payment status to succeeded, records an invoice settlement, and marks the invoice as paid.
Retrying failed payments
Section titled “Retrying failed payments”Failed payments can be retried:
POST /v1/payments/{id}/retryThis re-attempts payment processing through the original provider.
Refunds
Section titled “Refunds”POST /v1/payments/{id}/refundOnly succeeded payments can be refunded. Supports partial refunds by specifying an amount:
{ "amount": 2500}If amount is omitted, a full refund is issued. The payment status changes to refunded.
Refund statuses
Section titled “Refund statuses”| Status | Description |
|---|---|
pending | Refund initiated, awaiting provider confirmation |
succeeded | Refund completed |
failed | Refund failed |
Payment methods
Section titled “Payment methods”Payment methods store saved cards, bank accounts, or other payment instruments for a customer. They are linked to a specific payment provider.
Payment method properties
Section titled “Payment method properties”| Field | Type | Description |
|---|---|---|
id | uuid | Unique identifier |
customer_id | uuid | Customer who owns this method |
provider | string | Payment provider code |
provider_payment_method_id | string | Provider’s identifier for this method |
type | string | Method type (e.g., card, bank_account, sepa_debit) |
is_default | boolean | Whether this is the customer’s default method |
details | object | Provider-specific details (last 4 digits, brand, etc.) |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
Creating a payment method
Section titled “Creating a payment method”curl -X POST /v1/payment_methods/ \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "customer-uuid", "provider": "stripe", "provider_payment_method_id": "pm_1234567890", "type": "card", "is_default": true, "details": {"brand": "visa", "last4": "4242"} }'Setup sessions
Section titled “Setup sessions”To let customers add payment methods through a provider-hosted flow, create a setup session:
curl -X POST /v1/payment_methods/setup \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "customer-uuid", "provider": "stripe", "success_url": "https://yourapp.com/methods/success", "cancel_url": "https://yourapp.com/methods/cancel" }'Response:
{ "setup_id": "seti_...", "setup_url": "https://checkout.stripe.com/setup/...", "provider": "stripe", "expires_at": "2025-01-15T12:00:00Z"}Setting a default payment method
Section titled “Setting a default payment method”POST /v1/payment_methods/{payment_method_id}/set_defaultPayment requests & dunning
Section titled “Payment requests & dunning”Payment requests group overdue invoices for a customer and drive the dunning campaign process.
Payment request properties
Section titled “Payment request properties”| Field | Type | Description |
|---|---|---|
id | uuid | Unique identifier |
customer_id | uuid | Customer with overdue invoices |
dunning_campaign_id | uuid? | Linked dunning campaign |
amount_cents | string | Total amount across all grouped invoices |
amount_currency | string | Currency code |
payment_status | string | Current status |
payment_attempts | integer | Number of collection attempts made |
ready_for_payment_processing | boolean | Whether the request is ready to process |
invoices | array | List of linked invoices |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
Batch payment requests
Section titled “Batch payment requests”Create payment requests for all customers with overdue invoices in one call:
POST /v1/payment_requests/batchResponse:
{ "total_customers": 25, "created": 22, "failed": 3, "results": [ { "customer_id": "uuid", "customer_name": "Acme Corp", "payment_request_id": "uuid", "invoice_count": 3, "amount_cents": "150000", "amount_currency": "USD", "status": "created" } ]}Payment attempt history
Section titled “Payment attempt history”View the full attempt history for a payment request:
GET /v1/payment_requests/{request_id}/attemptsEach attempt entry includes:
| Field | Type | Description |
|---|---|---|
timestamp | datetime | When the attempt occurred |
action | string | Action taken |
old_status | string? | Status before the attempt |
new_status | string? | Status after the attempt |
attempt_number | integer? | Attempt sequence number |
details | object? | Additional attempt details |
Dunning campaigns
Section titled “Dunning campaigns”Dunning campaigns automate payment recovery with configurable retry logic.
Campaign properties
Section titled “Campaign properties”| Field | Type | Description |
|---|---|---|
id | uuid | Unique identifier |
code | string | Unique campaign code |
name | string | Display name |
description | string? | Campaign description |
max_attempts | integer | Maximum retry attempts (default: 3) |
days_between_attempts | integer | Days between retries (default: 3) |
bcc_emails | array | Email addresses to BCC on dunning notifications |
status | enum | active or inactive |
thresholds | array | Currency-specific minimum amount thresholds |
created_at | datetime | Creation timestamp |
updated_at | datetime | Last update timestamp |
Creating a campaign
Section titled “Creating a campaign”curl -X POST /v1/dunning_campaigns/ \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "code": "standard", "name": "Standard Dunning", "description": "Default recovery campaign", "max_attempts": 3, "days_between_attempts": 3, "bcc_emails": ["finance@yourcompany.com"], "status": "active", "thresholds": [ {"currency": "USD", "amount_cents": 1000} ] }'Campaign preview & analytics
Section titled “Campaign preview & analytics”Preview how a campaign would execute before activating it:
POST /v1/dunning_campaigns/{campaign_id}/previewView execution history for a campaign:
GET /v1/dunning_campaigns/{campaign_id}/execution_historyView the campaign timeline:
GET /v1/dunning_campaigns/{campaign_id}/timelinePerformance stats
Section titled “Performance stats”Get aggregate performance statistics across all dunning campaigns:
GET /v1/dunning_campaigns/performance_statsResponse:
{ "total_campaigns": 3, "active_campaigns": 2, "total_payment_requests": 156, "succeeded_requests": 120, "failed_requests": 18, "pending_requests": 18, "recovery_rate": 0.77, "total_recovered_amount_cents": "4500000", "total_outstanding_amount_cents": "1350000"}The dunning process
Section titled “The dunning process”- The
check_dunningtask finds overdue invoices above the campaign threshold - Groups invoices by customer and currency
- Creates a
PaymentRequestwith the total amount - The
process_payment_requeststask processes eligible requests - On success, linked invoices are marked as paid
- On failure, the system retries based on
max_attemptsanddays_between_attempts
Webhooks
Section titled “Webhooks”| Event | Trigger |
|---|---|
payment.created | Payment record created |
payment.succeeded | Payment completed |
payment.failed | Payment failed |
payment_request.created | Dunning payment request created |
payment_request.payment_succeeded | Dunning payment succeeded |
payment_request.payment_failed | Dunning payment failed |
API endpoints
Section titled “API endpoints”Payments
Section titled “Payments”| Method | Path | Description |
|---|---|---|
GET | /v1/payments/ | List payments |
GET | /v1/payments/{payment_id} | Get payment details |
POST | /v1/payments/checkout | Create checkout session |
POST | /v1/payments/record | Record manual payment |
POST | /v1/payments/webhook/{provider} | Handle provider webhook |
POST | /v1/payments/{payment_id}/mark-paid | Mark as paid |
POST | /v1/payments/{payment_id}/refund | Refund a payment |
POST | /v1/payments/{payment_id}/retry | Retry failed payment |
DELETE | /v1/payments/{payment_id} | Delete a pending payment |
Payment methods
Section titled “Payment methods”| Method | Path | Description |
|---|---|---|
GET | /v1/payment_methods/ | List payment methods |
POST | /v1/payment_methods/ | Create payment method |
POST | /v1/payment_methods/setup | Create setup session |
GET | /v1/payment_methods/{payment_method_id} | Get payment method |
DELETE | /v1/payment_methods/{payment_method_id} | Delete payment method |
POST | /v1/payment_methods/{payment_method_id}/set_default | Set default payment method |
Payment requests
Section titled “Payment requests”| Method | Path | Description |
|---|---|---|
GET | /v1/payment_requests/ | List payment requests |
POST | /v1/payment_requests/ | Create payment request |
POST | /v1/payment_requests/batch | Batch create for all overdue customers |
GET | /v1/payment_requests/{request_id} | Get payment request |
GET | /v1/payment_requests/{request_id}/attempts | Get payment attempt history |
Dunning campaigns
Section titled “Dunning campaigns”| Method | Path | Description |
|---|---|---|
POST | /v1/dunning_campaigns/ | Create dunning campaign |
GET | /v1/dunning_campaigns/ | List dunning campaigns |
GET | /v1/dunning_campaigns/performance_stats | Get performance stats |
GET | /v1/dunning_campaigns/{campaign_id} | Get dunning campaign |
PUT | /v1/dunning_campaigns/{campaign_id} | Update dunning campaign |
DELETE | /v1/dunning_campaigns/{campaign_id} | Delete dunning campaign |
GET | /v1/dunning_campaigns/{campaign_id}/execution_history | Get execution history |
POST | /v1/dunning_campaigns/{campaign_id}/preview | Preview campaign execution |
GET | /v1/dunning_campaigns/{campaign_id}/timeline | Get campaign timeline |