Documentation
RevenueConnect Stripe

Stripe PaymentIntent API

Attribute revenue from the Stripe PaymentIntent API.

Attribute payments from your own payment form, built on PaymentIntents. This takes about ten minutes.

Before you start

Connect Stripe first. Install the web SDK on your checkout page and the Node SDK on your server.

FounderHQ treats the PaymentIntent as the transaction. Put the attribution metadata on the PaymentIntent itself, not on a charge or an invoice.

Send the visitor IDs to your server

// app/checkout/pay-button.tsx
"use client";

import { founderhq } from "@founderhq/events";

export function PayButton({ amountMinor }: { amountMinor: number }) {
  async function startPayment() {
    const identity = founderhq.checkoutMetadata();
    const response = await fetch("/api/payment-intent", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ amountMinor, identity }),
    });
    const { clientSecret } = await response.json();
    // Hand clientSecret to Stripe Elements as you already do.
    console.log(clientSecret);
  }

  return <button onClick={startPayment}>Pay</button>;
}

Create the PaymentIntent with the metadata

// app/api/payment-intent/route.ts
import Stripe from "stripe";
import { checkoutMetadata } from "@founderhq/events-node";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: Request) {
  const { amountMinor, identity } = await request.json();
  const attribution = checkoutMetadata({
    anonymousId: identity.fhq_anonymous_id,
    sessionId: identity.fhq_session_id,
  });

  const intent = await stripe.paymentIntents.create({
    amount: amountMinor,
    currency: "usd",
    automatic_payment_methods: { enabled: true },
    metadata: attribution,
  });

  return Response.json({ clientSecret: intent.client_secret });
}

Amounts are in minor units. 4900 is $49.00.

Know what a refund does

FounderHQ subtracts a refund only when the individual Refund object reaches succeeded. Partial refunds each subtract their own amount. A pending refund changes nothing.

Verify

  1. Start a payment on your checkout page.
  2. In Stripe, open the PaymentIntent. Its metadata shows fhq_anonymous_id and fhq_session_id.
  3. Complete the payment with a test card.
  4. In FounderHQ, open Revenue. The payment shows the channel that brought the buyer.

Troubleshoot

The metadata sits on the charge, not the PaymentIntent

FounderHQ reads the PaymentIntent. Move the metadata to the paymentIntents.create call.

The payment arrives with no channel

The browser sent empty IDs, or this buyer never loaded a page that runs the SDK. Check that the checkout page itself loads the web SDK.

AI agent or LLM? Read this page as markdown

On this page