---
title: "Affiliate & Referral Enablement"
description: "Dual-rail referral economics — vendor-funded ERP commissions plus platform-funded on-chain token rewards, with shared attribution and audit"
locale: "en"
---
# Affiliate & Referral Enablement

Ring Platform reconciles **legacy vendor-commission scaffolding** with the **refcodes** growth module. One attribution cookie drives two payout rails: vendors fund fiat-side referral commission through ERP settlements; the platform funds referrer **token** rewards via an on-chain minter. Both rails share `orderReference` for cross-audit.

  **Referrers earn tokens.** **Vendors fund** their share of referral commission from settlement. **Buyers** see a referral badge at checkout when attributed. **Operators** approve fiat-path mints, run cron for the approved queue, and review commissions at `/admin/store/commissions`.

> **Deep dives:** [Referral codes (refcodes)](/docs/features/refcodes.md) · [Refcodes architecture](/docs/architecture/refcodes.md) · [ERP commissions](/docs/features/erp/commissions.md)

## Dual-rail economics

| Rail | Who pays | Mechanism | When it runs |
|------|----------|-----------|--------------|
| **ERP (vendor-funded)** | Vendor net payout | `calculateCommission()` → `settlements.metadata.commissionBreakdown.referralCommission` | Paid store order (WayForPay or credit) |
| **Refcodes (platform-funded)** | Platform treasury / mint authority | `ReferralRewardService` → `ReferralRewards.payReferral` on Polygon | After order paid (fiat: post-approval; credit: immediate) |

Both rails use **`resolveReferralCommissionPercent`** (`features/store/lib/referral-commission.ts`) — product override → merchant config → platform default (`5%`) → `REFERRAL_REWARD_PERCENT` env.

  **In scope:** store orders and membership upgrades. **Out of scope (this iteration):** news promotion referrals, referee checkout discounts (referrer-only incentive).

```mermaid
flowchart TB
    subgraph Edge["Attribution"]
        Ref["?ref=CODE"]
        Cookie["ring_ref + ring_ref_visible"]
        Signup["users.data.referredBy on first sign-in"]
    end

    subgraph Order["Paid order / membership"]
        ORD["orders + orderReference"]
        ATTR["resolveOrderReferral guards"]
    end

    subgraph RailA["ERP rail — vendor-funded"]
        SET["recordSettlementsForPaidOrder"]
        BRK["commissionBreakdown.referralCommission"]
        ASSIST["erp_sales_assists + stock movement metadata"]
    end

    subgraph RailB["Refcodes rail — platform token"]
        RW["referral_rewards ledger"]
        MINT["reward-minter → ReferralRewards"]
        NOTIFY["REFERRAL_REWARD_MINTED notification"]
    end

    Ref --> Cookie
    Cookie --> Signup
    Cookie --> ATTR
    Signup --> ATTR
    ATTR --> ORD
    ORD --> SET --> BRK
    ORD --> ASSIST
    ORD --> RW --> MINT --> NOTIFY
    ORD -.->|same orderReference| BRK
    ORD -.->|same orderReference| RW
```

## Attribution lifecycle

**Land with `?ref=`** — `proxy.ts` sets httpOnly `ring_ref` and client-readable `ring_ref_visible` (30 days, first-touch).

**Visit beacon** — `ReferralAttributionEffect` (public layout) calls `POST /api/refcodes/track`; `visitDaily` buckets power dashboard stats (28-day retention).

**Signup persistence** — Auth.js `signIn` for new users runs `persistSignupReferralAttribution` so `users.data.referredBy` survives cookie expiry (membership upgrades without a store order).

**Checkout** — Review step shows `ReferralCheckoutBadge`; order APIs return `referralApplied` + `referralCode`; credit checkout shows a toast; WayForPay stashes a sessionStorage flash for the processing page.

**Payment success** — Store webhook or credit path writes settlements (ERP rail) and `referral_rewards` (token rail). Membership webhook calls `ReferralRewardService.onMembershipPaid` when `referredBy` is set.

## Operator surfaces

- **[Referrer dashboard](/docs/features/refcodes.md)** — `/refcodes` — share links, reward history, visit stats (total / today / 7d / 28d).

- **[Admin refcodes](/docs/features/refcodes.md)** — `/admin/refcodes` — approve fiat rewards, batch mint, platform visit aggregates.

- **[Admin commissions](/docs/features/erp/commissions.md)** — `/admin/store/commissions` — settlement ledger, referral breakdown, process due payouts.

- **[Architecture](/docs/architecture/refcodes.md)** — Cookie → ledger → contract integration map and idempotency keys.

## Cross-rail audit

Every attributed paid event should carry the same **`orderReference`** (PaymentConductor string) on:

| Record | Field |
|--------|-------|
| `settlements` | `orderId` / metadata |
| `erp_sales_assists` | `orderId` |
| `referral_rewards` | `orderReference` (unique index) |
| On-chain | `keccak256(orderReference)` in `ReferralRewards.paidOrders` |

Smoke: `scripts/smoke-erp-referral-pipeline.cts`, `scripts/smoke-growth-pipelines.cts`, `scripts/smoke-refcodes-http.cts` (require `DB_BACKEND_MODE` + `NODE_OPTIONS=--conditions=react-server` — see `scripts/run-all-smokes.sh`).

## Operations checklist

| Step | Action |
|------|--------|
| **1. Migrations** | Apply `005_refcodes_schema.sql` then `007_settlements_schema.sql` (`apply-refcodes-migrations-dev.sh`, `apply-erp-migrations-dev.sh`). |
| **2. Env** | Fill **REFERRAL CODES** and **CRON_SECRET** in `env.local.template`; deploy ReferralRewards proxy; grant token mint to proxy. |
| **3. Cron** | Schedule `GET /api/cron/refcodes-mint` with `Authorization: Bearer $CRON_SECRET` (up to 20 `approved` rewards per run). |
| **4. Verify** | Referred order → `settlements` with `referralCommission` + `erp_sales_assists` + `referral_rewards` sharing `orderReference`. |

  Run `./scripts/run-all-smokes.sh` (sets `DB_BACKEND_MODE` and `NODE_OPTIONS=--conditions=react-server`). Targeted: `smoke-erp-referral-pipeline.cts`, `smoke-growth-pipelines.cts`, `smoke-refcodes-http.cts`.

## Related documentation

  
- [features/refcodes](/docs/features/refcodes.md) — Deep-dive: user/admin UI, contract deploy, and env vars for token rewards.

  
- [features/erp/commissions](/docs/features/erp/commissions.md) — Same-workflow: vendor-funded settlement hierarchy and payout modes.

  
- [features/erp/inventory](/docs/features/erp/inventory.md) — Depends-on: paid commitSaleForOrder runs beside settlement on store orders.

  
- [features/payment-conductor](/docs/features/payment-conductor.md) — Prerequisite: webhooks and orderReference contract.

  
- [features/store](/docs/features/store.md) — Prerequisite: checkout and vendor flows feed referred orders.
