Background
⌘K

Custom Backend Payment Provider Integration

Track affiliate conversions from any payment provider with your own backend using Affonso conversions and Segment events.

Silvestro
Written by Silvestro
Updated more than a week ago

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:

  1. capture the affiliate identifier before checkout
  2. pass it through your payment flow
  3. 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:

  1. the Affonso tracking script installed on your website
  2. an Affonso API key with write:commissions permission
  3. a backend that receives your payment provider's webhooks
  4. 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 track events
  • you want a thinner integration layer
  • you want Segment messageId to 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.

1

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.

2

Handle the successful payment in your backend

When your provider sends a successful payment or order webhook:

  1. verify the provider webhook signature
  2. extract the provider event ID
  3. extract the affiliate identifier from metadata
  4. extract the customer ID from your payment provider if available
  5. 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.

3

Send the conversion to Affonso

Send the final sale to:

POST https://api.affonso.io/v1/conversions

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

4

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.

5

Mirror refunds and reversals

When your provider sends a refund event, call:

POST https://api.affonso.io/v1/conversions/{id}/refund

For 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_id when you already know the exact Affonso referral
  • Use external_user_id when Affonso should match against the user ID in your own product or system
  • Use customer_id when you want to match against the customer ID from your payment provider or billing system
  • Use affonso_id only 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:

  1. capture affiliate context on landing
  2. attach it to checkout metadata
  3. 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-Timestamp
  • X-Affonso-Signature

This is an optional hardening step for backend-to-backend traffic. It is not required for every setup.

Troubleshooting Checklist

  1. Confirm the affiliate identifier is present before checkout
  2. Confirm your provider keeps that identifier in metadata or custom data
  3. Confirm your webhook handler reads the same value back out
  4. Confirm external_event_id stays stable across retries
  5. Confirm you send at least one supported identifier
  6. Confirm refund webhooks call the refund endpoint, not a second conversion
  7. 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.

Was this article helpful?

If you still need help, our support team is here for you.

Contact Support
bg

Ready to Scale Your SaaS?

Affonso is the easiest way to launch your own affiliate program. We take care of the technical stuff, so you can focus on growing your business.