PilotFoxDocs

Stripe

Connect Stripe with a restricted read-only key, which Stripe payments count, and passing the checkout reference to Stripe.

PilotFox reads a site's Stripe payments with a key that can only read, and links each one to the visit that led to it. Connect it in Site settings → Revenue → Stripe, or with PUT /api/v1/sites/{siteId}/integrations/stripe. The revenue overview explains connecting, syncing, Match again and disconnecting for every provider.

Create a restricted key

In the Stripe Dashboard, open API keys and click Create restricted key. Give it Read access to the resources below and leave everything else at None.

ResourceAccessNeeded for
Checkout SessionsReadRequired: the payments
Payment IntentsReadRequired
Charges and RefundsReadRequired: refunds
EventsReadRequired: following new payments and refunds
Invoices (Billing)ReadOptional: subscription renewals, and refunds of a subscription's first payment
Subscriptions (Billing)ReadOptional: the subscription metrics

PilotFox lists each required resource once when you connect. A key without one is refused with a message that names it; nothing is stored.

A secret key works too, but a restricted key can only read. Keys start with rk_live_ or rk_test_ (restricted) and sk_live_ or sk_test_ (secret); publishable keys (pk_) cannot read anything and are refused. A test key imports test-mode payments only. The settings show the key as its prefix and last four characters, such as rk_live_…wxyz.

Without Invoices or Subscriptions access everything else still imports, and the connection shows a notice saying what is missing (the notice field of the connection). After adding a permission to the same key in Stripe (Edit key in the key's menu), click Sync now: PilotFox checks again at once (otherwise every 6 hours).

When you Replace key and the site's latest Stripe payment is a subscription invoice, the new key also needs Read access to Invoices: PilotFox reads that invoice to check that the key belongs to the same Stripe account and mode. A key without it is refused with a message saying so.

PilotFox checks Stripe every 10 minutes once it has caught up (every minute while it imports), which keeps well inside the read allowance Stripe gives each account and that your own integration shares.

Which Stripe payments count

CountedNot counted
Paid Checkout Sessions (status complete, payment_status paid), including those created by Payment LinksPayments made without Checkout or a subscription, for example PaymentIntents you create yourself, or one-off invoices
Payments by delayed methods, once Stripe marks them paidCheckout Sessions that are unpaid, expired or have a total of 0 (free trials, setup mode)
Full and partial refunds of those paymentsUnpaid, void or uncollectible invoices, and invoices of 0 (a trial's)
The first payment of a subscription started in CheckoutRenewals and their refunds, when the key cannot read Invoices
With Read access to Invoices: every paid subscription invoice (renewals, upgrades, prorations), with its refunds
  • A Checkout Session's payment id is the session's (cs_…); a renewal's is its invoice's (in_…). A subscription started in Checkout counts its first payment once, as the Checkout Session; a subscription created without Checkout (through the API or the Dashboard) counts its first invoice instead.
  • The amount is the session's amount_total or the invoice's amount_paid, tax included.
  • A one-time payment's time is when it was charged. A subscription's first payment through Checkout keeps the time the Checkout Session was created; a renewal's time is when its invoice was paid.

Renewals are credited to the visit that started the subscription. Every payment of a subscription is linked like its earliest linked payment (normally the first), affiliate included, so a renewal months later still counts for the channel, campaign or affiliate that brought the subscriber. A subscription created without Checkout is linked through the pilotfox_user_id (or pilotfox_ref) in its own metadata, which Stripe copies onto each of its invoices, or else through the customer's email.

Pass the reference to Stripe

Set the checkout reference as metadata.pilotfox_ref, in any format (Stripe metadata values are at most 500 characters). Metadata can be set on the Checkout Session (metadata) or on its payment (payment_intent_data.metadata); PilotFox reads both, the session's first. For a subscription, subscription_data.metadata reaches its invoices.

server.js (Node.js, the stripe package)
import Stripe from "stripe";
// A key that can create Checkout Sessions (not the read-only one you gave PilotFox):
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

app.post("/create-checkout", async (req, res) => {
  const ref = String(req.body.ref ?? "");
  const session = await stripe.checkout.sessions.create({
    mode: "payment",
    line_items: [{ price: "price_123", quantity: 1 }],
    success_url: "https://example.com/thanks?session_id={CHECKOUT_SESSION_ID}",
    metadata: { pilotfox_ref: ref.slice(0, 500) },
    // A signed-in buyer: metadata: { pilotfox_ref: ref, pilotfox_user_id: user.id },
  });
  res.json({ url: session.url });
});

The {CHECKOUT_SESSION_ID} in the success URL is optional: it lets the script link the payment from your return page when nothing else does.

What PilotFox reads from a Stripe payment

FieldUsed as
metadata.pilotfox_ref (session, then payment intent; invoice, then its subscription's metadata)Checkout reference, any format
client_reference_idCheckout reference only in the PilotFox format (pf and digits); any other value is a user id
metadata.pilotfox_user_idUser id
The session id (cs_…)Return-page id, matched up to an hour after the payment
customer_details.email (else customer_email); on invoices customer_emailCustomer email
customer (cus_…)Same-customer link for later payments
subscriptionThe subscription link of renewals

So client_reference_id may hold your own user id: it then links the payment to the user you identified with that id (references and your own ids).

With Payment links and return pages on (the default), the script adds a fresh PilotFox reference to links to https://buy.stripe.com/ as they are clicked, in their client_reference_id parameter, and sends the matching checkout event (payment links).

To do it yourself, for example for Payment Links on your own domain: a Payment Link accepts the reference as its client_reference_id URL parameter (Stripe accepts letters, digits, dashes and underscores, up to 200 characters). Make it the way the script does, pf and digits; in any other format PilotFox reads it as a user id.

document.querySelectorAll('a[href^="https://pay.example.com/"]').forEach((a) => {
  a.addEventListener("click", () => {
    const ref = "pf" + crypto.getRandomValues(new Uint32Array(4)).join(""); // a PilotFox reference
    window.pilotfox?.event("checkout", { ref });
    const url = new URL(a.href);
    url.searchParams.set("client_reference_id", ref);
    a.href = url.toString();
  });
});

For the return page, set the Payment Link's redirect after payment to a URL of yours with {CHECKOUT_SESSION_ID}, for example https://example.com/thanks?session_id={CHECKOUT_SESSION_ID}.

Stripe subscriptions

With Read access to Subscriptions, PilotFox reads every subscription for the subscription metrics. Stripe's statuses are used as they are (incomplete_expired counts as incomplete). The plan price is each licensed, per-unit recurring price times its quantity, before tax and discounts; metered (usage-based) and tiered prices are left out.

On this page