Web SaaS Guide
End-to-end: a creator shares a link to your SaaS, a visitor signs up and subscribes via Stripe, and the creator earns a commission on every invoice — renewals included. No mobile app, no app store, nothing but your landing page and your Stripe account.
The loop
creator link https://yourapp.com/?via=LUMI
→ landing page (Web SDK captures ?via= and persists it)
→ signup (identify(userId) binds your user id)
→ Stripe checkout (metadata.customer_user_id = that same id)
→ invoice.paid (Stripe webhook → our API → commission)
→ renewals (every invoice.paid keeps paying the creator)
→ dashboard + CSV (pending → matured → paid out)1. Give creators their link
Each affiliate gets a code (e.g. LUMI). Their link is just your site plus a query
param — no redirect service needed:
https://yourapp.com/?via=LUMIBranded go.myappaffiliate.com short links work too (they carry a claim_token
instead); the Web SDK handles both.
2. Capture on the landing page
Drop in the Web SDK — the script tag auto-captures ?via= (and
claim_token) on load and persists it in localStorage, so it survives until signup
days later:
<script
src="https://cdn.myappaffiliate.com/sdk.js"
data-api-key="pk_live_…"
data-base-url="https://api.myappaffiliate.com"
async
></script>3. Identify at signup
When the account is created, bind your user id to the attribution. Client-side:
await maa.identify(user.id);…or server-side with the Node SDK (also covers referral codes typed into a signup form):
await maa.trackSignup({ userId: user.id, code: req.body.via });This id is the join key for everything that follows — it must be the exact string you put into Stripe metadata next.
4. Stamp Stripe with the user id
When creating the checkout session / subscription, set
metadata.customer_user_id to that same id:
await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [{ price: "price_…", quantity: 1 }],
subscription_data: { metadata: { customer_user_id: user.id } },
metadata: { customer_user_id: user.id },
success_url: "https://yourapp.com/welcome",
cancel_url: "https://yourapp.com/pricing",
});Without this metadata an invoice cannot be matched to an attribution and earns no commission.
5. Point the Stripe webhook at us
In the Stripe dashboard → Developers → Webhooks → Add endpoint:
- URL:
https://api.myappaffiliate.com/webhooks/stripe/<appId> - Events:
invoice.paid,charge.refunded,customer.subscription.deleted,checkout.session.completed - Paste the endpoint's signing secret into your MyAppAffiliate app settings (we
verify the
Stripe-Signatureheader on every delivery).
Full details in Webhooks.
Event mapping
| Stripe event | Normalized type | Commissionable? |
|---|---|---|
invoice.paid (billing_reason: subscription_create) | purchase | yes |
invoice.paid (billing_reason: subscription_cycle) | renewal | yes |
charge.refunded | refund | no (claws back pending commission) |
customer.subscription.deleted | cancel | no |
checkout.session.completed (mode: payment) | purchase | yes (one-time purchases) |
Amounts are taken in integer USD cents; commission = amount × rate_bps / 10000,
attributed to the referral within the attribution window, created pending, and
matured after the hold period (so refunds inside the hold never pay out).
6. Watch it pay
- The founder dashboard shows the funnel (clicks → signups → paid), revenue, and per-creator earnings.
- Creators get their own scoped view with earnings and next-payout date.
- Payouts are a CSV export of matured commissions — idempotent, reconciles to the payout total.
Checklist
- Web SDK snippet on the landing page (or
configure()in your app shell) identify(userId)at signup (ortrackSignupserver-side)metadata.customer_user_idon every Stripe subscription/checkout- Stripe webhook endpoint added + signing secret saved
- Test end-to-end with a Stripe test-mode subscription → commission appears on the dashboard