INTEGRATION GUIDE | STRIPE & POSTHOG SETUP
Stripe App & Multi-Stack Webhook Guard Reference
Connect your product usage stream from PostHog or your backend so RevClamp can automatically stop unpaid usage and prepare Stripe dispute evidence across Next.js, Node.js/Express, and Python/FastAPI.
Related Reference & Verification Tools:
Webhook DLQ & 72h Replay Runbook → •
Stripe basil Entitlement Auditor •
Visa CE 3.0 Readiness Checker •
2-Min Onboarding Guide •
Remote MCP Server (9 Tools)
STEP 0 • WHERE TO GET YOUR CREDENTIALS (30 SECONDS)
Open /dashboard Step 1 →
Before dropping the middleware snippet into your app, map these three credentials from your Merchant Console (/dashboard), 2-Min Quickstart (/docs/onboarding), or test ephemerally in /tools/stripe-basil-auditor:
| Credential / Env Var | Format | Where to Obtain It |
|---|---|---|
X-RevClamp-Tenant-Key |
rg_live_... |
Your Workspace API Key (rg_live_...) generated in /dashboard Step 1. |
REVCLAMP_TENANT_ID |
tnt_... |
Your Workspace ID (tnt_...) shown in /dashboard Step 1 & Step 2 (Live Telemetry Synchronized: Workspace tnt_...) or returned when provisioning. |
REVCLAMP_WEBHOOK_SECRET |
whsec_rg_... |
Your Workspace Webhook Signing Secret (whsec_rg_...) issued alongside your API Key in /dashboard Step 1 (POST /api/dashboard/provision) and used by RevClamp to sign outbound X-RevClamp-Signature headers. |
DROP-IN PRODUCTION WEBHOOK ENTITLEMENT GUARD (NEXT.JS / EXPRESS / FASTAPI)
// app/api/webhooks/revclamp/route.ts -- Next.js App Router (TypeScript Edge/Node)
import crypto from "node:crypto";
export async function POST(req: Request) {
const rawBody = await req.text();
const payload = JSON.parse(rawBody); // payload.event: "revclamp.entitlement.revoke" | "revclamp.entitlement.restore"
const [, t = "", v1 = ""] = (req.headers.get("x-revclamp-signature") || "").match(/^t=(\d+),v1=([0-9a-f]{64})$/) || [];
if (!t || !v1 || Math.abs(Math.floor(Date.now() / 1000) - Number(t)) > 300) return Response.json({ ok: false, error: "INVALID_SIGNATURE" }, { status: 401 });
if (payload.event !== "revclamp.entitlement.revoke" && payload.event !== "revclamp.entitlement.restore") return Response.json({ ok: false, error: "UNSUPPORTED_EVENT" }, { status: 400 });
const scope = payload.event === "revclamp.entitlement.restore" ? "revclamp_restore_scope" : "revclamp_revoke_scope";
if (process.env.REVCLAMP_TENANT_ID && payload.tenant_id !== process.env.REVCLAMP_TENANT_ID) return Response.json({ ok: false, error: "TENANT_MISMATCH" }, { status: 403 });
const derivedKey = crypto.createHmac("sha256", (process.env.REVCLAMP_WEBHOOK_SECRET || "").trim()).update(JSON.stringify([scope, payload.tenant_id || "default", payload.account_id || "default"])).digest("hex");
const expectedV1 = crypto.createHmac("sha256", derivedKey).update(t + "." + rawBody, "utf8").digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(v1, "hex"), Buffer.from(expectedV1, "hex"))) return Response.json({ ok: false, error: "INVALID_SIGNATURE" }, { status: 401 });
await db.tenants.update({ where: { stripeCustomerId: payload.customer_id, transitionSeq: { lt: payload.transition_seq } }, data: { apiAccessPaused: payload.event === "revclamp.entitlement.revoke", transitionSeq: payload.transition_seq } });
return Response.json({ ok: true, customer_id: payload.customer_id, handled: payload.event });
}
STRIPE DASHBOARD DRAWER & API INTEGRATION
// 1. Check Customer Protection Status (stripe.dashboard.customer.detail)
const cusRes = await fetch(
"https://api.revclamp.com/v1/stripe/customer-drawer?customer_id=" + encodeURIComponent(customerId),
{ headers: { "X-RevClamp-Tenant-Key": "<YOUR_TENANT_KEY>" } }
);
const { ok: cusOk, drawer: customerDrawer } = await cusRes.json();
// 2. Stage Chargeback Evidence Draft (stripe.dashboard.dispute.detail)
const dispRes = await fetch("https://api.revclamp.com/v1/stripe/dispute-drawer", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-RevClamp-Tenant-Key": "<YOUR_TENANT_KEY>"
},
body: JSON.stringify({ existingDispute, expectedCustomerId: customerId, usageEvents })
});
const { ok: dispOk, drawer: disputeDrawer } = await dispRes.json();
Zero-Risk & Fail-Open CTO Architecture Guarantees
| Engineering Safeguard | Runtime Guarantee |
|---|---|
| Out-of-Band Enforcement (Fail-Open) | Entitlement lock/restore signals arrive via signed webhooks (crypto.timingSafeEqual) and never block your live customer API request path. |
| Stateless Ephemeral Read-Only Audit | 1-click historical readiness audits (POST /api/dashboard/readiness-audit) evaluate caller-supplied customers and usageEvents ephemerally in memory (statelessEphemeralAudit: true) when custom payloads are passed, without persisting raw Stripe customer lists. |
| No Raw Prompts or Card Payloads Stored | Ingests strictly quantitative usage metadata (stripe_customer_id, event_name, units, occurredAtSec). |
Non-Destructive submit: false Staging | Appends verified activity inside [RevClamp Verified Product Usage Log] delimiters without overwriting merchant notes or auto-submitting to card networks. |
PostHog Webhook Settings (Zero-Code)
| Setting | Value to Enter |
|---|---|
| Webhook Destination URL | https://api.revclamp.com/v1/events/ingest |
| HTTP Method | POST |
| Authentication Header | X-RevClamp-Tenant-Key: <YOUR_TENANT_KEY> |
| Customer Matching | Matches stripe_customer_id first, or falls back to verified customer email |
| EU Cookieless Privacy Mode | Fully compatible with PostHog $ip: null (no cookies required) |
Automatic Stripe Events Handled by RevClamp
| Stripe Webhook Event | How RevClamp Protects You |
|---|---|
customer.subscription.updated | Syncs plan upgrades, renewals, and status changes in real time |
customer.subscription.deleted | Monitors post-cancellation usage and locks unpaid accounts after your grace period |
invoice.payment_failed | Starts your custom payment-retry grace window before restricting access |
charge.dispute.created | Automatically compiles 90 days of customer usage proof into a Stripe Dispute draft |