Payment Flow
Purpose
The technical narrative of how a sale gets reported, billed, and paid out — complements 01. Money Flow's business-level description.
REVERSED 2026-08-07
This doc previously described a Paddle transaction created by SellVia at checkout. Superseded — SellVia no longer processes the original sale (01. Money Flow). This doc now covers: (1) how a merchant's sale report is received and accepted, (2) how periodic billing actually charges the merchant, (3) how creator payouts are released.
Flow
1. Attribution & sale reporting
Follower clicks AffiliateLink → SellVia logs the click, redirects to merchant's own site
with tracking parameters
Merchant's integration (webhook, pixel, or manual) reports the sale back to SellVia:
POST /webhooks/merchant-sales (07. Webhooks — this is now an active MVP endpoint,
not the deferred v2 item it previously was)
2. Acceptance
Reported sale checked against 04. Fraud Prevention's rules (velocity, plausibility
against the attribution window, merchant reporting pattern)
Accepted → commission + platform fee calculated (01. Commission Engine), added to
the merchant's open Billing Cycle ledger
Rejected → flagged for Admin review, not silently dropped
3. Periodic billing
Scheduled Celery job (02. Background Jobs) closes each merchant's Billing Cycle
on schedule
~~Paddle charges the merchant's card on file for the cycle total~~ — superseded, see
Update (2026-08-23) below: merchant is billed via Swich, not charged via Paddle
(commissions + platform fee owed)
On success → Billing Cycle marked "charged"
On failure → retry per policy (01. State Machines' Billing Cycle open question)
4. Creator payout
Once a Billing Cycle reaches "charged," the commissions within it become available
in each creator's wallet
Standard $50 threshold → bank payout, unchanged from the original design
Failure Handling
- Merchant integration fails to report a sale at all: invisible to SellVia entirely — this is a real, structural weakness of external-site tracking (unlike hosted checkout, there's no webhook SellVia controls end-to-end). Reconciliation (05. Payments) becomes more important, not less, though it now has less to reconcile against (SellVia has no independent record of the underlying sale, only what it was told).
- Merchant card fails at billing time: per the new Billing Cycle state machine, retried, then likely campaign suspension if unresolved — exact policy still open.
Open Questions
- Exact merchant integration mechanism to build first — a generic webhook spec, or a specific platform integration (e.g. Shopify) as the priority — not yet decided
- Whether a tracking pixel fallback is needed for merchants who can't/won't implement a webhook
Update (2026-08-07): Merchant Integration Mechanism RESOLVED — Universal Onboarding Snippet
Resolved, replacing both options this doc left open: not a bespoke per-merchant webhook, not a platform-specific integration (Shopify app) built first. Instead: a universal tracking snippet the merchant installs once during onboarding (one script tag, on their order-confirmation page — works identically across Shopify, WooCommerce, or a custom site).
How it connects to the redirect: the SellVia redirect (customer clicks creator's link → SellVia logs the click → bounces to the merchant's real product page) still happens and still sets an attribution reference — this is what makes click-level tracking (and the click-to-sale conversion KPI) possible at all, not just sale-level reporting. The onboarding snippet reads that reference when a purchase completes and reports the sale back automatically.
Why this is better than a bespoke integration: zero custom integration work per merchant, works across any e-commerce platform without SellVia building platform-specific adapters, and it's a one-time onboarding step rather than an ongoing technical dependency the merchant has to maintain correctly.
Known limitation, unchanged by this mechanism: still relies on a cookie/reference surviving from the SellVia redirect to the merchant's confirmation page — same cookie-blocking exposure (Safari ITP, etc.) already flagged for any cross-domain tracking approach. This mechanism doesn't fix that; nothing fully does.
Update (2026-08-07): Reliability — Snippet Is Universal, Webhooks Are NOT
Clarifying, since this matters for what gets built: the universal onboarding snippet is the mechanism that works for every merchant — Shopify, WordPress, custom sites, anything that can run JavaScript, with zero platform-specific engineering. Server-side webhooks do not generalize the same way — each platform (Shopify, WooCommerce, Magento, etc.) has its own webhook format and setup process, meaning a webhook integration would need to be built and maintained separately per platform. A genuinely custom/bespoke site has no webhook option at all unless that merchant's own developer builds one.
Correct framing: the snippet is the universal default for every merchant, not a fallback. If a platform-specific webhook integration is ever built (Shopify would be the obvious first candidate, given its native webhook support), it's an optional reliability upgrade for merchants on that specific platform — more resistant to ad blockers since it fires server-side, not a broader or more general solution than the snippet.
Additional reliability idea, still worth considering, doesn't depend on webhooks: unique per-creator discount codes as a secondary attribution signal — works even if the snippet fails to fire for a given purchase (ad blocker, JS error), since it doesn't depend on any cross-domain tracking surviving at all. Not yet decided whether to build this for MVP or treat as v2.
Open Questions (Update)
- Whether to build the unique-discount-code fallback for MVP, given it doesn't depend on this doc's snippet-reliability limitations at all
- Whether a Shopify-specific webhook upgrade is worth building given actual merchant platform distribution, once there's real data on what platforms merchants are actually using
Update (2026-08-07): Discount Code Fallback — LOCKED FOR MVP
Confirmed: every AffiliateLink also gets a unique discount code (e.g. derived from the creator's handle — MIA10), created in the merchant's own store discount system during campaign setup. This is a genuinely low-friction ask for merchants — creating a discount code is something every e-commerce platform supports natively and merchants already know how to do, unlike building a custom webhook.
How it strengthens attribution, not just as backup:
- Primary path (unchanged): click → redirect sets attribution reference → snippet reports the sale, tagged with that reference
- Fallback path (new): if the snippet fails to fire for a given purchase (ad blocker, JS error, cookie blocked) but the reported order still shows the discount code was used, the discount code alone is enough to attribute the sale correctly — no dependency on cross-domain cookie survival at all
- Corroboration, when both are present: if a sale report includes both a valid attribution reference AND the matching discount code, that's a stronger signal for 04. Fraud Prevention's plausibility checks than either alone
Bonus, not the main point: the discount code can also function as a real incentive shown to the customer ("10% off with MIA10"), which may help conversion — a side benefit, not the reason it was built.
Schema addition: affiliate_links.discount_code (unique, generated at link creation), and the merchant sale-report payload (05. Payment Flow) gains an optional discount_code_used field alongside the existing attribution reference.
Open Questions (unchanged, still deferred)
- Shopify-specific webhook upgrade — still deferred, not built for MVP; the discount code fallback covers the reliability gap without needing it
Update (2026-08-07): Acceptance Criteria RESOLVED
Sale-report acceptance is now defined: auto-accept by default, rejected only on duplicate order ID or a failed signed-request check; suspicious patterns (not individual sales) route to Admin review via 04. Fraud Prevention. Full detail in 01. Commission Engine.
Update (2026-08-23): MAJOR REVISION — Shopify-Only Webhook Replaces the Universal Snippet, Paddle Removed
Founder decision, full reasoning in 02. Architecture Decision Log. Two changes, both reverse decisions this doc made on 2026-08-07:
Merchant integration: Shopify webhook only (reverses "Universal Onboarding Snippet")
"For now we are just going with Shopify only." Every MVP merchant runs Shopify, so the universal snippet's main advantage (works on any platform) doesn't matter, and Shopify's native orders/paid webhook becomes the primary — and only — mechanism for MVP, not a deferred reliability upgrade. This is more reliable than the snippet, not less: it fires server-side, so it isn't affected by ad blockers, cookie blocking, or JS errors the way the snippet was.
Revised flow, step 1:
Follower clicks AffiliateLink → SellVia logs the click, redirects to the merchant's Shopify store
with tracking parameters
Merchant's Shopify store fires an `orders/paid` webhook (registered during onboarding, per-merchant
signed secret) → POST /webhooks/shopify-sales
reports the sale, including the discount code used (if any), tagged with the click's attribution
reference where Shopify passes it through (e.g. via landing-page/UTM params carried into the order)
Discount-code fallback (2026-08-07's "LOCKED FOR MVP" section) is unchanged in concept — still created per-AffiliateLink in the merchant's Shopify discount system, still corroborates or substitutes for the attribution reference. Shopify natively supports discount codes, so this remains a low-friction ask.
What this removes for MVP, not permanently: the universal JS snippet, and any WooCommerce/custom-site/generic-webhook path. These return to being live options only if/when SellVia supports merchants outside Shopify — see Full Product Vision (Post-MVP).
Open, not yet designed: the exact Shopify OAuth/app-install flow for connecting a merchant's store (Shopify app-store listing vs. a private/custom app), and how the click's attribution reference survives into the Shopify order object (UTM param → order note/attribute, most likely, but not yet specified).
Processor: Paddle removed, Swich confirmed (reverses "Paddle charges the merchant's card on file")
"Instead of paddle or anything" — and, as of the same-day follow-up decision, "go with Swich" (swichnow.io). Step 3 (Periodic billing) and step 4 (Creator payout) below no longer involve Paddle, and are no longer a manual admin-run bank-transfer process either — both route through Swich:
- Step 3, revised: the scheduled job still closes each Billing Cycle on schedule, then calls Swich's billing/invoice API to generate a payment request for the cycle total. The merchant completes payment through Swich's checkout (card, bank transfer, JazzCash, or EasyPaisa). A Swich webhook confirms payment → the cycle is marked
chargedautomatically, not by an Admin manually checking a bank statement. - Step 4, revised: once a cycle is
charged, the scheduled payout job calls Swich's payout/disbursement API for each creator owed from that cycle — routed to whichever payout method the creator registered (bank account, JazzCash, or EasyPaisa; Raast where applicable). A Swich webhook confirms completion → Payout markedpaid.
See 05. Payout Process, 05. Wallet Design (both updated 2026-08-23) for the payout side, and MVP Scope's Still-Open Items for what's still genuinely undecided (Swich's exact API shapes, pricing, and onboarding requirements for MVP-scale volume — none of this is confirmed yet, it's a real integration still to be built).
Not a Merchant of Record: Swich is a payment processor/gateway, not a legal seller-of-record like Paddle was. Tax/compliance responsibility stays with SellVia — see 05. Tax Considerations.
Currency: PKR only, not USD/EUR/GBP — narrows the worked numbers throughout 01. Commission Engine and 05. Platform Business Model & Pricing (both updated 2026-08-23).
References
Referenced by
- Endpoint Specifications· API
- Webhooks· API
- Commission Engine· Business Logic
- Domain Model· Business Logic
- Money Flow· Business Logic
- State Machines· Business Logic
- Table Specifications· Database
- Business Edge Cases· Edge Cases
- Monitoring· Infrastructure & DevOps
- WAF Configuration· Infrastructure & DevOps
- Data Inventory & Disclosure· Security
- Encryption· Security
- Architecture Decision Log· Technical Architecture
- Backend Architecture· Technical Architecture
- Event-Driven Architecture· Technical Architecture
- Frontend Architecture· Technical Architecture
- FEATURE_LIST· UI