Promoter
What it is
Lets an event have promoters — people who share a referral link and earn a commission on baskets (checkouts) placed through it. Planners configure a commission rate and cap per event; promoters get a dashboard of their deal and referrals, and can cash out.
Core concepts
- Basket, not item — a basket is one complete checkout (
orderReference), which may contain multiple items. Commission is calculated once per basket, on the basket total, never per line item. - Event promoter settings (
event_promoter_settings):commissionPercentage,capAmount(max commission on a single basket),isEnabled. Configured per event by the planner. - Promoter code: the promoter's user ID, attached to a basket at checkout (via the referral link) as
promoterCode. - Promoter vault wallet: a single shared wallet (
PVB_PROMOTER_VAULT_WALLET_ID) that all commissions land in before a promoter cashes out.
How it works
Commission calculation
baseAmount = discount applied ? (basketOriginalAmount - basketDiscountAmount) : basketTotalPrice
rawCommission = baseAmount × (commissionPercentage / 100)
finalCommission = min(rawCommission, capAmount)
A ₦20,000 basket (3 items) at 10%/₦1,500 cap earns the promoter ₦1,500 once — not ₦2,000, and not three separate commissions for the three items.
Transfer flow
- Basket completes (all items saved,
orderReferencecreated, transaction committed). PromoterCommissionService.processCommissionForBasket(...)calculates the commission per the formula above.- Commission is transferred asynchronously from the event's PartyBank wallet to the promoter vault wallet — this happens after the checkout transaction has already committed, and a transfer failure does not roll back or block the customer's order. It's logged and can be reconciled manually via the
orderReferenceId. - Integration points:
GuestsService(RSVP baskets) andMinishopService(minishop baskets, viasetImmediate()after commit) both call into the same commission service — so the rule is consistent across both purchase paths.
Promoter-facing endpoints
GET /v1/promoter/event/:eventId/deal— commission rate, cap, and this promoter's referral link for the event.GET /v1/promoter/event/:eventId/referrals— paginated list of orders placed through this promoter's link.POST /v1/promoter/event/:eventId/request-access/GET .../request-access— request to become a promoter for an event, and check the request's status.POST /v1/promoter/event/:eventId/cashout— cash out accumulated commission. Requiresuser.isPromoter; automatically cashes out all outstanding commission rather than a partial amount.
Key business rules & edge cases
- Commission is silently skipped, not erred, when: there's no
promoterCodeon the order, no promoter settings exist for the event, settings are disabled,capAmountis 0, or the event's PartyBank wallet is missing itsthirdPartyReference. If a promoter says "I didn't get paid," check these five conditions before assuming a bug. - Commission is computed on what the customer actually paid — if a discount code was applied, the base is the post-discount amount, not the original list price.
- A transfer failure never blocks the customer's checkout. This is a deliberate tradeoff: reliability of the core purchase flow wins over commission accuracy, with manual reconciliation as the fallback (search logs for the
orderReferenceId).
Related code
src/modules/promoter/promoter.routes.ts,promoter.controller.ts,promoter-commission.service.tssrc/modules/guests/guests.service.ts,src/modules/minishop/minishop.service.ts— commission integration pointsevent_promoter_settings,event_shop_orderstables
Not yet in the generated OpenAPI spec — this module hasn't been through the pilot OpenAPI pass.