Using Without Charcole
For Existing Express Apps
If you have an existing Express application and want to add @charcoles/payments without using the Charcole CLI, install the package directly.
Installation
npm install @charcoles/payments
Setup
The critical requirement is middleware ordering. Register the raw body middleware before express.json():
import express from 'express'
import { setupPayments } from '@charcoles/payments'
const app = express()
// ⚠️ Must come BEFORE express.json()
app.use('/payments/webhook', express.raw({ type: 'application/json' }))
app.use(express.json())
// ... rest of your middleware
setupPayments(app, {
provider: 'stripe',
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
})
app.listen(3000)
If you reverse the order, webhook signature verification will fail silently.
Configuration Options
The setupPayments() function accepts an options object:
setupPayments(app, {
provider: 'stripe', // or 'lemonsqueezy'
stripeSecretKey: '...',
stripeWebhookSecret: '...',
lemonSqueezyApiKey: '...',
lemonSqueezyWebhookSecret: '...',
lemonSqueezyStoreId: '...',
mountPath: '/payments', // default — change if needed
})
Stripe Setup
setupPayments(app, {
provider: 'stripe',
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
})
All endpoints are live at /payments/*:
POST /payments/create-intentPOST /payments/refundGET /payments/status/:paymentIdPOST /payments/webhook
LemonSqueezy Setup
setupPayments(app, {
provider: 'lemonsqueezy',
lemonSqueezyApiKey: process.env.LEMONSQUEEZY_API_KEY,
lemonSqueezyWebhookSecret: process.env.LEMONSQUEEZY_WEBHOOK_SECRET,
lemonSqueezyStoreId: process.env.LEMONSQUEEZY_STORE_ID,
})
Custom Mount Path
If you want to mount payment endpoints at a different path, use the mountPath option:
setupPayments(app, {
provider: 'stripe',
stripeSecretKey: process.env.STRIPE_SECRET_KEY,
stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
mountPath: '/api/payments', // endpoints at /api/payments/*
})
Using Adapters Directly
If you only need the adapter logic without the Express routes, instantiate adapters directly:
import { StripeAdapter, LemonSqueezyAdapter } from '@charcoles/payments'
// Stripe
const stripeAdapter = new StripeAdapter({
secretKey: process.env.STRIPE_SECRET_KEY,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
})
const result = await stripeAdapter.createPayment({
amount: 2999,
currency: 'usd',
})
console.log(result.data.clientSecret)
// LemonSqueezy
const lsAdapter = new LemonSqueezyAdapter({
apiKey: process.env.LEMONSQUEEZY_API_KEY,
webhookSecret: process.env.LEMONSQUEEZY_WEBHOOK_SECRET,
storeId: process.env.LEMONSQUEEZY_STORE_ID,
})
const result = await lsAdapter.createPayment({
amount: 2999,
currency: 'usd',
metadata: { variantId: '78901' },
})
console.log(result.data.checkoutUrl)
Available adapter methods:
createPayment()refundPayment()getPaymentStatus()verifyWebhookSignature()
TypeScript Support
The package ships with TypeScript types (index.d.ts). All types are available:
import type {
CreatePaymentResult,
RefundPaymentResult,
PaymentStatus,
SetupPaymentsOptions,
PaymentEvent,
StripeAdapter,
LemonSqueezyAdapter,
} from '@charcoles/payments'
const options: SetupPaymentsOptions = {
provider: 'stripe',
stripeSecretKey: '...',
stripeWebhookSecret: '...',
}
setupPayments(app, options)
Environment Variables
Set these in your .env:
PAYMENT_PROVIDER=stripe
# Stripe
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Or LemonSqueezy
LEMONSQUEEZY_API_KEY=...
LEMONSQUEEZY_WEBHOOK_SECRET=...
LEMONSQUEEZY_STORE_ID=12345
See Environment Variables for complete reference.
Webhook Handling
Webhooks are handled automatically by the module. The webhook route verifies signatures and ensures events are processed only once (in-memory deduplication).
To add custom fulfillment logic, you'll need to extend the module or implement a separate webhook handler. Refer to Webhooks for event types.
What Comes Next
- API Endpoints — Make payment requests
- Webhooks — Handle payment events
- Examples — Copy-paste examples