Ambassly

Pass Ambassly Referrals into Stripe Checkout

Ambassly attributes Stripe conversions from the referral code you pass to Stripe.

For Checkout Sessions, pass the code as both:

  • client_reference_id
  • metadata.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:

  1. client_reference_id
  2. metadata.ambassly

Ambassly then creates the referral row and links it to the Stripe customer.