Skip to content

usepaymesh/paymesh

Repository files navigation


Paymesh

Provider-agnostic payments infrastructure for TypeScript teams.

Create payments, manage customers, normalize webhooks, and keep provider-specific details behind one typed API.

About · Getting started


What is Paymesh

Paymesh is a provider-agnostic payments toolkit for TypeScript applications. It gives your app a small, typed abstraction over payment providers so you can create checkout sessions, manage customers, verify webhooks, and react to normalized payment events without spreading provider SDK details across your codebase.

The core package exposes the client and provider contracts. Provider packages implement those contracts, and framework adapters make webhook handling feel native in your HTTP framework.

import { drizzle as paymeshDrizzle } from "@paymesh/drizzle";
import { stripe } from "@paymesh/stripe";
import { createClient } from "paymesh";
import { drizzle } from "drizzle-orm/node-postgres";

const db = drizzle(process.env.DATABASE_URL!);

export const paymesh = createClient({
  provider: stripe({
    secret: process.env.STRIPE_API_KEY!,
    webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
  }),
  database: paymeshDrizzle(db, {
    persistRaw: true,
  }),
  schema: {
    prefix: "paymesh_",
    tables: {
      customers: {
        name: "paymesh_customers",
      },
    },
  },
});

const payment = await paymesh.payments.create({
  amount: 1999,
  currency: "USD",
  description: "Pro plan",
  customer: {
    email: "ada@example.com",
  },
  successUrl: "https://example.com/success",
  cancelUrl: "https://example.com/cancel",
});

console.log(payment.checkoutUrl);

Getting Started

Install the core package, a provider, and a database adapter.

npm install paymesh @paymesh/stripe @paymesh/postgres
# or, if you already use Drizzle
npm install paymesh @paymesh/stripe @paymesh/drizzle drizzle-orm
# or, if you already use Prisma
npm install paymesh @paymesh/stripe @paymesh/prisma @prisma/client

Available providers currently include @paymesh/stripe, @paymesh/polar, @paymesh/abacatepay, and @paymesh/dodo.

Available database adapters currently include @paymesh/postgres, @paymesh/drizzle, and @paymesh/prisma.

Database and CLI

Paymesh can persist normalized relational data for customers, checkouts, invoices, subscriptions, webhook events, products, and prices. When a database is configured, paymesh.customers.get(id) reads from the local database instead of calling the provider API.

Native PIX flows live under paymesh.pix. Use paymesh.payments for generic checkout sessions and paymesh.pix when you need QR code, copia-e-cola, and expiration data as first-class fields.

@paymesh/dodo supports hosted BRL checkout links that can present Pix inside the Dodo checkout page, but it intentionally does not expose paymesh.pix because Dodo does not currently document a native QR-code-first backend PIX flow.

Database packages: @paymesh/postgres, @paymesh/drizzle, and @paymesh/prisma. Operational tooling: @paymesh/cli.

You can pass a database adapter instance directly:

const paymesh = createClient({
  provider: stripe(),
  database: postgres(process.env.DATABASE_URL!),
});

Or adapt an existing Drizzle database instance:

import { drizzle as drizzleAdapter } from "@paymesh/drizzle";
import { drizzle } from "drizzle-orm/node-postgres";

const db = drizzle(process.env.DATABASE_URL!);

const paymesh = createClient({
  provider: stripe(),
  database: drizzleAdapter(db),
});

Or adapt an existing Prisma client:

import { prisma } from "@paymesh/prisma";
import { PrismaClient } from "@prisma/client";

const db = new PrismaClient();

const paymesh = createClient({
  provider: stripe(),
  database: prisma(db),
});

The @paymesh/cli package reads the same client module used by your app. Point it at the module with --client, PAYMESH_PATH, or package.json.paymesh.path.

paymesh generate --client ./src/lib/paymesh.ts
paymesh migrate --client ./src/lib/paymesh.ts
paymesh push --client ./src/lib/paymesh.ts
paymesh status --client ./src/lib/paymesh.ts

paymesh generate writes both paymesh/history.json and paymesh/migrations/*.sql. The history file is the schema manifest used by migrate and status.

Customer writes go through a single upsert entrypoint.

await paymesh.customers.upsert({
  email: "ada@example.com",
  externalId: "user_123",
});

await paymesh.customers.upsert({
  id: "cus_123",
  name: "Ada Lovelace",
});

Webhooks

Paymesh maps provider-specific webhook payloads into normalized events and dispatches them to typed hooks.

Webhook adapters are available for @paymesh/next, @paymesh/express, @paymesh/fastify, @paymesh/hono, and @paymesh/elysia.

For Next.js App Router:

// app/api/webhooks/stripe/route.ts
import { Webhooks } from "@paymesh/next";
import { paymesh } from "@/lib/paymesh";

export const POST = Webhooks({
  client: paymesh,
  async onEvent(event) {
    console.log("Webhook event", event.type, event.id);
  },
  async onCheckoutCompleted(event) {
    console.log("Checkout completed", event.data.id);
  },
  async onPaymentSucceeded(event) {
    console.log("Payment succeeded", event.data.id);
  },
});

Framework adapters expose the same hook API while returning responses in the shape expected by each framework, including onEvent, onUnhandledEvent, onPaymentCreated, onPaymentSucceeded, onPaymentFailed, onPaymentCanceled, onPaymentRefunded, onCustomerCreated, onCustomerUpdated, onCustomerDeleted, onSubscriptionCreated, onSubscriptionUpdated, onSubscriptionCanceled, and onCheckoutCompleted.

Dispatch prefers the specific normalized hook first, falls back to onEvent when no specific handler exists, and uses onUnhandledEvent only when neither is defined.

Why Paymesh

Payment code usually starts small and then spreads provider-specific request shapes, webhook signatures, event names, and customer objects through the application. That makes it harder to test, harder to switch providers, and harder to support multiple runtimes.

Paymesh keeps those boundaries explicit.

  • one typed client for payment operations;
  • normalized payment, customer, and webhook event shapes;
  • provider packages that isolate external API details;
  • framework adapters that handle webhook verification and dispatch;
  • optional raw provider responses when you need to inspect or store the original payload.

Contribution

You can help by opening issues, proposing provider integrations, improving framework adapters, and contributing fixes to the source code.

About

Provider-agnostic payments infrastructure for TypeScript teams

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Contributors

Languages