Skip to main content
The WooCommerce guide is one concrete example of a general pattern that works for any platform — a custom storefront, a Shopify app, a membership site, or a no-code tool. This page distills that pattern with language-agnostic examples.

The two calls you need

1

Create a checkout session, then redirect

Server-side, call POST /checkout/sessions and send the buyer to the url in the response. Put your own order/cart id in metadata.
2

Confirm via the order.completed webhook

When the webhook fires, verify the signature and read your id back out of metadata to settle the matching order.

1. Create a checkout session

curl -X POST https://api.pocketsflow.com/checkout/sessions \
  -H "Authorization: Bearer pk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "productId": "65a1b2c3d4e5f6a7b8c9d0e1",
    "successUrl": "https://store.example.com/thank-you",
    "cancelUrl": "https://store.example.com/cart",
    "customerEmail": "buyer@example.com",
    "metadata": { "external_order_id": "ORDER-1043" }
  }'
FieldRequiredNotes
productIdYesA Pocketsflow product owned by your account.
successUrlNoAbsolute http(s) URL the buyer returns to after paying.
cancelUrlNoAbsolute http(s) URL if they abandon checkout.
customerEmailNoAttached to the resulting order.
discountCodeNoA discount code to pre-apply.
metadataNoArbitrary key/values, echoed back on the order + webhooks.
The response is { "id": "cs_…", "url": "https://yourstore.pocketsflow.com/checkout?…" }.

2. Verify and handle the webhook

Every webhook is an HTTP POST signed with HMAC-SHA256 over the raw body, keyed with your endpoint’s signing secret. Verify it before trusting the payload.
import express from "express";
import crypto from "crypto";

const app = express();

app.post(
  "/webhooks/pocketsflow",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const signature = req.header("X-Pocketsflow-Signature");
    const expected = crypto
      .createHmac("sha256", process.env.POCKETSFLOW_WEBHOOK_SECRET)
      .update(req.body) // raw Buffer
      .digest("hex");

    if (
      !signature ||
      !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
    ) {
      return res.status(400).send("Invalid signature");
    }

    const event = req.header("X-Pocketsflow-Event");
    const payload = JSON.parse(req.body.toString("utf8"));
    const orderId = payload.metadata?.external_order_id;

    if (event === "order.completed" && orderId) {
      markOrderPaid(orderId); // idempotent
    }

    res.status(200).send("OK");
  }
);

Best practices

  • Verify against the raw body. Don’t re-serialize the JSON before hashing.
  • Be idempotent. A webhook may be re-sent; processing the same event twice must be safe (check whether the order is already paid first).
  • Acknowledge fast. Return 2xx quickly and do heavy work asynchronously.
  • Reconcile. Treat the webhook as the source of truth; if one is missed, use GET /orders and match on your metadata id.
  • Keep secrets server-side. API keys and signing secrets must never reach the browser.

A note on Shopify and marketplace apps

The same redirect + webhook pattern works for a custom Shopify integration today. A deeper, no-code “native app” experience (one-click connect, automatic product sync) for WooCommerce, Shopify, and others is tracked on our roadmap — see the integrations overview for what’s available now.