Track Any Payment Provider With Your Own Backend
Use this guide if you want to integrate Affonso with a payment provider that is not one of our native integrations, or if you prefer to keep the payment logic inside your own backend.
This works well for setups built on providers like PayPal, Razorpay, Lemon Squeezy, Xendit, Flutterwave, Chargebee, Gumroad, or any custom billing stack.
This article is the setup guide. For the exact request and response contracts, use the API docs for Server-Side Tracking, Create Conversion, and Refund Conversion.
The core idea is simple:
- capture the affiliate identifier before checkout
- pass it through your payment flow
- send the final conversion to Affonso from your backend
You do not need to manually calculate commissions. Affonso can calculate the commission automatically through POST /v1/conversions.
If your company already sends business events through Segment, you can also use
POST /v1/sources/segment/events instead of building your own event mapper.
💡 Use POST /v1/conversions for your own backend flow. Use
POST /v1/sources/segment/events only if Segment is already your event hub.
Before You Start
Make sure you have:
- the Affonso tracking script installed on your website
- an Affonso API key with
write:commissionspermission - a backend that receives your payment provider's webhooks
- a stable provider-side event ID for idempotency
To keep affiliate tracking more reliable when ad blockers or privacy tools are present, see Proxy Setup (Optional).
Choose Your Integration Path
Option 1: Your own backend plus POST /v1/conversions
Choose this when:
- you already handle checkout creation in your backend
- you already process provider webhooks yourself
- you want full control over what counts as a conversion
- you are not using Segment as the main source of truth
Option 2: Segment plus POST /v1/sources/segment/events
Choose this when:
- your backend already emits Segment
trackevents - you want a thinner integration layer
- you want Segment
messageIdto act as the idempotency key
For the Segment setup walkthrough, see Segment Track Events. For the exact Segment payload contract, see the Segment API docs.
Recommended Integration Flow
Capture the affiliate identifier before checkout
Affonso sets the affiliate context on the visitor side. In most cases, you will pass that value into checkout as metadata, just like you would with a native provider integration such as Stripe.
const affonsoReferral = window.affonso_referral || "";
await createCheckout({
// your provider-specific checkout payload
metadata: {
affonso_referral: affonsoReferral,
},
});If your provider uses a different field name, map it to the closest metadata or custom-data field it supports.
Examples:
- PayPal: custom ID or custom field
- Razorpay:
notes - Lemon Squeezy:
custom_data - Xendit:
metadata - Flutterwave:
meta
The important part is that your webhook handler can recover the same identifier later.
Handle the successful payment in your backend
When your provider sends a successful payment or order webhook:
- verify the provider webhook signature
- extract the provider event ID
- extract the affiliate identifier from metadata
- extract the customer ID from your payment provider if available
- decide whether this event should create a conversion
That logic is the same pattern you can see in native provider integrations such as Stripe:
- attach affiliate context before checkout
- verify the incoming webhook
- map provider fields into one clean Affonso conversion call
You do not need to reproduce Affonso's internal commission logic in your own code. Your job is only to send the right identifiers and sale context.
Send the conversion to Affonso
Send the final sale to:
POST https://api.affonso.io/v1/conversionsSend one supported identifier plus your sale details. For new integrations,
prefer referral_id when you already know the Affonso referral ID, or
external_user_id when your backend has a stable user ID from your product or
app. Use customer_id when you want to match with the customer ID from your
payment provider or billing system.
Use a stable provider event ID as external_event_id. For the exact supported
fields, see the
Create Conversion API docs.
curl -X POST "https://api.affonso.io/v1/conversions" \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cust_123",
"sale_amount": 99,
"sale_amount_currency": "USD",
"external_event_id": "order_123_paid",
"product_ids": ["pro_monthly"],
"price_ids": ["price_123"],
"is_subscription": true,
"interval": "monthly",
"metadata": {
"provider": "razorpay",
"provider_order_id": "order_123",
"affonso_referral": "ref_abc123"
}
}'Affonso will match the conversion to the referral and calculate the commission when your setup supports it.
This endpoint is idempotent on external_event_id, so safe retries are
possible.
Store the returned conversion ID
Persist the Affonso response id in your own system.
You will need that ID later for:
- refunds
- partial refunds
- reconciliation
- support and debugging
If you only store the upstream provider event ID and not the Affonso transaction ID, refunds become harder to map cleanly.
Mirror refunds and reversals
When your provider sends a refund event, call:
POST https://api.affonso.io/v1/conversions/{id}/refundFor the exact refund request contract, see the Refund Conversion API docs.
Example:
curl -X POST "https://api.affonso.io/v1/conversions/txn_123/refund" \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"amount": 99,
"currency": "USD",
"reason": "customer_requested_refund",
"external_event_id": "refund_123"
}'Use a stable provider refund ID as external_event_id so retries stay safe.
Which Identifier Should You Send?
Use the most stable identifier you already control.
- Use
referral_idwhen you already know the exact Affonso referral - Use
external_user_idwhen Affonso should match against the user ID in your own product or system - Use
customer_idwhen you want to match against the customer ID from your payment provider or billing system - Use
affonso_idonly if your existing integration already uses it
If you send more than one identifier, they must all point to the same referral.
For the full identifier behavior, see the Server-Side Tracking API docs.
What To Be Careful About
1. Do not send every webhook blindly
Only send a conversion when the provider event actually represents a real commercial state change for your business.
Good examples:
- order paid
- invoice paid
- subscription renewed
- one-time purchase completed
Be careful with noisy events such as:
- checkout created
- order updated
- subscription updated without billing impact
2. Keep idempotency stable
external_event_id should come from the upstream provider event, not from a
new random UUID on each retry.
Good examples:
- provider payment ID
- invoice ID plus event type
- refund event ID
Bad example:
- generating a new UUID every time your webhook retries
3. Pass affiliate context early
If you wait until after checkout to reconstruct affiliate attribution, you are already in a weaker position.
The safer pattern is:
- capture affiliate context on landing
- attach it to checkout metadata
- recover it again inside the webhook
4. Keep your mapping layer thin
Your backend should map provider fields into Affonso fields.
It should not copy Affonso's commission engine into your own code.
That means:
- do not manually compute payout logic unless you truly need custom behavior
- do not hardcode program commission rules in your webhook handler
- do let Affonso evaluate the matched incentive
If You Already Use Segment
If your backend already emits Segment track events, you can send them to
Affonso instead of building your own event mapper.
Use the Segment Helpcenter guide for the setup walkthrough, and the Segment API docs for the exact payload contract.
Optional Hardening: Request Signing
Affonso supports optional signed requests for S2S ingestion.
If request signing is enabled in your setup, also send:
X-Affonso-TimestampX-Affonso-Signature
This is an optional hardening step for backend-to-backend traffic. It is not required for every setup.
Troubleshooting Checklist
- Confirm the affiliate identifier is present before checkout
- Confirm your provider keeps that identifier in metadata or custom data
- Confirm your webhook handler reads the same value back out
- Confirm
external_event_idstays stable across retries - Confirm you send at least one supported identifier
- Confirm refund webhooks call the refund endpoint, not a second conversion
- Confirm you store the Affonso conversion transaction ID
When This Approach Is The Right One
This guide is the right approach when:
- you want to use any payment provider with Affonso
- you already have your own backend
- you want a fast custom integration without waiting for a native connector
- you want Affonso to calculate commissions, but you want to control the event ingestion
If you use one of our built-in provider integrations already, prefer the native guide for that provider.


