Affihub
DocsIntegration GuidesCustom Checkout

Custom Checkout

Overview

If you're not using Stripe or Paddle — or need custom attribution logic — you can create commissions directly via the Affihub API. This works with any payment provider, custom checkout, or billing system.

When to use this

  • Payment providers other than Stripe or Paddle (e.g., Razorpay, Lemonsqueezy, Gumroad)
  • Internal billing systems
  • Marketplace transactions
  • Manual sales or invoicing
  • Complex multi-step checkout flows

Step 1: Capture the referral

The tracking script works the same regardless of your payment provider. Install it on your site and read the referral at checkout:

const ref = window.AffiHub.getRef();
// { ref: "partner-slug", program: "prog_xxx" }

Send this data to your server alongside the transaction:

fetch("/api/checkout", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    // ... your checkout data
    affihub_ref: ref?.ref || null,
  }),
});

Step 2: Look up the partner

On your server, after a successful payment, resolve the partner slug to a partner ID:

curl -X GET "https://api.affihub.com/api/v1/partners?search=partner-slug" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

{
  "data": [
    {
      "id": "par_abc123",
      "slug": "partner-slug",
      "email": "partner@example.com",
      "status": "active"
    }
  ]
}

Step 3: Evaluate commission (optional)

If you want to use your flow rules to determine the commission amount, you can query your flows and evaluate them in your application logic. Alternatively, calculate the commission yourself based on your business rules.

The standard formula:

const txnAmountCents = 9900; // $99.00
const commissionPct = 20;    // 20%
const commissionCents = Math.floor(
  (txnAmountCents * Math.round(commissionPct * 100)) / 10000
);
// commissionCents = 1980 ($19.80)

Step 4: Create the commission

Use the events endpoint to log the transaction. The webhook handler uses the same internal attribution function, but for custom integrations you'll create the event directly:

curl -X POST "https://api.affihub.com/api/v1/events" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "transaction_completed",
    "payload": {
      "partner_slug": "partner-slug",
      "txn_amount_cents": 9900,
      "customer_email": "customer@example.com",
      "provider_event_id": "your-unique-transaction-id"
    }
  }'
💡

Always include a unique provider_event_id to prevent duplicate commissions if you retry the request.

Full server-side example (Node.js)

const AFFIHUB_API_KEY = process.env.AFFIHUB_API_KEY;
const BASE_URL = "https://api.affihub.com";
 
async function createCommission({ partnerSlug, txnAmountCents, customerEmail, txnId }) {
  // Look up partner
  const partnerRes = await fetch(
    `${BASE_URL}/api/v1/partners?search=${partnerSlug}`,
    { headers: { Authorization: `Bearer ${AFFIHUB_API_KEY}` } }
  );
  const { data: partners } = await partnerRes.json();
  const partner = partners.find((p) => p.slug === partnerSlug);
 
  if (!partner || partner.status !== "active") {
    console.log("Partner not found or inactive");
    return;
  }
 
  // Create event for attribution
  const eventRes = await fetch(`${BASE_URL}/api/v1/events`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${AFFIHUB_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      type: "transaction_completed",
      payload: {
        partner_slug: partnerSlug,
        txn_amount_cents: txnAmountCents,
        customer_email: customerEmail,
        provider_event_id: txnId,
      },
    }),
  });
 
  const result = await eventRes.json();
  console.log("Commission created:", result);
}
 
// After a successful payment:
createCommission({
  partnerSlug: "john",
  txnAmountCents: 4900,
  customerEmail: "buyer@example.com",
  txnId: `txn_${Date.now()}`,
});

Deduplication

Use the provider_event_id field to ensure idempotency. If you send the same provider_event_id twice, the second request will be ignored. Use your payment system's transaction ID for this field.

Differences from webhook integrations

Feature Stripe/Paddle Webhooks Custom Checkout
Commission creation Automatic You call the API
Signature verification Built-in Not applicable
Flow rule evaluation Automatic Manual or via API
Deduplication By event ID By provider_event_id
Refund handling Automatic (Paddle) You handle it