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. 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_...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,
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, 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.
MkSaaS 문서