Pass Ambassly Referrals into Stripe Checkout
Ambassly attributes paid customers when your checkout code sends the referral code to Stripe as client_reference_id and/or metadata.ambassly. Use both fields when possible so the attribution is visible in Stripe and durable after the browser leaves your site.
Ambassly attributes Stripe conversions from the referral code you pass to Stripe.
For Checkout Sessions, pass the code as both:
client_reference_idmetadata.ambassly
Ambassly accepts either field, but sending both makes the attribution easy to inspect in Stripe.
Node and Express
Install dependencies:
npm install express stripe dotenv
Create server.js:
require("dotenv").config();
const express = require("express");
const Stripe = require("stripe");
const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
const app = express();
app.use(express.json());
app.get("/", (_req, res) => {
res.type("html").send(`<!doctype html>
<html>
<head>
<title>Checkout</title>
<script
src="https://ambassly.com/a.js"
data-ambassly="COMPANY_PUBLIC_ID"
async
></script>
</head>
<body>
<button id="checkout">Checkout</button>
<script>
document.getElementById("checkout").addEventListener("click", async () => {
const referral = window.Ambassly?.getReferral?.();
const response = await fetch("/create-checkout-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ambassly: referral?.code ?? null })
});
const { url } = await response.json();
window.location.href = url;
});
</script>
</body>
</html>`);
});
app.post("/create-checkout-session", async (req, res) => {
const ambassly =
typeof req.body.ambassly === "string" && req.body.ambassly.length > 0
? req.body.ambassly
: null;
const session = await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [
{
price: process.env.STRIPE_PRICE_ID,
quantity: 1
}
],
success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.APP_URL}/`,
...(ambassly
? {
client_reference_id: ambassly,
metadata: {
ambassly
}
}
: {})
});
res.json({ url: session.url });
});
app.listen(3000, () => {
console.log("Listening on http://localhost:3000");
});
Create .env:
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PRICE_ID=price_...
APP_URL=http://localhost:3000
Run it:
node server.js
Then visit:
http://localhost:3000/?via=TESTCODE
Next.js App Router
Install Stripe:
npm install stripe
Add the script to your app shell, replacing COMPANY_PUBLIC_ID.
// app/layout.tsx
import Script from "next/script";
export default function RootLayout({
children
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://ambassly.com/a.js"
data-ambassly="COMPANY_PUBLIC_ID"
strategy="afterInteractive"
/>
</body>
</html>
);
}
Create a checkout button:
// app/checkout-button.tsx
"use client";
type AmbasslyReferral = {
code: string;
link?: string;
ts?: string;
};
declare global {
interface Window {
Ambassly?: {
getReferral?: () => AmbasslyReferral | null;
};
}
}
export function CheckoutButton() {
async function startCheckout() {
const referral = window.Ambassly?.getReferral?.();
const response = await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ambassly: referral?.code ?? null })
});
if (!response.ok) {
throw new Error("Checkout failed");
}
const { url } = await response.json();
window.location.href = url;
}
return <button onClick={startCheckout}>Checkout</button>;
}
Create the route handler:
// app/api/checkout/route.ts
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(request: Request) {
const body = await request.json();
const ambassly =
typeof body.ambassly === "string" && body.ambassly.length > 0
? body.ambassly
: null;
const session = await stripe.checkout.sessions.create({
mode: "subscription",
line_items: [
{
price: process.env.STRIPE_PRICE_ID!,
quantity: 1
}
],
success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing`,
...(ambassly
? {
client_reference_id: ambassly,
metadata: {
ambassly
}
}
: {})
});
return Response.json({ url: session.url });
}
Use the button on a page:
// app/pricing/page.tsx
import { CheckoutButton } from "../checkout-button";
export default function PricingPage() {
return <CheckoutButton />;
}
Set environment variables:
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PRICE_ID=price_...
NEXT_PUBLIC_SITE_URL=http://localhost:3000
Test with:
http://localhost:3000/pricing?via=TESTCODE
Payment Links
Stripe Payment Links support client_reference_id as a URL parameter. Use that for Ambassly attribution when you are not creating Checkout Sessions from your server.
Create your normal Payment Link in Stripe, then append the current Ambassly code before redirecting the customer.
<script
src="https://ambassly.com/a.js"
data-ambassly="COMPANY_PUBLIC_ID"
async
></script>
<button id="payment-link">Checkout</button>
<script>
const paymentLink = "https://buy.stripe.com/test_abc123";
document.getElementById("payment-link").addEventListener("click", () => {
const referral = window.Ambassly?.getReferral?.();
const url = new URL(paymentLink);
if (referral?.code) {
url.searchParams.set("client_reference_id", referral.code);
}
window.location.href = url.toString();
});
</script>
Do not put secrets, email addresses, or private customer data in client_reference_id. Use only the Ambassly referral code.
Payment Links do not give your page a server-side Checkout Session creation step where you can set dynamic metadata.ambassly per click. If you need both client_reference_id and metadata.ambassly, use server-created Checkout Sessions.
What Ambassly reads
When Stripe sends checkout.session.completed, Ambassly resolves the referral code from:
client_reference_idmetadata.ambassly
Ambassly then creates the referral row and links it to the Stripe customer.