Waffo Pancake
How to set up and use Waffo Pancake for payments and subscriptions
MkSaaS supports Waffo Pancake for one-time payments and subscriptions. Waffo is a Merchant of Record (MoR), so it handles global tax calculation, collection, compliance, and payouts on your behalf.
The integration uses the official @waffo/pancake-ts SDK together with Waffo's hosted checkout and consumer portal. Card details do not pass through your application server.
Setup
MkSaaS includes a free plan, a Pro subscription plan with monthly and yearly intervals, and a Lifetime one-time plan by default.
Create a Waffo Account
Sign up at pancake.waffo.ai and create a store during onboarding. The Waffo Quickstart explains the basic account setup.
Get API Credentials
In Waffo Dashboard, open API & Development and click Create API Key.
Copy the Merchant ID and private key, then save them as
WAFFO_MERCHANT_ID and WAFFO_PRIVATE_KEY.
WAFFO_MERCHANT_ID is the Merchant ID, not a storeId or a store
identifier copied from a URL. API keys are bound to the test or
production environment when they are created, so create a separate key
for each environment.
The private key is server-only. Preserve PEM line breaks with escaped
\n characters when storing it in an environment variable:
WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEv...\n-----END PRIVATE KEY-----"Configure the Webhook
In Waffo Dashboard > Settings > Webhooks, click Add Webhook and
configure:
- Payload format: Raw. This format includes the
X-Waffo-Signatureheader required for verification. - URL:
https://YOUR-DOMAIN.com/api/webhooks/waffo - Events: keep the payment, subscription, and refund events listed in the Webhook Events section.
Waffo does not require a webhook secret environment variable. The
@waffo/pancake-ts SDK contains the verification keys and verifies the
raw request body with verifyWebhook().
Create Products
Waffo checkout uses Product IDs beginning with PROD_, rather than
Stripe-style price IDs:
- Create a monthly Subscription product and save its ID as
NEXT_PUBLIC_WAFFO_PRODUCT_PRO_MONTHLY. - Create a yearly Subscription product and save its ID as
NEXT_PUBLIC_WAFFO_PRODUCT_PRO_YEARLY. - Create a One-time product and save its ID as
NEXT_PUBLIC_WAFFO_PRODUCT_LIFETIME. - Create additional products for credit packages if credit purchases are enabled.
New products start in test mode. Publish them to production before accepting real payments; publishing is a one-way operation. See the Waffo subscriptions guide and Publish Product.
Add Environment Variables
Set the provider and Waffo credentials in .env:
NEXT_PUBLIC_PAYMENT_PROVIDER=waffo
# Server-side credentials
WAFFO_MERCHANT_ID=MER_...
WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEv...\n-----END PRIVATE KEY-----"
# Optional: accept sandbox webhooks in a production build for smoke tests
# WAFFO_DEBUG=true
# Product IDs
NEXT_PUBLIC_WAFFO_PRODUCT_PRO_MONTHLY=PROD_...
NEXT_PUBLIC_WAFFO_PRODUCT_PRO_YEARLY=PROD_...
NEXT_PUBLIC_WAFFO_PRODUCT_LIFETIME=PROD_...
# Optional credit-package product IDs
NEXT_PUBLIC_WAFFO_PRODUCT_CREDITS_BASIC=PROD_...
NEXT_PUBLIC_WAFFO_PRODUCT_CREDITS_STANDARD=PROD_...
NEXT_PUBLIC_WAFFO_PRODUCT_CREDITS_PREMIUM=PROD_...
NEXT_PUBLIC_WAFFO_PRODUCT_CREDITS_ENTERPRISE=PROD_...Configure Pricing
Keep the existing price.plans configuration in
src/config/website.tsx, but make its priceId values resolve to the
Waffo product IDs when Waffo is active. The amount, currency, interval,
credits, and plan metadata must match the products configured in Waffo.
price: {
plans: {
pro: {
id: 'pro',
prices: [
{
type: PaymentTypes.SUBSCRIPTION,
priceId: process.env.NEXT_PUBLIC_WAFFO_PRODUCT_PRO_MONTHLY!,
amount: 990,
currency: 'USD',
interval: PlanIntervals.MONTH,
},
{
type: PaymentTypes.SUBSCRIPTION,
priceId: process.env.NEXT_PUBLIC_WAFFO_PRODUCT_PRO_YEARLY!,
amount: 9900,
currency: 'USD',
interval: PlanIntervals.YEAR,
},
],
isFree: false,
isLifetime: false,
},
},
},Core Features
- Monthly and yearly recurring subscriptions
- Lifetime one-time payments
- Hosted checkout without handling card data on your server
- Webhook handling for payments, subscriptions, and refunds
- Waffo-managed tax, compliance, and payouts
- Shared consumer portal with Magic Link login
- Provider-agnostic pricing, checkout, credits, and billing components
Development
Waffo must be able to reach your webhook endpoint during local development. Expose the local server through an HTTPS tunnel:
ngrok http 3000
# or
cloudflared tunnel --url http://localhost:3000Register the tunnel URL as a test webhook, for example
https://xxxx.ngrok-free.app/api/webhooks/waffo. Do not put a temporary
tunnel URL in the production webhook. Avoid localtunnel because it can strip
the X-Waffo-Signature header.
The template includes a Waffo sandbox E2E suite:
pnpm e2e:waffoThe sandbox checkout does not create real charges. Choose Credit/Debit Card
and use the Quick Fill Success option, or use one of the test cards
listed below. The ignored MkFast Template environment file can be reused by
setting WAFFO_ENV_FILE when running the local E2E helper.
Production
- Complete Waffo store review / KYB so production payments are enabled.
- Publish the subscription and one-time products from test to production.
- Create production API credentials and set
WAFFO_MERCHANT_IDandWAFFO_PRIVATE_KEYto those credentials. - Register
https://YOUR-DOMAIN.com/api/webhooks/waffoas the production webhook. - Leave
WAFFO_DEBUGunset or set it tofalse. Sandbox events must never grant access in a production build.
Customer Portal
Waffo provides the shared consumer portal at
https://pancake.waffo.ai/consumer/portal/login. Customers sign in with the
email used for their purchase and a one-time Magic Link. They can view
subscriptions and invoices, cancel or reactivate subscriptions, update billing
details, and request refunds.
Webhook Events
Configure the following events for /api/webhooks/waffo:
| Event | Description |
|---|---|
order.completed | One-time order payment succeeded |
subscription.activated | Subscription first payment succeeded |
subscription.payment_succeeded | Renewal payment succeeded |
subscription.updated | Subscription product changed |
subscription.canceling | Cancellation requested; active until period end |
subscription.uncanceled | Cancellation withdrawn |
subscription.canceled | Subscription terminated |
subscription.past_due | Renewal payment failed and is being retried |
refund.succeeded | Refund completed and access is revoked |
refund.failed | Refund failed |
Waffo sends test and production events to the same endpoint and includes a
mode field in every payload. MkSaaS rejects events whose mode does not match
the running environment.
Test Cards
Use Waffo test mode with any future expiry date and any three-digit CVC:
| Card | Type | Result |
|---|---|---|
4576 7500 0000 0110 | Visa Credit | Success |
2226 9000 0000 0110 | Mastercard Credit | Success |
4576 7500 0000 0220 | Visa Credit | Declined |
Best Practices
- Keep
WAFFO_PRIVATE_KEYandWAFFO_MERCHANT_IDon the server only. - Use the Raw webhook format so
X-Waffo-Signatureis preserved. - Use test and production credentials in their matching environments.
- Complete a full sandbox checkout and webhook round trip before going live.
Documents MkSaaS