=============== LIBRARY RULES =============== From library maintainers: - - Use the official SDKs: @waffo/pancake-ts for TypeScript, @waffo/pancake-nextjs for Next.js projects. - Always verify webhook signatures before processing events. - Deduplicate webhook events using eventType + eventId as a composite key, since different ### Install Next.js SDK Source: https://docs.waffo.ai/integrate/sdks Install the @waffo/pancake-nextjs package for drop-in components, hooks, and server actions. ```bash npm install @waffo/pancake-nextjs ``` -------------------------------- ### Run interactive migration with npx @waffo/pancake-migrate Source: https://docs.waffo.ai/integrate/migrate Starts the interactive migration wizard. It guides you through choosing the source (Stripe or Creem), selecting or creating a target store, previewing the migration, and then migrating products, prices, images, webhooks, and branding. ```bash npx @waffo/pancake-migrate ``` -------------------------------- ### Create group - SDK example Source: https://docs.waffo.ai/api-reference/endpoints/subscription-products/create-group Example using the SDK to create a product group. The response is assigned to the `group` variable. ```javascript const { group } = await client.subscriptionProductGroups.create({ storeId: "STO_2D5F8G3H1K4M6N9P", name: "Pricing Plans", description: "Free, Pro, and Enterprise tiers", rules: { sharedTrial: true, selfServicePlanChange: true }, productIds: [ "PROD_3F7H2J5L8N1Q4S6U", "PROD_8B4D6F9H2K5M7P1R", "PROD_1C3E5G7J0L2N4Q6S", ], }); ``` -------------------------------- ### Create group - Python example Source: https://docs.waffo.ai/api-reference/endpoints/subscription-products/create-group Example using Python requests to create a product group. Uses a `sign_request` helper for authentication headers. ```python body = { "storeId": "STO_2D5F8G3H1K4M6N9P", "name": "Pricing Plans", "description": "Free, Pro, and Enterprise tiers", "rules": {"sharedTrial": True, "selfServicePlanChange": True}, "productIds": [ "PROD_3F7H2J5L8N1Q4S6U", "PROD_8B4D6F9H2K5M7P1R", "PROD_1C3E5G7J0L2N4Q6S", ], } response = requests.post( "https://api.waffo.ai/v1/actions/subscription-product-group/create-group", json=body, headers=sign_request("POST", "/v1/actions/subscription-product-group/create-group", body), ) group = response.json()["data"]["group"] ``` -------------------------------- ### Installation: npm install @waffo/pancake-nextjs Source: https://docs.waffo.ai/integrate/sdks Install the SDK via npm. Requires @waffo/pancake-ts as a peer dependency and works with Next.js 14+ App Router. ```bash npm install @waffo/pancake-nextjs ``` -------------------------------- ### SDK Example for One-Time Products Source: https://docs.waffo.ai/api-reference/endpoints/graphql/stores-and-products TypeScript SDK example using client.graphql.query to fetch one-time products and count with variables. ```TypeScript const result = await client.graphql.query<{ onetimeProducts: Array<{ id: string; name: string; prices: Record; status: string; }>; onetimeProductsCount: number; }>({ query: `query($storeId: String!) { onetimeProducts(filter: { storeId: { eq: $storeId }, status: { eq: "active" } }, limit: 20) { id name prices status } onetimeProductsCount(filter: { storeId: { eq: $storeId } }) }`, variables: { storeId: "STO_3bVzrkD0FJjFdZNLk8Ualx" }, }); ``` -------------------------------- ### wget example Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/update-status Builds a signed request using the merchant ID, timestamp, and private key, then sends it with wget. ```bash MERCHANT_ID="MER_2aUyqjCzEIiEcYMKj7TZtw" TIMESTAMP=$(date +%s) BODY='{"id":"PROD_3kF9mNpQrStUvWxYz1A2bC","status":"inactive"}' BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -binary | base64 | tr -d '\n') CANONICAL_REQUEST="POST /v1/actions/onetime-product/update-status $TIMESTAMP $BODY_HASH" SIGNATURE=$(echo -n "$CANONICAL_REQUEST" | openssl dgst -sha256 -sign private_key.pem | base64 | tr -d '\n') wget -qO- "https://api.waffo.ai/v1/actions/onetime-product/update-status" \ --header="Content-Type: application/json" \ --header="X-Merchant-Id: $MERCHANT_ID" \ --header="X-Timestamp: $TIMESTAMP" \ --header="X-Signature: $SIGNATURE" \ --post-data="$BODY" ``` -------------------------------- ### SDK Example Source: https://docs.waffo.ai/api-reference/endpoints/graphql/orders-and-payments Example of using the GraphQL query via the SDK client to fetch completed orders. ```APIDOC ## SDK Example: Querying onetimeOrders ### Description Demonstrates using the SDK's `client.graphql.query` method to fetch completed one-time orders. ### Usage ```typescript const result = await client.graphql.query<{ onetimeOrders: Array<{ id: string; buyerEmail: string; priceSnapshot: { total: number; currency: string }; status: string; }>; }>({ query: `query($storeId: String!) { onetimeOrders(storeId: $storeId, filter: { status: { eq: "completed" } }) { id buyerEmail priceSnapshot { total currency } status } }`, variables: { storeId: "STO_3bVzrkD0FJjFdZNLk8Ualx" }, }); ``` ### Parameters - **query** (String) - The GraphQL query string. - **variables** (Object) - Query variables, including `storeId`. ``` -------------------------------- ### Install @waffo/pancake-ts SDK Source: https://docs.waffo.ai/api-reference/introduction Install the official TypeScript SDK via npm. ```bash npm install @waffo/pancake-ts ``` -------------------------------- ### Go example Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/update-product Uses the callAPI() helper from authentication.mdx. Builds a map for the request body and logs any error. ```go // Uses callAPI() helper from authentication.mdx body := map[string]interface{}{ "id": "PROD_3kF9mNpQrStUvWxYz1A2bC", "name": "Premium Template Pack v2", "description": "75 premium design templates — expanded collection.", "prices": map[string]interface{}{ "USD": map[string]interface{}{"amount": "59.00", "taxIncluded": false, "taxCategory": "digital_goods"}, "EUR": map[string]interface{}{"amount": "55.00", "taxIncluded": true, "taxCategory": "digital_goods"}, }, "successUrl": "https://example.com/thank-you", } result, err := callAPI("POST", "/v1/actions/onetime-product/update-product", body) if err != nil { log.Fatal(err) } fmt.Println(string(result)) ``` -------------------------------- ### C++ example Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/update-status Uses the callApi() helper from authentication.mdx with a raw string body and prints the response. ```cpp // Uses callApi() helper from authentication.mdx std::string body = R"({"id":"PROD_3kF9mNpQrStUvWxYz1A2bC","status":"inactive"})"; std::string response = callApi("POST", "/v1/actions/onetime-product/update-status", body); std::cout << response << std::endl; ``` -------------------------------- ### ngrok forwarding output example Source: https://docs.waffo.ai/guides/webhooks Example output from ngrok showing the public forwarding URL that should be used as the webhook endpoint. ```text Forwarding https://abc-123-456-789.ngrok-free.app -> http://localhost:3000 ``` -------------------------------- ### Create group - cURL example Source: https://docs.waffo.ai/api-reference/endpoints/subscription-products/create-group Example using cURL to create a product group. Requires the X-Merchant-Id, X-Timestamp, and X-Signature headers. ```bash curl -X POST https://api.waffo.ai/v1/actions/subscription-product-group/create-group \ -H "Content-Type: application/json" \ -H "X-Merchant-Id: MER_7K3M9P2R5T8W1Y4Z" \ -H "X-Timestamp: 2026-03-30T10:00:00Z" \ -H "X-Signature: BASE64_RSA_SIGNATURE" \ -d '{ "storeId": "STO_2D5F8G3H1K4M6N9P", "name": "Pricing Plans", "description": "Free, Pro, and Enterprise tiers", "rules": { "sharedTrial": true, "selfServicePlanChange": true }, "productIds": [ "PROD_3F7H2J5L8N1Q4S6U", "PROD_8B4D6F9H2K5M7P1R", "PROD_1C3E5G7J0L2N4Q6S" ] }' ``` -------------------------------- ### Python example Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/update-status Uses the call_api() helper from authentication.mdx to send a POST request and print the resulting product status. ```python # Uses call_api() helper from authentication.mdx result = call_api("POST", "/v1/actions/onetime-product/update-status", { "id": "PROD_3kF9mNpQrStUvWxYz1A2bC", "status": "inactive", }) print(result["data"]["product"]["status"]) # => "inactive" ``` -------------------------------- ### Authentication Example (SDK) Source: https://docs.waffo.ai/features/integrations Server-to-server authentication using the WaffoPancake SDK. Requires WAFFO_MERCHANT_ID and WAFFO_PRIVATE_KEY environment variables. ```typescript import { WaffoPancake } from "@waffo/pancake-ts"; const client = new WaffoPancake({ merchantId: process.env.WAFFO_MERCHANT_ID!, privateKey: process.env.WAFFO_PRIVATE_KEY!, }); const { store } = await client.stores.create({ name: "My Store" }); ``` -------------------------------- ### Python example Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/update-product Uses the call_api() helper from authentication.mdx. Sends a POST request and prints the updated product name. ```python # Uses call_api() helper from authentication.mdx result = call_api("POST", "/v1/actions/onetime-product/update-product", { "id": "PROD_3kF9mNpQrStUvWxYz1A2bC", "name": "Premium Template Pack v2", "description": "75 premium design templates — expanded collection.", "prices": { "USD": {"amount": "59.00", "taxIncluded": False, "taxCategory": "digital_goods"}, "EUR": {"amount": "55.00", "taxIncluded": True, "taxCategory": "digital_goods"}, }, "successUrl": "https://example.com/thank-you", }) print(result["data"]["product"]["name"]) # => "Premium Template Pack v2" ``` -------------------------------- ### cURL example Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/update-status Builds a signed request using the merchant ID, timestamp, and private key, then sends it with curl. ```bash MERCHANT_ID="MER_2aUyqjCzEIiEcYMKj7TZtw" TIMESTAMP=$(date +%s) BODY='{"id":"PROD_3kF9mNpQrStUvWxYz1A2bC","status":"inactive"}' BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -binary | base64 | tr -d '\n') CANONICAL_REQUEST="POST /v1/actions/onetime-product/update-status $TIMESTAMP $BODY_HASH" SIGNATURE=$(echo -n "$CANONICAL_REQUEST" | openssl dgst -sha256 -sign private_key.pem | base64 | tr -d '\n') curl -X POST "https://api.waffo.ai/v1/actions/onetime-product/update-status" \ -H "Content-Type: application/json" \ -H "X-Merchant-Id: $MERCHANT_ID" \ -H "X-Timestamp: $TIMESTAMP" \ -H "X-Signature: $SIGNATURE" \ -d "$BODY" ``` -------------------------------- ### Publish Group - SDK Example Source: https://docs.waffo.ai/api-reference/endpoints/subscription-products/publish-group Publishes a product group using the SDK. The group ID must be a test environment group. ```javascript await client.subscriptionProductGroups.publish({ id: "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a", }); ``` -------------------------------- ### Start cloudflared tunnel as ngrok alternative Source: https://docs.waffo.ai/guides/webhooks Alternative to ngrok that requires no account and preserves headers. Prints a *.trycloudflare.com URL. ```bash cloudflared tunnel --url http://localhost:3000 ``` -------------------------------- ### Java example Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/update-product Uses the callApi() helper from authentication.mdx. Builds a JSON body string and prints the response. ```java // Uses callApi() helper from authentication.mdx String body = """ { "id": "PROD_3kF9mNpQrStUvWxYz1A2bC", "name": "Premium Template Pack v2", "description": "75 premium design templates — expanded collection.", "prices": { "USD": { "amount": "59.00", "taxIncluded": false, "taxCategory": "digital_goods" }, "EUR": { "amount": "55.00", "taxIncluded": true, "taxCategory": "digital_goods" } }, "successUrl": "https://example.com/thank-you" }"""; String response = callApi("POST", "/v1/actions/onetime-product/update-product", body); System.out.println(response); ``` -------------------------------- ### Rust example Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/update-product Uses the call_api() helper from authentication.mdx. Builds a JSON body with serde_json::json! and prints the result. ```rust // Uses call_api() helper from authentication.mdx let body = serde_json::json!({ "id": "PROD_3kF9mNpQrStUvWxYz1A2bC", "name": "Premium Template Pack v2", "description": "75 premium design templates — expanded collection.", "prices": { "USD": { "amount": "59.00", "taxIncluded": false, "taxCategory": "digital_goods" }, "EUR": { "amount": "55.00", "taxIncluded": true, "taxCategory": "digital_goods" } }, "successUrl": "https://example.com/thank-you" }); let result = call_api("POST", "/v1/actions/onetime-product/update-product", &body).await?; println!("{}", result); ``` -------------------------------- ### @waffo/pancake-nextjs SDK Reference Source: https://docs.waffo.ai/integrate/sdks Comprehensive reference for the @waffo/pancake-nextjs SDK, covering installation, client and server exports, quick usage examples, and webhook route handler setup. ```APIDOC ## [NPM] @waffo/pancake-nextjs ### Description Official Next.js SDK for Waffo — components, hooks, and server actions. ### Installation ```bash npm install @waffo/pancake-nextjs ``` Requires `@waffo/pancake-ts` as a peer dependency. Works with Next.js 14+ App Router. ### Client Exports | Export | Type | Description | |--------|------|-------------| | `CheckoutButton` | Component | Three-mode checkout button (link / anonymous / authenticated) | | `WaffoPancakeProvider` | Component | Buyer self-service context with automatic token lifecycle | | `useCheckout()` | Hook | Programmatic checkout control | | `useBuyer()` | Hook | Buyer self-service actions (cancel, refund, reactivate, query) | | `useBuyerOrders()` | Hook | Fetch buyer’s orders | | `useBuyerPayments()` | Hook | Fetch buyer’s payment records | | `useBuyerRefundTickets()` | Hook | Fetch buyer’s refund tickets | | `useMerchantOrders()` | Hook | Merchant dashboard orders | | `useMerchantSales()` | Hook | Merchant sales overview | | `useMerchantSubscriptions()` | Hook | Merchant subscription overview | | `Webhook` | Route Handler | Auto-verify signatures and dispatch events | ### Server Exports (`@waffo/pancake-nextjs/server`) | Export | Description | |--------|-------------| | `createCheckoutAction(config)` | Server Action: create checkout sessions and plan change links (`type: "planChange"`) | | `createBuyerTokenAction(config)` | Server Action: issue buyer session tokens | | `createBuyerSessionAction(config)` | Server Action: buyer self-service operations | | `createMerchantQueryAction(config)` | Server Action: merchant GraphQL queries | | `createCustomerPortalRedirect(config, { resolveAuthenticatedCustomer, loginUrl?, onError? })` | Route Handler: 302 a signed-in customer into your store’s customer portal. Without `loginUrl`, signed-out visitors get a 401; `onError` replaces the default 502 (guide) | ### Quick Example ```ts // app/lib/actions.ts "use server"; import { createCheckoutAction, createBuyerTokenAction } from "@waffo/pancake-nextjs/server"; const config = { merchantId: process.env.WAFFO_MERCHANT_ID!, privateKey: process.env.WAFFO_PRIVATE_KEY!, }; export const checkoutAction = createCheckoutAction(config); export const issueBuyerToken = createBuyerTokenAction(config); ``` ```tsx // app/components/BuyButton.tsx "use client"; import { CheckoutButton } from "@waffo/pancake-nextjs"; import { checkoutAction } from "@/app/lib/actions"; export function BuyButton({ productId }: { productId: string }) { return ( Subscribe Now ); } ``` ### Webhook Route Handler ```ts // app/api/webhooks/waffo/route.ts import { Webhook } from "@waffo/pancake-nextjs"; export const POST = Webhook({ onOrderCompleted: async (event) => { // Deliver digital goods }, onSubscriptionActivated: async (event) => { // Provision access }, onSubscriptionCanceled: async (event) => { // Revoke access }, }); ``` ``` -------------------------------- ### Using the SDK (Recommended) Source: https://docs.waffo.ai/api-reference/authentication Shows how to initialize the WaffoPancake client with merchant ID and private key, and make a request to create a store. ```APIDOC ## Using the SDK (Recommended) ### Description Shows how to initialize the WaffoPancake client with merchant ID and private key, and make a request to create a store. ### Code Example typescript import { WaffoPancake } from "@waffo/pancake-ts"; const client = new WaffoPancake({ merchantId: process.env.WAFFO_MERCHANT_ID!, privateKey: process.env.WAFFO_PRIVATE_KEY!, }); // All requests are automatically signed const { store } = await client.stores.create({ name: "My Store" }); ``` -------------------------------- ### Install Pancake plugin for OpenClaw and Hermes Source: https://docs.waffo.ai/guides/webhooks Setup commands to install the Pancake plugin for OpenClaw or Hermes flavors. Run the matching command for your stack; after installation, the plugin provides a relay URL of the form https://relay.waffo.ai/webhook/. ```bash # OpenClaw npx -p @waffo/pancake-plugin openclaw-setup # Hermes npx -p @waffo/pancake-plugin hermes-setup ``` -------------------------------- ### Quick Start: create store, products, and checkout session Source: https://docs.waffo.ai/integrate/ai-integration Create a store, subscription products, and a checkout session. Redirect the customer to session.checkoutUrl. ```typescript // 1. Create a store const { store } = await client.stores.create({ name: "My SaaS" }); // 2. Create products const { product: monthly } = await client.subscriptionProducts.create({ storeId: store.id, name: "Pro Monthly", billingPeriod: "monthly", prices: { USD: { amount: "9.99", taxIncluded: true, taxCategory: "saas" } }, }); const { product: yearly } = await client.subscriptionProducts.create({ storeId: store.id, name: "Pro Yearly", billingPeriod: "yearly", prices: { USD: { amount: "99.00", taxIncluded: true, taxCategory: "saas" } }, }); // 3. Create a checkout session const session = await client.checkout.createSession({ productId: monthly.id, productType: "subscription", currency: "USD", }); // Redirect customer to session.checkoutUrl ``` -------------------------------- ### TypeScript SDK Quick Start Source: https://docs.waffo.ai/integrate/sdks Initialize the client, create a store, a one-time product, and a checkout session. Requires WAFFO_MERCHANT_ID and WAFFO_PRIVATE_KEY environment variables. ```typescript import { WaffoPancake } from "@waffo/pancake-ts"; const client = new WaffoPancake({ merchantId: process.env.WAFFO_MERCHANT_ID!, privateKey: process.env.WAFFO_PRIVATE_KEY!, }); // Create a store const { store } = await client.stores.create({ name: "My Store" }); // Create a one-time product const { product } = await client.onetimeProducts.create({ storeId: store.id, name: "E-Book: TypeScript Handbook", prices: { USD: { amount: "29.00", taxIncluded: false, taxCategory: "digital_goods" }, }, }); // Create a checkout session const session = await client.checkout.createSession({ productId: product.id, currency: "USD", }); // => redirect consumer to session.checkoutUrl ``` -------------------------------- ### Pagination Query Source: https://docs.waffo.ai/api-reference/endpoints/graphql/overview Example GraphQL query demonstrating pagination using limit and offset, and a count query to get total counts. ```APIDOC ## GraphQL Query: Pagination ### Description Use `limit` and `offset` for pagination. Use `*Count` queries to get total counts. ### Query ```graphql query { onetimeProducts( filter: { storeId: { eq: "STO_3bVzrkD0FJjFdZNLk8Ualx" } } limit: 10 offset: 0 ) { id name } onetimeProductsCount( filter: { storeId: { eq: "STO_3bVzrkD0FJjFdZNLk8Ualx" } } ) } ``` ### Parameters | Parameter | Type | Description | |-----------|------|-------------| | `limit` | integer | Maximum number of results to return | | `offset` | integer | Number of results to skip | ``` -------------------------------- ### Get Current Billing Period Information Source: https://docs.waffo.ai/api-reference/endpoints/graphql/orders-and-payments Query to retrieve the current billing period details (start, end, number) for active subscriptions. The period number is reported by the payment channel and not computed by Waffo. ```GraphQL query($storeId: String!) { subscriptionOrders(storeId: $storeId, filter: { status: { in: ["active"] } }) { id orderMerchantExternalId currentPeriodStart currentPeriodEnd currentPeriodNumber } } ``` -------------------------------- ### Path A: Create Store, Products, and Checkout Session via SDK Source: https://docs.waffo.ai/integrate/skill This TypeScript example shows the full workflow for a fresh integration: create a store, create subscription products, and then create a checkout session. It uses the WaffoPancake SDK and requires the two environment variables. ```typescript import { WaffoPancake } from "@waffo/pancake-ts"; const client = new WaffoPancake({ merchantId: process.env.WAFFO_MERCHANT_ID!, privateKey: process.env.WAFFO_PRIVATE_KEY!, }); // 1. Create a store const { store } = await client.stores.create({ name: "My SaaS" }); console.log("Store ID:", store.id); // Save this // 2. Create products const { product: monthly } = await client.subscriptionProducts.create({ storeId: store.id, name: "Pro Monthly", billingPeriod: "monthly", prices: { USD: { amount: "9.99", taxIncluded: true, taxCategory: "saas" } }, }); const { product: yearly } = await client.subscriptionProducts.create({ storeId: store.id, name: "Pro Yearly", billingPeriod: "yearly", prices: { USD: { amount: "99.00", taxIncluded: true, taxCategory: "saas" } }, }); console.log("Monthly Product ID:", monthly.id); // Save this console.log("Yearly Product ID:", yearly.id); // Save this // 3. Create a checkout session const session = await client.checkout.createSession({ productId: monthly.id, productType: "subscription", currency: "USD", }); // Redirect customer to session.checkoutUrl ``` -------------------------------- ### Quick Example: Server Actions (app/lib/actions.ts) Source: https://docs.waffo.ai/integrate/sdks Creates server actions for checkout and buyer token issuance using environment variables for merchant ID and private key. ```typescript // app/lib/actions.ts "use server"; import { createCheckoutAction, createBuyerTokenAction } from "@waffo/pancake-nextjs/server"; const config = { merchantId: process.env.WAFFO_MERCHANT_ID!, privateKey: process.env.WAFFO_PRIVATE_KEY!, }; export const checkoutAction = createCheckoutAction(config); export const issueBuyerToken = createBuyerTokenAction(config); ``` -------------------------------- ### Create product and query via GraphQL with Waffo SDK Source: https://docs.waffo.ai/integrate/sdks Shows how to create a product using the onetimeProducts.create method and query data via the GraphQL client. API key authentication is handled automatically by the SDK, so no manual header setup is needed. ```typescript // Create a product const { product } = await client.onetimeProducts.create({ storeId: "your-store-uuid", name: "My Product", prices: { USD: { amount: "29.00", taxIncluded: false, taxCategory: "saas" }, }, }); // Query data via GraphQL const data = await client.graphql.query<{ stores: Array<{ id: string; name: string; status: string }> }>( `{ stores { id name status } }` ); ``` -------------------------------- ### Create One-Time Product with Python Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/create-product Python example that signs the request with a private key and sends a POST to the create-product endpoint. Requires the cryptography library and reads private_key.pem. ```python import hashlib, time, base64, json, requests from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding merchant_id = "MER_2aUyqjCzEIiEcYMKj7TZtw" timestamp = str(int(time.time())) body = { "storeId": "STO_2aUyqjCzEIiEcYMKj7TZtw", "name": "Premium Template Pack", "description": "50 premium design templates for your next project.", "prices": { "USD": {"amount": "49.00", "taxIncluded": False, "taxCategory": "digital_goods"}, "EUR": {"amount": "45.00", "taxIncluded": True, "taxCategory": "digital_goods"}, }, "successUrl": "https://example.com/thank-you", } body_json = json.dumps(body, separators=(",", ":")) body_hash = base64.b64encode(hashlib.sha256(body_json.encode()).digest()).decode() path = "/v1/actions/onetime-product/create-product" canonical = f"POST\n{path}\n{timestamp}\n{body_hash}" with open("private_key.pem", "rb") as f: private_key = serialization.load_pem_private_key(f.read(), password=None) signature = base64.b64encode( private_key.sign(canonical.encode(), padding.PKCS1v15(), hashes.SHA256()) ).decode() resp = requests.post( f"https://api.waffo.ai{path}", headers={ "Content-Type": "application/json", "X-Merchant-Id": merchant_id, "X-Timestamp": timestamp, "X-Signature": signature, }, data=body_json, ) print(resp.json()) ``` -------------------------------- ### Filtering Example: onetimeProducts Source: https://docs.waffo.ai/api-reference/endpoints/graphql/overview Example query using typed filter objects to filter onetimeProducts by storeId and status. ```APIDOC ## Filtering Example: onetimeProducts ### Description Example query using typed filter objects to filter onetimeProducts by storeId and status. ### Method GraphQL Query ### Endpoint GraphQL endpoint (use standard GraphQL introspection) ### Parameters #### Query Arguments - **filter** (FilterInput) - Required - Filter object with typed fields. Supports StringFilter, DateTimeFilter, IntFilter, AmountFilter, BooleanFilter. ### Request Example ```graphql query { onetimeProducts( filter: { storeId: { eq: "STO_3bVzrkD0FJjFdZNLk8Ualx" } status: { eq: "active" } } ) { id name prices } } ``` ### Response #### Success Response Returns a list of onetime products with the requested fields. #### Response Example ```json { "data": { "onetimeProducts": [ { "id": "...", "name": "...", "prices": [...] } ] } } ``` ``` -------------------------------- ### Waffo Pancake SDK - Client Initialization Source: https://docs.waffo.ai/integrate/skill Shows how to instantiate the WaffoPancake client with a private key from a base64 environment variable or a file path. ```APIDOC ## WaffoPancake Client Initialization ### Description Creates a new WaffoPancake client instance using the provided merchant ID and RSA private key. The SDK auto-normalizes all key formats and throws immediately if the key is invalid. ### Constructor `new WaffoPancake({ merchantId, privateKey })` ### Parameters - **merchantId** (string) - Required - The merchant identifier. - **privateKey** (string) - Required - The RSA private key in PEM format, base64-encoded, or a file path. ### Example (Base64 env var) ```typescript const privateKey = Buffer.from(process.env.WAFFO_PRIVATE_KEY_BASE64!, "base64").toString("utf-8"); const client = new WaffoPancake({ merchantId, privateKey }); ``` ### Example (File path) ```typescript import { readFileSync } from "fs"; const privateKey = readFileSync("./keys/private.pem", "utf-8"); const client = new WaffoPancake({ merchantId, privateKey }); ``` ### Notes - The SDK auto-normalizes PEM headers, raw base64, literal `\n`, and Windows line endings. - The constructor throws immediately if the key is invalid. ``` -------------------------------- ### Full AI Integration Prompt with Steps Source: https://docs.waffo.ai/integrate/ai-integration Use this prompt for a complete integration with end-to-end tests. It includes steps for obtaining credentials, installing the SDK, creating endpoints, and testing with a specific card. ```text Read https://docs.waffo.ai/llms-full.txt, load the official Waffo Pancake skill from https://docs.waffo.ai/integrate/skill, and use Waffo Pancake SDK to integrate Waffo Pancake payments into the current project and run through the full checkout flow: 1. Get Merchant ID from Dashboard → API & Development → "Create an API Key" section → first row above the key list → copy button (use this as `WAFFO_MERCHANT_ID`, not `storeId`) 2. Create an API Key from Dashboard → API & Development → API Keys 3. Use only `WAFFO_MERCHANT_ID` and `WAFFO_PRIVATE_KEY` as required env vars for the first working integration 4. Install @waffo/pancake-ts SDK 5. Create checkout and webhook endpoints 6. Test with card 4576750000000110 7. Verify webhook receives order.completed event Use test environment. ``` -------------------------------- ### Publish Group - wget Example Source: https://docs.waffo.ai/api-reference/endpoints/subscription-products/publish-group Publishes a product group using wget with API Key authentication. The group ID must be a test environment group. ```bash MERCHANT_ID="MER_2aUyqjCzEIiEcYMKj7TZtw" TIMESTAMP=$(date +%s) BODY='{"id":"d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a"}' BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -binary | base64 | tr -d '\n') CANONICAL_REQUEST="POST /v1/actions/subscription-product-group/publish-group $TIMESTAMP $BODY_HASH" SIGNATURE=$(echo -n "$CANONICAL_REQUEST" | openssl dgst -sha256 -sign private_key.pem | base64 | tr -d '\n') wget -qO- "https://api.waffo.ai/v1/actions/subscription-product-group/publish-group" \ --header="Content-Type: application/json" \ --header="X-Merchant-Id: $MERCHANT_ID" \ --header="X-Timestamp: $TIMESTAMP" \ --header="X-Signature: $SIGNATURE" \ --post-data="$BODY" ``` -------------------------------- ### SDK Example for One-Time Orders Source: https://docs.waffo.ai/api-reference/endpoints/graphql/orders-and-payments TypeScript SDK example using client.graphql.query to fetch completed one-time orders with a typed response. ```typescript const result = await client.graphql.query<{ onetimeOrders: Array<{ id: string; buyerEmail: string; priceSnapshot: { total: number; currency: string }; status: string; }>; }>({ query: `query($storeId: String!) { onetimeOrders(storeId: $storeId, filter: { status: { eq: "completed" } }) { id buyerEmail priceSnapshot { total currency } status } }`, variables: { storeId: "STO_3bVzrkD0FJjFdZNLk8Ualx" }, }); ``` -------------------------------- ### Webhook Payload Example Source: https://docs.waffo.ai/features/integrations Example of a webhook payload sent to your endpoint when an event occurs. The payload includes the event type and associated data. ```APIDOC ## Webhook Payload ### Description Example of a webhook payload sent to your endpoint when an event occurs. ### Payload Example ```json { "event": "order.completed", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "orderId": "660e8400-e29b-41d4-a716-446655440001", "amount": 2900, "currency": "USD", "status": "completed", "createdAt": "2026-01-15T10:30:00.000Z" } } ``` ### Notes - All IDs are UUID v4 format. - Amounts are in the smallest currency unit. - Timestamps are ISO 8601 UTC. ``` -------------------------------- ### Python: issue-session-token with storeId, productId, and portal Source: https://docs.waffo.ai/api-reference/endpoints/auth/issue-session-token Python example showing how to issue a session token using storeId, productId, and for a customer portal. For the portal, append the token to portalUrl and then 302 the browser there. ```python # Using storeId result = call_api_with_signature('POST', '/v1/actions/auth/issue-session-token', { 'storeId': 'STO_2aUyqjCzEIiEcYMKj7TZtw', 'buyerIdentity': 'customer@example.com' }) # Using productId result = call_api_with_signature('POST', '/v1/actions/auth/issue-session-token', { 'productId': 'PROD_7J3K5L8M2N4P6Q9R', 'buyerIdentity': 'customer@example.com' }) print(result['data']['token']) # Customer portal: append the token to portalUrl, then 302 the browser there result = call_api_with_signature('POST', '/v1/actions/auth/issue-session-token', { 'storeId': 'STO_2aUyqjCzEIiEcYMKj7TZtw', 'buyerIdentity': 'user_8f3a2c', 'purpose': 'portal' }) redirect_url = result['data']['portalUrl'] + '#token=' + result['data']['token'] ``` -------------------------------- ### checkoutSettings JSON example Source: https://docs.waffo.ai/checkout/customization Example of the checkoutSettings object with light and dark theme configurations. Use this structure when updating via the Update Store endpoint. ```json { "checkoutSettings": { "light": { "checkoutLogo": "https://...", "checkoutColorPrimary": "#111111", "checkoutColorBackground": "#F4F4F4", "checkoutColorCard": "#FFFFFF", "checkoutColorText": "#111111", "checkoutBorderRadius": "8px" }, "dark": { "checkoutLogo": null, "checkoutColorPrimary": "#FEFDFD", "checkoutColorBackground": "#111315", "checkoutColorCard": "#181A1B", "checkoutColorText": "#FFFFFF", "checkoutBorderRadius": "8px" } } } ``` -------------------------------- ### Create One-Time Product with SDK (TypeScript) Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/create-product Use the SDK to create a one-time product with client initialization, prices, media, and metadata. Requires WAFFO_MERCHANT_ID and WAFFO_PRIVATE_KEY environment variables. ```typescript import { WaffoPancake, TaxCategory } from "@waffo/pancake-ts"; const client = new WaffoPancake({ merchantId: process.env.WAFFO_MERCHANT_ID!, privateKey: process.env.WAFFO_PRIVATE_KEY!, }); const { product } = await client.onetimeProducts.create({ storeId: "STO_2aUyqjCzEIiEcYMKj7TZtw", name: "Premium Template Pack", description: "50 premium design templates for your next project.", prices: { USD: { amount: "49.00", taxIncluded: false, taxCategory: TaxCategory.DigitalGoods }, EUR: { amount: "45.00", taxIncluded: true, taxCategory: TaxCategory.DigitalGoods }, }, media: [ { type: "image", url: "https://example.com/templates-preview.png", alt: "Template preview" }, ], successUrl: "https://example.com/thank-you", metadata: { category: "design", fileCount: "50" }, }); ``` -------------------------------- ### TypeScript SDK update example Source: https://docs.waffo.ai/api-reference/endpoints/onetime-products/update-product Uses the client.onetimeProducts.update method to update a product. The example sets name, description, prices (with taxCategory as TaxCategory.DigitalGoods), and successUrl. ```typescript const { product } = await client.onetimeProducts.update({ id: "PROD_3kF9mNpQrStUvWxYz1A2bC", name: "Premium Template Pack v2", description: "75 premium design templates — expanded collection.", prices: { USD: { amount: "59.00", taxIncluded: false, taxCategory: TaxCategory.DigitalGoods }, EUR: { amount: "55.00", taxIncluded: true, taxCategory: TaxCategory.DigitalGoods }, }, successUrl: "https://example.com/thank-you", }); ``` -------------------------------- ### Publish Group - C++ Example Source: https://docs.waffo.ai/api-reference/endpoints/subscription-products/publish-group Publishes a product group using the callApi() helper from authentication.mdx. The group ID must be a test environment group. ```cpp // Uses callApi() helper from authentication.mdx std::string body = R"({"id":"d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a"})"; std::string response = callApi("POST", "/v1/actions/subscription-product-group/publish-group", body); std::cout << response << std::endl; ``` -------------------------------- ### Start ngrok tunnel for local webhook handler Source: https://docs.waffo.ai/guides/webhooks Starts an ngrok tunnel forwarding to localhost:3000, providing a public HTTPS URL for webhook delivery. ```bash ngrok http 3000 ``` -------------------------------- ### createSession - Dynamic Pricing Example Source: https://docs.waffo.ai/integrate/ai-integration Creates a checkout session with order-level parameters, including priceSnapshot for dynamic pricing override. This example shows usage-based tiered pricing. ```APIDOC ## createSession ### Description Creates a checkout session with order-level parameters. The `priceSnapshot` parameter overrides the product's set price, enabling dynamic pricing based on usage tier. ### Method POST (SDK method call) ### Endpoint `client.checkout.createSession` ### Parameters #### Request Body - **storeId** (string) - Required - Store identifier - **productId** (string) - Required - Product identifier - **productType** (string) - Required - Product type, e.g., "onetime" - **currency** (string) - Required - Checkout currency, selects matching currency from product prices - **priceSnapshot** (object) - Optional - Overrides product price; fields: `amount` (number), `taxIncluded` (boolean), `taxCategory` (string) - **buyerEmail** (string) - Optional - Pre-fills consumer email, skips email input step - **metadata** (object) - Optional - Custom key-value pairs, passed to webhook event.data - **successUrl** (string) - Optional - Redirect URL after successful payment, overrides product-level successUrl ### Request Example javascript const session = await client.checkout.createSession({ storeId: "store_id", productId: "api-credits-product-id", productType: "onetime", currency: "USD", priceSnapshot: { amount: 49.00, taxIncluded: true, taxCategory: "saas" }, buyerEmail: "user@example.com", metadata: { internalOrderId: "ORD-2024-001", tier: "growth" }, successUrl: "https://myapp.com/purchase/success", }); ### Response #### Success Response Returns a session object (not detailed in source). ``` -------------------------------- ### Publish Group - Go Example Source: https://docs.waffo.ai/api-reference/endpoints/subscription-products/publish-group Publishes a product group using the callAPI() helper from authentication.mdx. The group ID must be a test environment group. ```go // Uses callAPI() helper from authentication.mdx body := map[string]interface{}{ "id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f9a", } result, err := callAPI("POST", "/v1/actions/subscription-product-group/publish-group", body) if err != nil { log.Fatal(err) } fmt.Println(string(result)) ``` -------------------------------- ### JavaScript example: Remove recipient Source: https://docs.waffo.ai/api-reference/endpoints/notification-recipients/remove-recipient JavaScript fetch example to remove a recipient. Uses process.env.WAFFO_MERCHANT_ID and a signature variable. The response is parsed as JSON and the data property is extracted. ```javascript const response = await fetch( "https://api.waffo.com/v1/actions/store-notification-recipient/remove-recipient", { method: "POST", headers: { "Content-Type": "application/json", "X-Merchant-Id": process.env.WAFFO_MERCHANT_ID, "X-Signature": signature, }, body: JSON.stringify({ storeId: "STO_2aUyqjCzEIiEcYMKj7TZtw", recipientId: "11111111-2222-3333-4444-555555555555", }), }, ); const { data } = await response.json(); ``` -------------------------------- ### Webhook Payload Example Source: https://docs.waffo.ai/features/integrations Example JSON payload sent for the order.completed event. All IDs are UUID v4, amounts are in the smallest currency unit, and timestamps are ISO 8601 UTC. ```json { "event": "order.completed", "data": { "id": "550e8400-e29b-41d4-a716-446655440000", "orderId": "660e8400-e29b-41d4-a716-446655440001", "amount": 2900, "currency": "USD", "status": "completed", "createdAt": "2026-01-15T10:30:00.000Z" } } ```