EasyStarter logoEasyStarter
Payments

Waffo Payments

Configure Waffo Pancake hosted checkout, the consumer portal, and Webhooks

Waffo Pancake Payment Integration

EasyStarter's web app includes Waffo Pancake payment support for web-only scenarios, including:

  • Hosted subscription checkout (monthly / yearly)
  • Hosted one-time lifetime purchase checkout
  • Waffo product trials
  • Waffo Consumer Portal
  • Cancellation at the end of the billing period
  • Webhook handling for orders, subscriptions, renewals, and refunds

Payment setup is split into two parts:

  1. Environment variables: Merchant ID, private key, and runtime environment in the server's .dev.vars / .env.production
  2. Pricing plans: Waffo Product IDs and pricing metadata in packages/app-config/src/app-config.ts

The Waffo SDK is server-side only. Never expose WAFFO_PRIVATE_KEY through web environment variables or frontend code, and never commit it to Git.

Required environment variables

WAFFO_MERCHANT_ID=
WAFFO_PRIVATE_KEY=
WAFFO_ENVIRONMENT=test
VariableDescription
WAFFO_MERCHANT_IDMerchant ID in the MER_xxx format; this is not a Store ID
WAFFO_PRIVATE_KEYRSA private key for the current environment, used only by the server SDK
WAFFO_ENVIRONMENTWebhook verification environment: test in development and prod in production

Get the Merchant ID and test private key

  1. Sign in to the Waffo Pancake Merchant Dashboard
  2. Open Integration and select Test Mode
  3. Copy the Merchant ID at the top of the page (MER_xxx)
  4. Create a test key under Create API Key, then copy the private key or use Copy .env config
  5. Add the credentials to the local server environment:
apps/server/.dev.vars
WAFFO_MERCHANT_ID=MER_xxxxxxxxxxxxxxxxxxxxxxxx
WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
WAFFO_ENVIRONMENT=test

Waffo isolates test and production API keys. When switching the Dashboard environment, replace both the private key and WAFFO_ENVIRONMENT; otherwise API requests or Webhook verification will fail.

Keep the PEM format copied from the Dashboard. For a single-line .env value, preserve the quotes and escaped \n line breaks; the Waffo SDK normalizes the PEM value automatically.

Create products in Waffo

EasyStarter defaults to three plans: Free, Pro (monthly + yearly), and Lifetime (one-time purchase). The Free plan does not require a Waffo product.

Open Products in the Merchant Dashboard and create these three products:

EasyStarter priceWaffo product typeWaffo billing period
Pro MonthlySubscriptionMonthly
Pro YearlySubscriptionYearly
LifetimeOne-timeNot applicable

After saving each product, copy its Product ID (PROD_xxx). EasyStarter stores Waffo Product IDs in the shared providerPriceId field.

If you offer a trial, configure its duration on the Waffo subscription product or product group and keep the next step's trialDays value in sync.

Configure pricing plans

Add the Waffo Product IDs to web.payments.plans in packages/app-config/src/app-config.ts:

packages/app-config/src/app-config.ts
web: {
  payments: {
    provider: "waffo",
    plans: [
      {
        id: "free",
      },
      {
        id: "pro",
        prices: [
          {
            id: "monthly",
            provider: "waffo",
            test: {
              providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Test monthly Product ID
            },
            prod: {
              providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Production monthly Product ID
            },
            currency: "usd",
            amountCents: 1000,
            priceType: "subscription",
            interval: "month",
            trialDays: 7,
            status: "active",
          },
          {
            id: "yearly",
            provider: "waffo",
            test: {
              providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Test yearly Product ID
            },
            prod: {
              providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Production yearly Product ID
            },
            currency: "usd",
            amountCents: 10000,
            priceType: "subscription",
            interval: "year",
            trialDays: 7,
            status: "active",
          },
        ],
      },
      {
        id: "lifetime",
        prices: [
          {
            id: "lifetime",
            provider: "waffo",
            test: {
              providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Test one-time Product ID
            },
            prod: {
              providerPriceId: "PROD_xxxxxxxxxxxxxxxx", // Production one-time Product ID
            },
            currency: "usd",
            amountCents: 20000,
            priceType: "lifetime",
            status: "active",
          },
        ],
      },
    ],
  },
},

Field reference:

FieldDescription
providerSet both the web default and every price provider to "waffo"
test.providerPriceIdWaffo Product ID available in the test environment (PROD_xxx)
prod.providerPriceIdPublished Waffo Product ID available in production (PROD_xxx)
amountCentsAmount displayed by EasyStarter in cents; it must match the Waffo product price
priceType"subscription" for recurring or "lifetime" for a one-time purchase
interval"month" or "year" for subscriptions; omit for Lifetime
trialDaysA non-empty value asks checkout to enable a trial; Waffo controls the actual duration
status"active" to enable or "archived" to hide the price

Waffo product APIs use display amounts such as "10.00". EasyStarter's local catalog still uses cents, so $10.00 is amountCents: 1000.

Configure Waffo Webhooks

Webhooks are required to grant entitlements after checkout and keep subscription state synchronized.

  1. Open Settings → Webhooks in the Waffo Merchant Dashboard

  2. Add an HTTP Webhook and select the test or production environment that matches the current credentials

  3. Set the Endpoint URL:

    • Local development: https://your-ngrok-url/api/webhooks/waffo
    • Production: https://your-server.workers.dev/api/webhooks/waffo
  4. Subscribe to the events handled by EasyStarter:

    EventEasyStarter behavior
    order.completedComplete a one-time purchase or credit package order
    subscription.activatedActivate the subscription
    subscription.payment_succeededSynchronize a successful renewal
    subscription.cancelingMark cancellation at period end and keep current access
    subscription.uncanceledRestore a subscription that was scheduled to cancel
    subscription.updatedSynchronize subscription changes
    subscription.canceledMark the subscription as terminated
    subscription.past_dueMark a failed renewal as past due
    refund.succeededRevoke the corresponding lifetime or credit entitlement
    refund.failedRecord delivery without changing entitlements

Waffo signs the raw request body with RSA-SHA256 and sends the signature in x-waffo-signature. EasyStarter reads the raw text and verifies it with the Waffo SDK, so no separate Webhook secret is required.

Use ngrok to forward the Server's 3001 port during local development. Avoid tunnel services that strip custom request headers, because losing x-waffo-signature makes verification impossible.

ngrok http 3001

Start the app and complete a sandbox checkout

Start the Web and Server apps:

pnpm dev:web+server

Open the pricing page and complete a test checkout. Waffo checkout opens in a new tab so EasyStarter keeps the current merchant page state.

ScenarioTest card number
Successful payment4576 7500 0000 0110
Declined payment4576 7500 0000 0220

Use any future expiry date and any CVC. After payment, confirm the Server receives POST /api/webhooks/waffo with a 200 response, then check the subscription or lifetime entitlement at /settings/billing.

Production setup

Complete these checks before launch:

  1. Switch the Waffo Dashboard to Live Mode, then create and copy a separate production private key
  2. Publish the required products and confirm every prod.providerPriceId points to a Product ID available in production
  3. Register https://your-server.workers.dev/api/webhooks/waffo in the production environment
  4. Add the production server variables:
apps/server/.env.production
WAFFO_MERCHANT_ID=MER_xxxxxxxxxxxxxxxxxxxxxxxx
WAFFO_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
WAFFO_ENVIRONMENT=prod
  1. Follow Deploy Server to upload production secrets and deploy

Do not reuse the test private key in production, and do not configure a production Product ID before that product has been published.

Consumer Portal and current limitations

Users can open the Waffo Consumer Portal from /settings/billing. The current EasyStarter integration has these capability boundaries:

  • Cancellation at the end of the current billing period is supported
  • A buyer must undo a scheduled cancellation in the Waffo portal
  • Waffo's Change Subscription Product endpoint is not implemented yet and currently always returns 501 Not Implemented, so EasyStarter cannot offer in-app subscription upgrades or downgrades until Waffo makes this platform capability available
  • Waffo Webhooks still synchronize uncancellations and subscription changes completed in the portal

For platform details, see the official Waffo SDK integration guide and Webhook guide.