RC
0/23
AUDIO READY — click anywhere to start
SECTION_19

Accepting Payments with Stripe

Use the platform's Stripe payment extension to accept one-time and recurring payments on-chain.

The platform's Stripe payment extension is the canonical way to accept payments in Internet Computer apps. It wraps Checkout Sessions, webhook verification, subscription management, and usage metering — all orchestrated through the Caffeine extension layer, without you having to build a payment backend from scratch.

Choose your billing model first — it determines which Stripe resources you configure and which webhook events you must handle.

> THREE BILLING MODELS

One-time payments

Customers pay once for a product or a digital goods delivery — no subscription, no automatic renewal.

When: When you sell a fixed product: e-book, course, software license, NFT mint, one-off donation.

Example: Scenario: A builder sells a Caffeine prompt pack for $9 — customer checks out once, receives an instant download link.

Recurring subscriptions

Customers are billed automatically at regular intervals — monthly, yearly, or custom — until they cancel.

When: When you deliver ongoing value: SaaS tools, premium communities, hosted apps, content memberships.

Example: Scenario: A hosted ICP app builder offers a Studio plan at $25/month — Stripe auto-renews, webhook revokes access on cancellation.

Usage-based billing

Customers pay based on actual consumption — API calls, bytes processed, tokens generated, records stored.

When: When value varies per unit: API services, AI inference, storage backends, compute platforms.

Example: Scenario: An AI writing service charges $0.02 per 1,000 generated tokens — Stripe meters usage and invoices a lump sum at month end.

> QUICK START PROMPT

Copy this prompt and paste it into your own Caffeine build to add Stripe payments right away — Checkout Sessions, webhook verification, and a success page are all included. Adjust the product name, price, and currency to match your offering.

Copy & paste quick start prompt
Add Stripe payments to my project using the Stripe extension:
- Create a Checkout Session for one-time payments (product: "Premium Prompt Pack", price: $9.00 USD) and redirect the customer to Stripe-hosted Checkout.
- Handle the checkout.session.completed webhook: verify the signature with the webhook secret, then grant the customer access to the purchased content.
- Show a success page after payment confirmation and a cancel page if the customer abandons checkout.
- Store payment status in canister state so access survives reloads.
- Use test mode keys during development and switch to live keys at deploy time.

> MODEL COMPARISON

AxisOne-timeRecurringUsage
Payment triggerOne-time checkoutAutomatic per cycleMetered usage → invoice
Revenue predictabilityLow — per saleHigh — recurringMedium — fluctuates with usage
Setup complexityLowMedium — webhooks for renewal/cancellationHigh — metering + invoicing
Best-fit product typeDigital goods, licensesSaaS, membershipsAPIs, compute, storage

> CREATE A CHECKOUT SESSION

Redirect the customer to a Stripe Checkout Session. The platform extension wraps the API call; you pass the product ID, success, and cancel URLs.

Checkout Session creation
import { createActor } from "@/backend";
import { useActor } from "@caffeineai/core-infrastructure";

// Frontend: request a Checkout Session URL from the canister
// (the Stripe extension signs the request server-side).
async function startCheckout(productId: string) {
  const { actor } = useActor(createActor);
  if (!actor) throw new Error("actor not ready");

  const result = await actor.createCheckoutSession({
    productId,
    successUrl: `${window.location.origin}/success`,
    cancelUrl:  `${window.location.origin}/cancel`,
    mode: "payment", // "payment" | "subscription"
  });

  if ("ok" in result) {
    // Redirect the browser to Stripe-hosted Checkout
    window.location.href = result.ok.url;
  } else {
    throw new Error(result.err);
  }
}

> VERIFY & PROCESS A WEBHOOK

Stripe sends events to your canister. Always verify the signature with the webhook secret before running business logic — never trust client-side payment confirmation.

Webhook handler
// Backend (Motoko): the Stripe extension exposes a typed handler.
// Verify the signature, then match on the event type.

public shared func handleStripeWebhook(
  payload : Text,
  signature : Text,
) : async Text {
  // 1. Verify signature — throws if invalid
  let event = await Stripe.verifyWebhook(payload, signature);

  // 2. Dispatch on event type
  switch (event.type) {
    case ("checkout.session.completed") {
      let session = event.data.session;
      // Fulfill the order: grant access, send email, mint NFT...
      await fulfillOrder(session);
    };
    case ("invoice.paid") {
      // Recurring subscription renewal succeeded
      await renewAccess(event.data.invoice);
    };
    case ("customer.subscription.deleted") {
      // Subscription cancelled — revoke access
      await revokeAccess(event.data.subscription);
    };
    case (_) {
      // Unhandled event — log and acknowledge
    };
  };

  return "ok";
}

[THE EXTENSION DOES THE HEAVY LIFTING]

The platform's Stripe extension wraps API authentication, signature verification, idempotency, and error retries. Do not build payment logic from scratch — configure the extension, define your products in Stripe, and react to webhook events.

[ALWAYS VERIFY WEBHOOK SIGNATURES]

Verify every incoming webhook signature with your Stripe webhook secret before fulfilling orders or granting access. Never trust client-side payment confirmation — a malicious client can send arbitrary requests to your canister. Only a verified Stripe signature proves a payment actually occurred.

// Stripe Payments Checklist

  • Choose your billing model — one-time, recurring, or usage-based — matching your product
  • Configure the platform's Stripe extension (API keys, webhook secret, create products in Stripe)
  • Request a Checkout Session from the frontend and redirect the customer to Stripe-hosted Checkout
  • Implement a webhook handler — verify the signature and react to checkout.session.completed
  • Test end-to-end in test mode before going live — test cards, subscription renewals, refunds