supapulsedocs
Revenue packageOverview

Overview

Record revenue from Stripe or your own server and credit it to the visit that first brought the customer. How the flow works and what it covers.

Supapulse credits each payment to the source that first brought the customer. You tell Supapulse who your customer is in the browser, then payments arrive from Stripe or from your server. Renewals months later take the same first touch.

The flow

Browser. After login or signup, identify the visitor with your own user ID.

supapulse.identify(user.id);

Supapulse links that ID to the pageviews it already recorded for this visitor.

Payments. Pick the path that fits your setup.

  • Stripe: paste a per-site webhook URL into Stripe. Supapulse reads purchases, invoices, refunds and subscription changes itself. No server code. See Stripe.
  • Any other processor: your server sends a revenue event with the site's secret token, using @supapulse/revenue or the HTTP API. See Payment webhooks.

Optional: checkout metadata. If a customer pays before you can identify them, pass getVisitorFingerprint() and the session ID through checkout. This is the strongest link. See Attribution.

A server-side event

curl -X POST https://supapulse.app/api/ingest \
  -H "Content-Type: application/json" \
  -H "X-API-Token: sec_your_secret_token" \
  -d '{
    "type": "revenue",
    "data": {
      "customer_id": "user_123",
      "amount": 49.90,
      "currency": "USD",
      "transaction_id": "in_1Q8x…",
      "event_type": "renewal"
    }
  }'

The API validates the event, then answers 202 Accepted and a worker stores it. A bad payload gets 422 straight away, so you can see the problem in the response. Payment webhooks has handlers you can adapt.

Fields

FieldRequiredDefaultNotes
customer_idYesnoneYour customer key. Use the same value you pass to identify().
amountYesnoneMajor units, like 49.90. Not negative; use event_type: "refund" for refunds.
transaction_idYesnoneUnique per payment and event type within a site. A repeat isn't stored again, so webhook retries don't double count.
event_typeNopurchasepurchase, subscription, renewal, upgrade, downgrade, churn or refund.
currencyNoUSDThree-letter code. Reports don't convert currencies.
visitor_fingerprint, session_idNononeLink the payment to a visit directly. Without them Supapulse uses the customer's identified visit.
mrr_impact, subscription_id, is_recurringNononeSubscription data. See API reference.
payment_processorNostripeFree text, like paddle.
occurred_atNonowWhen the payment happened. Not more than a day in the future.

The full reference is in Revenue API.

What you see

The site's revenue view shows total revenue (net of refunds), transactions, average order value, revenue per visitor, MRR and refunds. Sources can be read by first touch or last touch. Revenue is also broken down by page, entry page, exit page, referrer, UTM campaign, country, device, browser and OS.

The @supapulse/revenue SDK

@supapulse/revenue wraps the request with validation and retries. visitor_fingerprint and session_id are optional. See Installation.

Plans and limits

Revenue needs an active plan or trial: a site whose team has neither gets 402 with plan_inactive. Revenue events are never blocked by the monthly event limit, so a payment isn't lost because pageviews ran over. New revenue rows count toward usage; identify events don't. See Error codes.

Security

  • The secret token can send revenue for the site. Keep it in a server environment variable and never ship it to a browser.
  • Verify your processor's webhook signature before you send revenue, so nobody can forge a payment by calling your webhook URL. Supapulse's Stripe webhook verifies the signature for you.
  • If the secret token leaks, regenerate it in the site's settings. The old one stops working immediately.