Manual mode
Manual mode supports two integration options. Pick the one that matches how you want to handle checkout.
Option A — Stripe Payment Link
Section titled “Option A — Stripe Payment Link”Use a static, Stripe-hosted Payment Link URL. Buyers visit a Keylight buy URL (https://api.keylight.dev/buy/<your-account-id>/<product>/<keyType>) — Keylight then redirects them straight to your Stripe Payment Link.
Setup:
- In your Stripe Dashboard, create a Payment Link for each key type you want to sell.
- On each Payment Link, open Advanced options → Metadata and add the required keys (see Metadata is required below).
- Copy the Payment Link URL and paste it into the key type’s Payment Link URL field in the Keylight dashboard (Advanced Stripe settings).
- Configure your inbound webhook so Keylight can mint licenses after payment (see Webhook setup below).
Best for: simple, no-code setups where buyers go through Stripe’s hosted checkout and you don’t need programmatic control.
Option B — Per-product override key
Section titled “Option B — Per-product override key”Paste your Stripe secret key (sk_live_… or sk_test_…) into the product’s Advanced Stripe settings. Keylight uses your key directly to create dynamic Checkout Sessions on your account.
Setup:
- In Keylight, open the product’s Advanced Stripe settings.
- Paste your Stripe secret key into the Stripe secret key field. Keylight stores it encrypted at rest.
- Configure your inbound webhook (see Webhook setup below).
What you get:
- The Keylight buy URL (
/buy/<tenant>/<product>/<keyType>) works end-to-end — buyers are sent to a freshly-created Checkout Session on your Stripe account. Keylight auto-injects the required metadata for you. - The programmatic checkout API (
POST /api/<tenant>/checkout) works — your backend can create checkout sessions via Keylight.
Best for: when you want full programmatic control, want to call Keylight from your own backend, or want to use your own Stripe account without going through Stripe Connect’s OAuth flow.
Webhook setup
Section titled “Webhook setup”Both options require Keylight to receive checkout.session.completed from your Stripe account so it can mint the license and (optionally) email the buyer.
In your Stripe Dashboard → Developers → Webhooks → Add endpoint:
- Endpoint URL:
https://api.keylight.dev/<your-account-id>/webhook/stripe - Events to send:
checkout.session.completed
Reveal the signing secret (whsec_…) and paste it into Keylight via the Advanced Stripe settings for your product (or the per-tenant Stripe setup step in onboarding for tenant-wide secrets). Keylight stores it encrypted and uses it to verify every incoming event.
Without a matching secret, Keylight rejects all incoming webhook calls with a signature error.
Metadata is required
Section titled “Metadata is required”Every checkout session sent to Keylight must carry metadata.product_id. The webhook rejects any session without it because a single account can sell multiple apps and Keylight has to know which one to mint a key for.
If you create sessions server-side (Option B):
const session = await stripe.checkout.sessions.create({ mode: "payment", line_items: [{ price: "price_...", quantity: 1 }], success_url: "https://testco.example.com/thanks", cancel_url: "https://testco.example.com/buy", metadata: { product_id: "testapp", // must match your app's productId key_type_id: "personal", // optional - matches your key type's keyTypeId },});For Payment Links (Option A), set the same metadata on the link itself via the Stripe Dashboard.
key_type_id metadata
Section titled “key_type_id metadata”Each app can define multiple key types (e.g. “personal”, “team”, “site”). Each has its own activation limit and optional duration. When key_type_id is present in the checkout session metadata, the webhook resolves the matching key type and creates the license with that key type’s activation limit and expiry. If key_type_id is missing or doesn’t match any configured key type, the license defaults to the app’s first key type.
Testing with the Stripe CLI
Section titled “Testing with the Stripe CLI”In test mode, use stripe-cli to fire simulated events or forward live events:
# Install: brew install stripe/stripe-cli/stripestripe login
# Fire a synthetic completed checkout against your accountstripe trigger checkout.session.completed \ --add checkout_session:metadata.product_id=testapp
# Or forward a live stream while debuggingstripe listen --forward-to https://api.keylight.dev/<your-account-id>/webhook/stripestripe listen prints its own webhook signing secret — that’s for the CLI session only. Production must use the secret from the Webhook setup step.
How license issuance works
Section titled “How license issuance works”When Keylight receives a valid checkout.session.completed:
- Verifies the Stripe signature against the stored secret.
- Generates a new license key with your app’s
keyPrefix(e.g.TEST-A1B2-C3D4-E5F6). - Stores only the key’s hash and a masked display form — never the full raw key.
- Sends a license-delivery email to the customer (when
customer_details.emailis on the session) with the raw key.
If the webhook fails (network issue, signature mismatch, timeout) the license is still created server-side but the email may not send. You can always re-issue from the dashboard.