Skip to main content

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

ProviderCodeCheckoutWebhooks
StripestripeStripe Checkout SessionsStripe-Signature header
AdyenadyenAdyen Sessions APIHMAC-SHA256
GoCardlessgocardlessGoCardless flowWebhook-Signature header
UCPucpUniversal Commerce ProtocolX-UCP-Signature header
ManualmanualNo checkout URLHMAC-SHA256

Payment lifecycle

  PENDING → PROCESSING → SUCCEEDED
                      ↘ FAILED
                      ↘ CANCELED
  SUCCEEDED → REFUNDED

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"
  }'
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

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)

Provider-specific configuration

Set in your environment:
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

Manual payments

For offline payments (bank transfers, checks, etc.), mark a payment as paid directly:
POST /v1/payments/{id}/mark-paid
This updates the payment status to succeeded, records an invoice settlement, and marks the invoice as paid.

Refunds

POST /v1/payments/{id}/refund
Only succeeded payments can be refunded. The payment status changes to refunded.

Payment requests & dunning

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

Dunning campaigns

Dunning campaigns automate payment recovery:
curl -X POST /v1/dunning_campaigns \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "standard",
    "name": "Standard Dunning",
    "max_attempts": 3,
    "days_between_attempts": 3,
    "thresholds": [
      {"currency": "USD", "amount_cents": 1000}
    ]
  }'
The dunning process:
  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

Webhooks

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

API endpoints

MethodPathDescription
GET/v1/paymentsList payments
GET/v1/payments/{id}Get payment details
POST/v1/payments/checkoutCreate checkout session
POST/v1/payments/webhook/{provider}Handle provider webhook
POST/v1/payments/{id}/mark-paidMark as paid (manual)
POST/v1/payments/{id}/refundRefund a payment
DELETE/v1/payments/{id}Delete a pending payment
GET/v1/payment_requestsList payment requests
POST/v1/payment_requestsCreate payment request