Installation
Install Paymesh, wire a provider, optionally add persistence, and mount webhook handling in a way that scales beyond a demo.
Install the core package and one provider
Start with the core client and exactly one provider.
npm install paymesh @paymesh/stripeIf your product model fits Polar better:
npm install paymesh @paymesh/polarIf you need PIX transparent charges in Brazil:
npm install paymesh @paymesh/abacatepayIf you need Dodo's catalog-driven hosted checkout flow:
npm install paymesh @paymesh/dodoYou do not need a database on day one, but most real installations should add one quickly so webhook state, customers, PIX objects, and catalog snapshots are queryable locally.
Create a shared client module
Put the client in a stable server-side module such as src/lib/paymesh.ts, src/server/paymesh.ts, or app/lib/paymesh.ts.
import { createClient } from "paymesh";
import { stripe } from "@paymesh/stripe";
export const paymesh = createClient({
provider: stripe({
secret: "sk_test_123",
webhookSecret: "whsec_123",
}),
});import { createClient } from "paymesh";
import { polar } from "@paymesh/polar";
export const paymesh = createClient({
provider: polar({
accessToken: "polar_access_123",
webhookSecret: "whsec_polar_123",
}),
});import { createClient } from "paymesh";
import { abacatepay } from "@paymesh/abacatepay";
export const paymesh = createClient({
provider: abacatepay({
apiKey: process.env.ABACATEPAY_API_KEY!,
webhookSecret: process.env.ABACATEPAY_WEBHOOK_SECRET,
}),
});import { createClient } from "paymesh";
import { dodo } from "@paymesh/dodo";
export const paymesh = createClient({
provider: dodo({
apiKey: process.env.DODO_PAYMENTS_API_KEY!,
webhookSecret: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
baseUrl: "https://test.dodopayments.com",
}),
});@paymesh/dodo is catalog-driven like Polar and AbacatePay checkout flows. You create hosted payments with productIds. For BRL payments, the Dodo hosted checkout can offer Pix, but the provider does not expose paymesh.pix.
Add a database adapter
For anything beyond a toy integration, add one database adapter.
npm install @paymesh/memoryimport { createClient } from "paymesh";
import { memory } from "@paymesh/memory";
import { stripe } from "@paymesh/stripe";
export const paymesh = createClient({
provider: stripe({
secret: "sk_test_123",
webhookSecret: "whsec_123",
}),
database: memory({
seed: {
customers: [
{
id: "cus_seed",
provider: "stripe",
sandbox: true,
email: "ada@example.com",
},
],
},
}),
});npm install @paymesh/postgres pgimport { createClient } from "paymesh";
import { postgres } from "@paymesh/postgres";
import { stripe } from "@paymesh/stripe";
export const paymesh = createClient({
provider: stripe({
secret: "sk_test_123",
webhookSecret: "whsec_123",
}),
database: postgres("postgres://postgres:postgres@localhost:5432/paymesh", {
persistRaw: true,
}),
});npm install @paymesh/drizzle drizzle-orm pgimport { createClient } from "paymesh";
import { drizzle as paymeshDrizzle } from "@paymesh/drizzle";
import { stripe } from "@paymesh/stripe";
import { drizzle } from "drizzle-orm/node-postgres";
const db = drizzle("postgres://postgres:postgres@localhost:5432/paymesh");
export const paymesh = createClient({
provider: stripe({
secret: "sk_test_123",
webhookSecret: "whsec_123",
}),
database: paymeshDrizzle(db, {
persistRaw: true,
}),
});npm install @paymesh/prisma @prisma/clientimport { createClient } from "paymesh";
import { prisma as paymeshPrisma } from "@paymesh/prisma";
import { PrismaClient } from "@prisma/client";
import { stripe } from "@paymesh/stripe";
const db = new PrismaClient();
export const paymesh = createClient({
provider: stripe({
secret: "sk_test_123",
webhookSecret: "whsec_123",
}),
database: paymeshPrisma(db, {
persistRaw: true,
}),
});persistRaw: true stores provider payloads alongside normalized data. Use it when you need reconciliation, replay analysis, or provider-specific debugging. Leave it off when normalized records are enough.
Use @paymesh/memory only for tests, CI, demos, and local examples. For production persistence, choose Postgres, Drizzle, or Prisma.
Mount webhook handling
Your provider package is only half of the installation story. You also need to accept webhook traffic and dispatch normalized hooks.
import { Webhooks } from "@paymesh/next";
import { paymesh } from "@/lib/paymesh";
export const POST = Webhooks({
client: paymesh,
async onPaymentSucceeded(event) {
console.log("payment.succeeded", event.data.id);
},
});import express from "express";
import { Webhooks } from "@paymesh/express";
import { paymesh } from "./paymesh";
const app = express();
app.post(
"/webhooks/paymesh",
express.raw({ type: "*/*" }),
Webhooks({
client: paymesh,
async onPaymentSucceeded(event) {
console.log("payment.succeeded", event.data.id);
},
}),
);import { Hono } from "hono";
import { Webhooks } from "@paymesh/hono";
import { paymesh } from "./paymesh";
const app = new Hono();
app.post(
"/webhooks/paymesh",
Webhooks({
client: paymesh,
}),
);import { Elysia } from "elysia";
import { Webhooks } from "@paymesh/elysia";
import { paymesh } from "./paymesh";
const app = new Elysia();
app.post(
"/webhooks/paymesh",
Webhooks({
client: paymesh,
}),
);Add plugins only after the core path is live
Once payments, customers, and webhooks are stable, add plugins for operational workflows.
npm install @paymesh/dash @paymesh/audit-logsimport { auditLog } from "@paymesh/audit-logs";
import { dash } from "@paymesh/dash";
export const paymesh = createClient({
provider: stripe({
secret: "sk_test_123",
webhookSecret: "whsec_123",
}),
database: postgres("postgres://postgres:postgres@localhost:5432/paymesh"),
plugins: [
dash({
auth({ request }) {
const email = request.headers.get("x-user-email");
if (!email) {
throw new Error("Unauthorized");
}
return {
id: "user_123",
type: "user",
email,
};
},
}),
auditLog({
events: ["payment.*", "customer.*", "checkout.*"],
retention: "1y",
}),
],
});What "installed correctly" means
An installation is complete when:
- your app has one shared Paymesh client
- one provider is configured
- webhook routes are mounted
- successful events reach normalized hooks
- local persistence is configured if you need durable state
If you only installed packages, you are not done.