Billing providers

Connect Stripe, Paddle, Lemon Squeezy, Polar, Chargebee, Shopify, Recurly, Braintree, WooCommerce or BigCommerce so payments, refunds and disputes flow into commissions automatically.

Embed
Preview of the card
<a href="https://referalli.com/docs/billing-providers"><img src="https://referalli.com/docs/_api/public/orgs/07f432f4-34b9-46a7-aaad-c0e395d2e4f9/spaces/2bf4f614-3630-4fbf-85f9-5ecafa6b7d97/pages/bcedb6ea-8271-4a54-ad02-f5cb6e3f161e/social-card.svg?v=3e7ae33eec8b" alt="Billing providers" width="600" height="315"></a>

A billing connection gives your billing provider a webhook URL on Referalli. The provider sends its normal webhooks there; Referalli verifies the signature, translates each event into a canonical billing event, and processes commissions. You connect your own provider account; Referalli holds no platform billing keys.

Create a connection

curl -X POST https://referalli.com/v1/billing/connections \
  -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -d '{"provider": "stripe", "name": "Stripe live", "webhookSecret": "whsec_from_stripe_dashboard"}'
{
  "id": "bcon_01JB...",
  "provider": "stripe",
  "webhookSecret": "whsec_from_stripe_dashboard",
  "webhookUrl": "/webhooks/billing/bcon_01JB..."
}

provider is one of stripe, paddle, lemonsqueezy, polar, chargebee, shopify, recurly, braintree, woocommerce, bigcommerce or generic. Point the provider at the full URL:

https://referalli.com/webhooks/billing/bcon_01JB...

webhookSecret must match what the provider signs with. If you omit it, Referalli generates one (whsec_…) and returns it once; enter that in providers that let you choose the secret (Lemon Squeezy, WooCommerce, BigCommerce, generic).

Set a provider-issued secret

Stripe, Paddle, Polar, Shopify and Recurly issue the signing secret themselves, after you register the webhook URL. Create the connection first, register its URL with the provider, then store the secret the provider shows you:

curl -X PUT https://referalli.com/v1/billing/connections/bcon_01JB.../secret \
  -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -d '{"webhookSecret": "whsec_from_stripe_dashboard"}'
# { "id": "bcon_01JB...", "provider": "stripe", "ok": true }

webhookSecret is at least 8 characters; for Chargebee it is the username:password pair, and for Braintree the public_key:private_key pair. The call replaces the stored secret, requires the owner or admin role, is audited, and returns 404 for an unknown connection. Until the right secret is stored, the provider's webhooks fail with 401.

Add API credentials (Recurly, BigCommerce)

Recurly's JSON webhooks and BigCommerce's webhooks carry only ids, so Referalli reads each invoice, payment or order from the provider's API with your own credentials. Store them once per connection:

curl -X PUT https://referalli.com/v1/billing/connections/bcon_01JB.../credentials \
  -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -d '{"apiKey": "your_recurly_private_api_key", "region": "us"}'
# { "id": "bcon_01JB...", "provider": "recurly", "ok": true }

Provider

Fields

Recurly

apiKey (a private API key), optional region: us (default) or eu

BigCommerce

apiToken (a store API account token with read access to Orders), optional storeHash if your webhooks don't carry producer

The credentials are stored encrypted, used only to read the objects a webhook names, and never returned. Until they're set, that connection's webhooks get 400, so the provider retries them. GET /v1/billing/connections reports needsCredentials and hasCredentials for each connection. Requires owner or admin; audited.

Match customer ids

Payments are matched to referrals by customer id. Either call associate with the customer id this provider puts in its webhooks (listed below), or link the provider's customer to your own id with referally_customer_ref metadata. See Linking billing customers.

Provider reference

Stripe

  • Signature: Stripe-Signature header (t=…,v1=…), HMAC-SHA256 of {t}.{body}. Use the endpoint's signing secret from the Stripe dashboard as webhookSecret.

  • customerRef: the Stripe customer id (cus_…).

  • Events used: invoice.paid, customer.created, customer.updated, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, charge.refunded, charge.dispute.created.

  • Invoice metadata expansion: "true" marks expansion revenue. A subscription created in trialing status records a trial conversion and pays any trial bounty.

  • referally_customer_ref in customer, subscription or invoice metadata links the cus_… id to the referral filed under that id. customer.created and customer.updated are only used for this link.

Paddle

  • Signature: Paddle-Signature header (ts=…;h1=…), HMAC-SHA256 of {ts}:{body}.

  • customerRef: Paddle customer_id.

  • Events used: transaction.completed, subscription.created, subscription.canceled, adjustment.created.

  • A subscription created in trialing status records a trial. referally_customer_ref in custom_data links the customer.

Lemon Squeezy

  • Signature: X-Signature header, hex HMAC-SHA256 of the body.

  • customerRef: the customer_id attribute.

  • Events used: order_created, subscription_payment_success, subscription_created, subscription_cancelled, subscription_expired, order_refunded, subscription_payment_refunded.

  • Refunds and chargebacks: Lemon Squeezy is the merchant of record and sends no dispute event; a lost chargeback arrives as a refund. A refunded order Lemon Squeezy marks fraudulent is clawed back in full, like a dispute. Its refunded_amount is the running total, so each partial refund only reverses what earlier refunds haven't.

  • A subscription created on_trial records a trial. referally_customer_ref in checkout custom data (checkout[custom][referally_customer_ref], delivered as meta.custom_data) links the customer.

Polar

  • Signature: webhook-id, webhook-timestamp and webhook-signature headers; the signature is v1, plus the base64 HMAC-SHA256 of {id}.{timestamp}.{body}, keyed with the connection secret as given.

  • customerRef: Polar customer_id.

  • Events used: order.created, subscription.created, subscription.canceled, subscription.revoked, refund.created.

  • referally_customer_ref in the object's metadata links the customer. Set it as checkout metadata.

Chargebee

  • Authentication: Chargebee does not sign webhooks. Protect the endpoint with HTTP Basic auth in Chargebee, and set webhookSecret to the same username:password pair.

  • customerRef: the Chargebee customer id.

  • Events used: payment_succeeded, subscription_created, subscription_cancelled, payment_refunded, dispute_created.

  • Disputes: dispute_created claws back the invoice the disputed payment paid (the transaction's linked_invoices), in full, like a Stripe dispute. A won dispute doesn't reinstate the commission; correct it through the ledger if you want to.

  • A subscription created in_trial records a trial. Chargebee events carry no custom metadata, so associate customers with the Chargebee customer id.

Shopify

  • Signature: X-Shopify-Hmac-Sha256 header, base64 HMAC-SHA256 of the body. Use your app's webhook secret.

  • customerRef: the order's customer id, falling back to the customer's email.

  • Events used: order webhooks (orders/create, orders/paid) become invoice_paid; refund webhooks (refunds/create) become refund.

  • Discount codes on the order are checked against your partner coupons. A match attributes the buyer to that partner at purchase time, with no click needed.

Recurly

  • Signature: recurly-signature header: <unix ms timestamp>,<hex signature>[,<hex signature>], each an HMAC-SHA256 of {timestamp}.{body} keyed with the endpoint's secret key (two signatures for 24 hours after you regenerate the key). A timestamp more than 5 minutes off is refused. Use JSON webhooks; XML webhooks aren't signed.

  • Credentials: a private API key (above).

  • customerRef: the account code. Set it to your own customer id when you create the Recurly account.

  • Events used: charge_invoice.paid becomes invoice_paid, payment.refunded becomes refund (against the invoice the original payment paid), subscription.created, subscription.canceled and subscription.expired.

  • A coupon code on the invoice attributes the buyer to the partner who owns it.

Braintree

  • Signature: Braintree posts form-encoded bt_signature and bt_payload. Set webhookSecret to your API key pair as public_key:private_key; the signature is an HMAC-SHA1 of bt_payload, keyed with the SHA-1 of the private key.

  • customerRef: the Braintree customer id on the transaction.

  • Events used: subscription_charged_successfully becomes invoice_paid (the subscription's latest transaction), subscription_went_active becomes sub_created, subscription_canceled and subscription_expired become sub_canceled, dispute_opened becomes a full clawback, and a settled credit transaction (transaction_settled) becomes a refund.

  • Braintree sends no webhook for card refunds that settle immediately. Report those as refund events through the generic connector.

  • A custom field referally_customer_ref on the transaction links the customer to the id they were referred under.

WooCommerce

  • Signature: X-WC-Webhook-Signature header, base64 HMAC-SHA256 of the body. Create the webhook under WooCommerce → Settings → Advanced → Webhooks with topic Order updated (and Order created), and set its secret to webhookSecret.

  • customerRef: the WooCommerce customer id, or the billing email for guest checkouts.

  • Events used: a paid order (status processing or completed with a payment date) becomes invoice_paid; each entry in the order's refunds becomes its own refund.

  • Coupon codes on the order attribute the buyer to the partner who owns the coupon. A referally_customer_ref order meta field links the buyer to the id they were referred under.

BigCommerce

  • Authentication: BigCommerce doesn't sign webhooks. Create the webhook with POST /v3/hooks and headers: { "X-Referally-Secret": "<webhookSecret>" }; Referalli checks that header.

  • Credentials: a store API token with read access to Orders (above).

  • customerRef: the BigCommerce customer id, or the billing email for guest orders.

  • Events used: store/order/statusUpdated and store/order/created for an order in a paid state (Awaiting Fulfillment, Awaiting Shipment, Partially Shipped, Shipped or Completed) become invoice_paid; store/order/refund/created becomes refund.

  • Coupon codes on the order attribute the buyer to the partner who owns the coupon.

Generic

For any other system, or your own backend. Event types are invoice_paid, sub_created, sub_updated, sub_canceled, refund, dispute and customer_updated. Send customerRef as the id you attributed the signup under, or set linkedCustomerRef to it. See the generic conversion API.

Responses and errors

A processed webhook returns 200 with { "received": n, "results": [...] }. Events of types Referalli does not use are accepted and ignored. Redelivered events report "duplicate": true.

Status

Cause

400

A Recurly or BigCommerce connection has no API credentials yet, so the event can't be read. The provider retries it.

401

Signature (or Basic auth, or BigCommerce's secret header) did not verify. Check that webhookSecret matches the provider.

404

Unknown connection id, or the connection is not active.

GET /v1/billing/events?customerRef=… shows what was received for a customer. An event with no customer id at all (one Referalli can't attribute) is acknowledged with 200 and skipped, so the provider doesn't retry it.

Manage a connection

Call

Purpose

GET /v1/billing/connections

Your connections: status, webhookUrl, needsCredentials, hasCredentials and rotation.

GET /v1/billing/connections/{id}

One connection.

PATCH /v1/billing/connections/{id}

Pause ("status": "paused": its webhook URL answers 404 until resumed), resume ("active") or rename (name).

POST /v1/billing/connections/{id}/rotate-secret

For connections where Referalli issued the secret: a new webhookSecret, returned once. The old one keeps verifying for 24 hours (previousSecretExpiresAt) while you update the provider.

PUT /v1/billing/connections/{id}/secret

For providers that issue the secret themselves (Stripe, Paddle, Polar, Shopify): roll it there, then set it here. The old one also keeps verifying for 24 hours.

DELETE /v1/billing/connections/{id}

Delete for good: the webhook URL stops answering and the secrets and API credentials are wiped. Payments and commissions it recorded stay.

All of these need owner or admin and are audited. The same actions are on Settings → Billing providers.

Updated