Traqio.

Integrations

Stripe integration

Connect your Stripe account: SaaS payments, renewals and refunds reach the campaign that brought each user, with one server call at sign-up.

Stripe bills your SaaS customers and reports each payment with a webhook. Connected to Traqio, your Stripe account brings every paid invoice, renewal and refund onto the user who signed up — so a campaign is credited with the money its sign-ups paid, month one and month twelve, with one server call at sign-up and none per payment. It is the web counterpart of the RevenueCat integration, with the same rules for refunds, test payments and duplicates.

Your Stripe, not Traqio's billing

This connects your Stripe account, read-only, to measure your revenue. It has nothing to do with how Traqio bills you.

Set up#

  1. Report the sign-up once#

    Where your backend creates the account, report it with the user id and the trackID the visitor arrived with — see the Node SDK (PHP and Python work the same way). Passing the email is optional: the SDK hashes it on your server and sends only the hash, the fallback key described below.

    await traqio.signup({
      userId: user.id,
      trackId: trackIdFromRequest(req),
      email: user.email, // optional — hashed locally, never sent
    });
  2. Put the user id in Stripe metadata#

    Set metadata.traqio_user_id to the same user id when you create the Subscription — Stripe copies a subscription's metadata onto every invoice it produces, renewals included. On the Customer works too.

    await stripe.subscriptions.create({
      customer: customerId,
      items: [{ price: priceId }],
      metadata: { traqio_user_id: user.id },
    });
    
    // Stripe Checkout:
    await stripe.checkout.sessions.create({
      mode: "subscription",
      subscription_data: { metadata: { traqio_user_id: user.id } },
      // …
    });
  3. Create a restricted key#

    Stripe Dashboard → Developers → API keys → Create restricted key. Set Customers: Read, Invoices: Read and Webhook Endpoints: Write; leave everything else at None. A live key (rk_live_…) measures real payments; a test key (rk_test_…) lets you try the wiring with test payments only.

  4. Paste it in Traqio#

    In the console: your product → Integrations → Revenue → Stripe. Traqio reads one customer and one invoice to prove the key, then creates its webhook endpoint in your Stripe account — URL https://api.traqio.app/webhooks/stripe/<product id> — and keeps its signing secret. A key Stripe refuses is not saved, and the message names the permission to tick.

    No Webhook Endpoints permission?

    Add the endpoint yourself in Stripe → Developers → Webhooks, with the URL shown in the console and the events listed below, tick « I added the webhook endpoint myself » and paste its signing secret (whsec_…) with the key.
  5. Make a test-mode payment#

    With a test key, a test-mode payment shows in the console's Recent Stripe events as Test mode — wiring verified, with the amount Traqio parsed. It is never counted. Live payments are the ones counted; each line says which user it was credited to, or why it is unmatched.

How a payment finds its user#

Traqio tries these in order, and stops at the first that answers:

WhereWhat Traqio reads
The invoice's subscriptionmetadata.traqio_user_id — the snapshot Stripe copies onto each invoice
The invoicemetadata.traqio_user_id
The Stripe Customermetadata.traqio_user_id, read with the restricted key
The customer's emailSHA-256 of the trimmed, lowercased email, matched against the emailHash sent at sign-up — only if exactly one user has it

A payment that none of them ties to a user is unmatched: recorded, counted in the product's revenue, credited to no campaign. A user id Traqio has not seen yet — the payment arrived before the sign-up call — is kept, and the payment joins the user when the sign-up lands.

What each Stripe event does#

Stripe eventIn Traqio
invoice.paida purchase — amount_paid, in the invoice's currency; a €0 invoice (trial, credit balance) moves no money
charge.refundeda refund — negative revenue, its own entry against the invoice it refunds; each partial refund once
customer.subscription.created / updated / deletedlogged with the state change (trialing → active…): no money moves
Anything in test modelogged as test mode, never counted
  • Currency: each payment is kept in the currency it was paid in, and converted to the product's reporting currency at that day's rate in the reports.
  • Duplicates: a Stripe event is processed once, and a payment is keyed on its invoice id — a redelivery, or the same invoice also reported by your server with transactionId, is one row. See Counting one purchase once.
  • Refunds are only taken back from payments Traqio recorded: an invoice paid before Stripe was connected was never added, so its refund subtracts nothing.
  • Amounts are gross: what the customer paid, tax included; the tax share is stored, not applied. Stripe's fees are not subtracted.

Limits#

  • Traqio counts invoices. A one-off Checkout payment counts when its session creates an invoice (invoice_creation.enabled); a bare PaymentIntent with no invoice does not.
  • One Stripe account per Traqio product. A Stripe account that sells several products sends all their payments to each connected product, where the others' customers show as unmatched.
  • Website payments are attributed and reported in Traqio, but are not sent to Meta or TikTok yet.

Frequently asked questions#

Why a restricted key and not a Stripe Connect sign-in?
A restricted key works on any Stripe account the moment it is created, and it is narrower: it reads customers and invoices, and may create one webhook endpoint. It cannot charge, refund or change anything. Traqio refuses a full secret key (sk_…) and stores the restricted key encrypted.
Does Traqio write anything in my Stripe account?
One thing: the webhook endpoint it creates for itself, described as Traqio's. Disconnecting Stripe in the console deletes it. Nothing else is written, and no money object is ever touched.
What if a payment names no user?
It is still revenue: it is recorded and counted in the product's total, as unmatched — it reaches no campaign. Traqio never gives a payment to a user by guess, and the Stripe events list says why each payment is unmatched.
Do I still call track() for each payment?
No. With Stripe connected, signup() is the only server call. If you keep a track() for a Stripe payment, pass the invoice id (in_…) as transactionId with store: "stripe": both reports land on one row.

Last updated November 13, 2026