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
- Start a payment on your checkout page.
- In Stripe, open the PaymentIntent. Its metadata shows
fhq_anonymous_idandfhq_session_id. - Complete the payment with a test card.
- 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.