TanStarter Docs

Waffo Pancake

How to set up and use Waffo Pancake for payments and subscriptions

TanStarter uses Waffo Pancake for payment processing, supporting both one-time payments and subscriptions. Waffo Pancake is a Merchant of Record (MoR): it acts as the legal seller and handles global tax calculation, collection, compliance, and payouts on your behalf.

The integration uses the official @waffo/pancake-ts SDK with Waffo's hosted checkout and consumer portal, so no card data ever touches your server.

Setup

TanStarter template provides three pricing plans by default: a free plan, a pro subscription plan (monthly/yearly), and a lifetime plan (one-time payment). Follow these steps to set up:

Create Waffo Account

Sign up for a Waffo Pancake account at pancake.waffo.ai and create a store during onboarding. You can find more about the basics in the Waffo Quickstart.

Get API Keys

Get your Merchant ID and private key from the Waffo Dashboard:

  • Go to Waffo Dashboard > API & Development and click Create API Key to generate a new key pair
  • Copy the Merchant ID (starts with MER_) and the private key
  • Save them to your environment file as WAFFO_MERCHANT_ID and WAFFO_PRIVATE_KEY

WAFFO_MERCHANT_ID is the Merchant ID — not a storeId and not a store identifier from a URL. API keys are bound to either test or production at creation time, so create a separate key for each environment.

The private key is server-only. When storing a PEM key in an environment variable, preserve the line breaks with escaped \n:

WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEv...\n-----END PRIVATE KEY-----"

You can find more about API Key authentication in the Waffo documentation.

Set Up Webhook

Set up a webhook and subscribe to the payment events:

  • Go to Waffo Dashboard > Settings > Webhooks and click Add Webhook
  • Choose the Raw payload format (only Raw carries the X-Waffo-Signature header used for verification)
  • Enter Webhook URL: https://YOUR-DOMAIN.com/api/webhooks/waffo
  • All events are subscribed by default. Keep at least the following:
    • order.completed
    • subscription.activated
    • subscription.payment_succeeded
    • subscription.updated
    • subscription.canceling
    • subscription.uncanceled
    • subscription.canceled
    • subscription.past_due
    • refund.succeeded
    • refund.failed

Unlike Stripe and Creem, there is no webhook signing secret environment variable for Waffo. The @waffo/pancake-ts SDK embeds the verification public keys and auto-detects the environment, so verifyWebhook() handles signature verification for you.

You can find more about webhooks and webhook signature verification in the Waffo documentation.

Create Products and Pricing Plans

Create products in Waffo and set up pricing plans. Waffo uses Product IDs (starts with PROD_) for checkout, not price IDs:

  • Go to Waffo Dashboard > Products and click Create Product
  • Create the Pro subscription plan products:
    • Product type: Subscription
    • Name: Pro Plan
    • Billing period: Monthly — save and copy the Product ID for VITE_WAFFO_PRODUCT_PRO_MONTHLY
    • Create a second subscription product with billing period Yearly — save and copy the Product ID for VITE_WAFFO_PRODUCT_PRO_YEARLY
  • Create the Lifetime plan product:
    • Product type: One-time
    • Name: Lifetime Plan
    • Save and copy the Product ID for VITE_WAFFO_PRODUCT_LIFETIME

Create separate subscription products for each billing period (monthly/yearly), as recommended in the Waffo subscriptions guide. New products start in test mode; publish them to production before you accept real payments — see Publish Product.

Add Environment Variables

Add the following environment variables:

.env
# Payment provider
VITE_PAYMENT_PROVIDER=waffo

# Waffo API credentials (server-side only)
WAFFO_MERCHANT_ID=MER_...
WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEv...\n-----END PRIVATE KEY-----"

# Optional: accept test-mode webhooks in a production build (smoke testing only)
# WAFFO_DEBUG=true

# Product IDs
VITE_WAFFO_PRODUCT_PRO_MONTHLY=PROD_...
VITE_WAFFO_PRODUCT_PRO_YEARLY=PROD_...
VITE_WAFFO_PRODUCT_LIFETIME=PROD_...

Update Website Configuration

Update the payment section in src/config/website.ts to configure pricing plans — amounts, currencies, intervals, and plan metadata. The enable, provider, and priceId fields are automatically resolved from your environment variables (VITE_PAYMENT_PROVIDER and VITE_WAFFO_PRODUCT_*), so you don't need to hardcode them.

You must configure this section to match the products you created in Waffo:

src/config/website.ts
payment: {
  enable: isPaymentEnabled,              // ← auto: true when VITE_PAYMENT_PROVIDER is set
  provider: isPaymentEnabled ? paymentProvider : undefined, // ← auto: 'waffo'
  price: {
    plans: {
      free: {
        id: 'free',
        prices: [],
        isFree: true,
        isLifetime: false,
      },
      pro: {
        id: 'pro',
        prices: [
          {
            type: 'subscription',
            priceId: priceIds.proMonthly,  // ← auto: from VITE_WAFFO_PRODUCT_PRO_MONTHLY
            amount: 990,                   // amount in cents ($9.90)
            currency: 'USD',
            interval: 'month',
          },
          {
            type: 'subscription',
            priceId: priceIds.proYearly,   // ← auto: from VITE_WAFFO_PRODUCT_PRO_YEARLY
            amount: 9900,                  // amount in cents ($99.00)
            currency: 'USD',
            interval: 'year',
          },
        ],
        isFree: false,
        isLifetime: false,
        popular: true,
      },
      lifetime: {
        id: 'lifetime',
        prices: [
          {
            type: 'one_time',
            priceId: priceIds.lifetime,     // ← auto: from VITE_WAFFO_PRODUCT_LIFETIME
            amount: 19900,                  // amount in cents ($199.00)
            currency: 'USD',
            allowPromotionCode: true,
          },
        ],
        isFree: false,
        isLifetime: true,
      },
    },
  },
},

If you are setting up your environment, you can now go back to the Environment Configuration and continue. The rest of this document can be read later.

Environment Configuration

Set up environment variables


Core Features

  • One-time payment for lifetime membership
  • Recurring subscription payments (monthly/yearly)
  • Free trial period support
  • Hosted checkout with buyer attribution (authenticated checkout)
  • Webhook handling for payment, subscription, and refund events
  • Tax, compliance, and payouts handled by Waffo
  • Consumer portal with Magic Link login
  • Built-in pricing components (table, card, button)
  • Server-side actions for secure payment operations
  • Multiple pricing plan support (free, pro, lifetime)

Development

For local development, expose your server with an HTTPS tunnel so Waffo can reach your webhook endpoint. The official docs recommend ngrok; cloudflared also works. Do not use localtunnel — it strips custom HTTP headers such as X-Waffo-Signature:

ngrok http 3000
# or
cloudflared tunnel --url http://localhost:3000

Then:

  1. Set the tunnel URL (e.g. https://xxxx.ngrok-free.app/api/webhooks/waffo) as the test webhook URL in Waffo Dashboard > Settings > Webhooks. Never put a temporary tunnel URL into the production webhook.
  2. Make a test purchase on the website and verify the event flow works as expected.

The template also ships a Waffo sandbox E2E suite:

pnpm e2e:waffo

Waffo provides a complete test environment with test cards — no real charges. In the hosted sandbox checkout you can pick Credit/Debit Card and use the Quick Fill Success option to complete a payment instantly. See Test Mode in the Waffo documentation.

Production

  1. Complete the store review / KYB so production payments are enabled — see Account Reviews
  2. Publish your subscription and one-time products from test to production. Publishing is a one-way, first-publish-only operation — see Publish Product
  3. Create a production API key and use it for WAFFO_MERCHANT_ID / WAFFO_PRIVATE_KEY
  4. Add the production webhook URL https://YOUR-DOMAIN.com/api/webhooks/waffo in Waffo Dashboard > Settings > Webhooks
  5. Leave WAFFO_DEBUG unset (or set to false) so test-mode events are rejected in production — a sandbox purchase must never grant real access

Customer Portal

Waffo provides a hosted consumer portal at https://pancake.waffo.ai/consumer/portal/login. Customers log in with the email used for their purchase and a one-time Magic Link — no password required. In the portal they can:

  • View active subscriptions, next billing date, and payment history
  • Cancel or reactivate subscriptions
  • Download PDF invoices and receipts
  • Update billing details
  • Request refunds

The Billing page (/settings/billing) sends customers to this portal via the "Manage subscription" button. You can find more about the Customer Portal in the Waffo documentation.

Webhook Events

Waffo supports the following webhook events:

EventDescription
order.completedOne-time order payment succeeded
subscription.activatedSubscription first payment succeeded
subscription.payment_succeededRenewal payment succeeded (not the first)
subscription.updatedSubscription product changed (upgrade/downgrade)
subscription.cancelingCancellation requested — active until period ends
subscription.uncanceledCancellation withdrawn
subscription.canceledSubscription terminated (period ended)
subscription.past_dueRenewal payment failed, Waffo is retrying
refund.succeededRefund completed — access is revoked
refund.failedRefund failed

Waffo delivers test and production events to the same endpoint, and every payload carries a mode field. The template rejects events whose mode does not match the running environment; in a production build, set WAFFO_DEBUG=true only when smoke-testing a deployed Worker with a sandbox merchant.

Test Cards

To test the Waffo integration, use Waffo's test mode with these test cards:

CardTypeResult
4576 7500 0000 0110Visa CreditSuccess
2226 9000 0000 0110Mastercard CreditSuccess
4576 7500 0000 0220Visa CreditDeclined

Any future expiry date and any 3-digit CVC work. You can find more about test mode and test cards in the Waffo documentation.

Best Practices

  1. Protect API keys: Never expose WAFFO_PRIVATE_KEY or the Merchant ID in client-side code
  2. Validate webhook signatures: Always verify the X-Waffo-Signature header (the SDK's verifyWebhook() does this)
  3. Match environments: Use the test or production API key and webhook URL that correspond to the environment you are running
  4. Handle errors gracefully: Provide user-friendly error messages when payments fail
  5. Test webhooks thoroughly: Complete a full purchase in test mode before going live

References

Last updated on

On this page