MyAppAffiliate

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

Code
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)

Each affiliate gets a code (e.g. LUMI). Their link is just your site plus a query param — no redirect service needed:

Code
https://yourapp.com/?via=LUMI

Branded 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:

HTML
<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:

TypeScript
await maa.identify(user.id);

…or server-side with the Node SDK (also covers referral codes typed into a signup form):

TypeScript
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:

TypeScript
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-Signature header on every delivery).

Full details in Webhooks.

Event mapping

Stripe eventNormalized typeCommissionable?
invoice.paid (billing_reason: subscription_create)purchaseyes
invoice.paid (billing_reason: subscription_cycle)renewalyes
charge.refundedrefundno (claws back pending commission)
customer.subscription.deletedcancelno
checkout.session.completed (mode: payment)purchaseyes (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

  1. Web SDK snippet on the landing page (or configure() in your app shell)
  2. identify(userId) at signup (or trackSignup server-side)
  3. metadata.customer_user_id on every Stripe subscription/checkout
  4. Stripe webhook endpoint added + signing secret saved
  5. Test end-to-end with a Stripe test-mode subscription → commission appears on the dashboard