Skip to content

Payments

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.

ProviderCodeCheckoutWebhooks
StripestripeStripe Checkout SessionsStripe-Signature header
AdyenadyenAdyen Sessions APIHMAC-SHA256
GoCardlessgocardlessGoCardless flowWebhook-Signature header
UCPucpUniversal Commerce ProtocolX-UCP-Signature header
ManualmanualNo checkout URLHMAC-SHA256
PENDING → PROCESSING → SUCCEEDED
↘ FAILED
↘ CANCELED
SUCCEEDED → REFUNDED
FieldTypeDescription
iduuidUnique payment identifier
invoice_iduuidLinked invoice
customer_iduuidCustomer who made the payment
amount_centsstringPayment amount in cents
currencystring3-letter ISO currency code
statusenumpending, processing, succeeded, failed, refunded, canceled
providerstringPayment provider code
provider_payment_idstring?Provider’s payment identifier
provider_checkout_idstring?Provider’s checkout session ID
provider_checkout_urlstring?Checkout URL for customer redirect
failure_reasonstring?Reason for payment failure
payment_metadataobject?Arbitrary metadata from the provider
completed_atdatetime?When the payment completed
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp

To collect payment for an invoice, create a checkout session:

Terminal window
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.

Payment providers send webhook notifications to update payment status. Configure the webhook URL per provider:

POST /v1/payments/webhook/{provider}

The system:

  1. Verifies the webhook signature using the provider’s secret
  2. Parses the event payload
  3. Updates the payment status
  4. Updates the linked invoice status
  5. Sends BoxBilling webhooks (payment.succeeded, payment.failed)

Set in your environment:

Terminal window
stripe_api_key=sk_live_...
stripe_webhook_secret=whsec_...

Webhook events handled:

  • checkout.session.completedsucceeded
  • payment_intent.succeededsucceeded
  • payment_intent.payment_failedfailed
  • checkout.session.expiredcanceled

For offline payments (bank transfers, checks, etc.), record a manual payment:

Terminal window
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"
}'
FieldTypeRequiredDescription
invoice_iduuidYesInvoice this payment is for
amountnumberYesPayment amount (must be > 0)
currencystringNo3-letter ISO code, defaults to USD
referencestringNoExternal reference (check number, wire transfer ID)
notesstringNoOptional notes about the payment

You can also mark an existing pending payment as paid:

Terminal window
POST /v1/payments/{id}/mark-paid

This updates the payment status to succeeded, records an invoice settlement, and marks the invoice as paid.

Failed payments can be retried:

Terminal window
POST /v1/payments/{id}/retry

This re-attempts payment processing through the original provider.

Terminal window
POST /v1/payments/{id}/refund

Only 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.

StatusDescription
pendingRefund initiated, awaiting provider confirmation
succeededRefund completed
failedRefund failed

Payment methods store saved cards, bank accounts, or other payment instruments for a customer. They are linked to a specific payment provider.

FieldTypeDescription
iduuidUnique identifier
customer_iduuidCustomer who owns this method
providerstringPayment provider code
provider_payment_method_idstringProvider’s identifier for this method
typestringMethod type (e.g., card, bank_account, sepa_debit)
is_defaultbooleanWhether this is the customer’s default method
detailsobjectProvider-specific details (last 4 digits, brand, etc.)
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp
Terminal window
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"}
}'

To let customers add payment methods through a provider-hosted flow, create a setup session:

Terminal window
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"
}
Terminal window
POST /v1/payment_methods/{payment_method_id}/set_default

Payment requests group overdue invoices for a customer and drive the dunning campaign process.

FieldTypeDescription
iduuidUnique identifier
customer_iduuidCustomer with overdue invoices
dunning_campaign_iduuid?Linked dunning campaign
amount_centsstringTotal amount across all grouped invoices
amount_currencystringCurrency code
payment_statusstringCurrent status
payment_attemptsintegerNumber of collection attempts made
ready_for_payment_processingbooleanWhether the request is ready to process
invoicesarrayList of linked invoices
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp

Create payment requests for all customers with overdue invoices in one call:

Terminal window
POST /v1/payment_requests/batch

Response:

{
"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"
}
]
}

View the full attempt history for a payment request:

Terminal window
GET /v1/payment_requests/{request_id}/attempts

Each attempt entry includes:

FieldTypeDescription
timestampdatetimeWhen the attempt occurred
actionstringAction taken
old_statusstring?Status before the attempt
new_statusstring?Status after the attempt
attempt_numberinteger?Attempt sequence number
detailsobject?Additional attempt details

Dunning campaigns automate payment recovery with configurable retry logic.

FieldTypeDescription
iduuidUnique identifier
codestringUnique campaign code
namestringDisplay name
descriptionstring?Campaign description
max_attemptsintegerMaximum retry attempts (default: 3)
days_between_attemptsintegerDays between retries (default: 3)
bcc_emailsarrayEmail addresses to BCC on dunning notifications
statusenumactive or inactive
thresholdsarrayCurrency-specific minimum amount thresholds
created_atdatetimeCreation timestamp
updated_atdatetimeLast update timestamp
Terminal window
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}
]
}'

Preview how a campaign would execute before activating it:

Terminal window
POST /v1/dunning_campaigns/{campaign_id}/preview

View execution history for a campaign:

Terminal window
GET /v1/dunning_campaigns/{campaign_id}/execution_history

View the campaign timeline:

Terminal window
GET /v1/dunning_campaigns/{campaign_id}/timeline

Get aggregate performance statistics across all dunning campaigns:

Terminal window
GET /v1/dunning_campaigns/performance_stats

Response:

{
"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"
}
  1. The check_dunning task finds overdue invoices above the campaign threshold
  2. Groups invoices by customer and currency
  3. Creates a PaymentRequest with the total amount
  4. The process_payment_requests task processes eligible requests
  5. On success, linked invoices are marked as paid
  6. On failure, the system retries based on max_attempts and days_between_attempts
EventTrigger
payment.createdPayment record created
payment.succeededPayment completed
payment.failedPayment failed
payment_request.createdDunning payment request created
payment_request.payment_succeededDunning payment succeeded
payment_request.payment_failedDunning payment failed
MethodPathDescription
GET/v1/payments/List payments
GET/v1/payments/{payment_id}Get payment details
POST/v1/payments/checkoutCreate checkout session
POST/v1/payments/recordRecord manual payment
POST/v1/payments/webhook/{provider}Handle provider webhook
POST/v1/payments/{payment_id}/mark-paidMark as paid
POST/v1/payments/{payment_id}/refundRefund a payment
POST/v1/payments/{payment_id}/retryRetry failed payment
DELETE/v1/payments/{payment_id}Delete a pending payment
MethodPathDescription
GET/v1/payment_methods/List payment methods
POST/v1/payment_methods/Create payment method
POST/v1/payment_methods/setupCreate 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_defaultSet default payment method
MethodPathDescription
GET/v1/payment_requests/List payment requests
POST/v1/payment_requests/Create payment request
POST/v1/payment_requests/batchBatch create for all overdue customers
GET/v1/payment_requests/{request_id}Get payment request
GET/v1/payment_requests/{request_id}/attemptsGet payment attempt history
MethodPathDescription
POST/v1/dunning_campaigns/Create dunning campaign
GET/v1/dunning_campaigns/List dunning campaigns
GET/v1/dunning_campaigns/performance_statsGet 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_historyGet execution history
POST/v1/dunning_campaigns/{campaign_id}/previewPreview campaign execution
GET/v1/dunning_campaigns/{campaign_id}/timelineGet campaign timeline