Skip to content

Commission engine

The most predictable commission engine in the category.

Per-product rates, per-category rates, per-tier rates, per-affiliate overrides, flat amounts, time-bounded windows. Built so the math still holds 90 days later when a refund finally comes through.

The cascade

Most-specific rule wins.

Every order line passes through a deterministic resolver:

  1. affiliate — specific affiliate, applies to all their orders
  2. product — specific WC/Shopify product id
  3. category — any product in that category
  4. tier — affiliate's current tier
  5. global — merchant default

Within the same specificity, higher priority wins. Within the same priority, latest starts_at wins. The engine writes the winning rule's rate and basis onto the commission row, so rule changes never reach back into history.

// Per-product 25% rule
{
  scope: 'product',
  scope_ref: '99', // WC product id
  basis: 'subtotal',
  rate: 0.25,
  priority: 100,
  starts_at: '2026-04-01T00:00:00Z',
  ends_at: '2026-04-30T23:59:59Z'
}

// Engine resolution
const winning = pickWinningCommissionSample(
  computeOrderCommission({ rules, defaultRule, context, lines })
);

// Persisted on the commission row
{
  rate: 0.25,           // ← from the winning rule
  basis: 'subtotal',    // ← also persisted
  amount: 25.00,
  rule_id_or_default: 'p99'
}

Refund clawback

The decision tree we wish every platform had.

Pending → void

Commission is still in the hold window. Safe to void silently. Caller can manually create a smaller one if you process a partial refund.

Approved → negative-amount sibling

Already approved but not yet paid. Insert a negative-amount clawback row that nets out at the next payout cycle. Original row stays for auditability.

Paid → manual review

Money has left the building. Surface to merchant for resolution; don't silently claw back from the next payout.

Compare with most other platforms which simply "void everything" on a refund — losing the audit trail and frequently surprising affiliates.

Tiers + auto-promotion

Affiliates promote themselves.

Define tier promotion rules — e.g. revenue ≥ $10k in 30 days → Gold tier. The daily TZ-aware evaluator runs every merchant in their local timezone, picks the rule with the highest target tier the affiliate qualifies for, and updates their tier_id (audited + emitted as affiliate.tier_promoted outbound webhook).

  • Yes Metrics: revenue_30d, orders_30d, clicks_30d, commissions_paid_lifetime
  • Yes Multiple rules can target the same tier; engine picks highest threshold
  • Yes No silent demotions — only strictly higher tiers
  • Yes Ranks by target_tier.sort_order so adjacent tiers don't flip-flop

FAQ

Commission engine questions.

What happens to the commission rate when a refund hits 90 days later?+
The winning rule's rate + basis are persisted on the commission row at creation time. So even if you delete or edit the rule afterwards, refund clawback math is exact.
How does specificity work?+
affiliate (50) > product (40) > category (30) > tier (20) > global (10). Within the same specificity, higher priority wins. Time-bounded windows (starts_at / ends_at) gate every rule.
Can I set a commission rate for a single affiliate?+
Yes — scope=affiliate, scope_ref=<affiliate_id>. They can also have a custom_commission JSON on the affiliate row that applies to every order.
Are flat-amount commissions supported?+
Yes — basis="flat" with flat_amount. Useful for "$5 per signup" affiliate programs.

Get the commission math right from day one.

Try Cobalz free for 14 days. Per-product rules, MLM, refund clawback — all included.