A framework-agnostic payment checkout library for Node.js, written in TypeScript.
Drop-in payment orchestration for Express, NestJS, Fastify and any other Node.js framework — connect any payment provider through a single interface.
Inspired by awesome-node-auth. Same philosophy: no framework lock-in, no DB lock-in, implement one interface and you're done.
npm install awesome-node-checkoutimport { CheckoutConfigurator, PayPalProvider, NexiProvider, SatispayProvider } from 'awesome-node-checkout';
import * as fs from 'fs';
const checkout = new CheckoutConfigurator();
checkout
.registerProvider(new PayPalProvider({
clientId: process.env.PAYPAL_CLIENT_ID!,
clientSecret: process.env.PAYPAL_CLIENT_SECRET!,
environment: 'sandbox',
}))
.registerProvider(new NexiProvider({
merchantId: process.env.NEXI_MERCHANT_ID!,
macKey: process.env.NEXI_MAC_KEY!,
environment: 'sandbox',
}))
.registerProvider(new SatispayProvider({
keyId: process.env.SATISPAY_KEY_ID!,
privateKey: fs.readFileSync('private.pem', 'utf8'),
environment: 'sandbox',
serverUrl: 'https://myapp.com',
}));
// Use directly — no HTTP framework needed
const result = await checkout.createPayment('paypal', {
amount: 49.99,
currency: 'EUR',
description: 'Order #1234',
returnUrl: 'https://myapp.com/payment/success',
cancelUrl: 'https://myapp.com/payment/cancel',
orderId: 'ORD-1234',
});
if (result.approvalUrl) {
// redirect the user to result.approvalUrl
}import express from 'express';
import { createCheckoutRouter } from 'awesome-node-checkout/express';
const app = express();
app.use(express.json());
// Mounts all checkout routes under /checkout
app.use('/checkout', createCheckoutRouter(checkout));
app.listen(3000);Use the CheckoutConfigurator methods directly in your own routes:
fastify.post('/checkout/:provider', async (req, reply) => {
const result = await checkout.createPayment(req.params.provider, req.body);
reply.status(result.success ? 201 : 400).send(result);
});
fastify.post('/checkout/:provider/webhook', async (req, reply) => {
const result = await checkout.handleWebhook(req.params.provider, req.body, req.headers);
reply.send(result);
});| Method | Path | Description |
|---|---|---|
| POST | /:provider |
Create a payment |
| POST | /:provider/execute |
Execute/capture a payment |
| GET | /:provider/redirect |
Handle provider redirect callback |
| GET | /:provider/:id |
Get payment details |
| POST | /:provider/refund |
Refund a payment |
| POST | /:provider/webhook |
Handle provider webhook |
| Flow | Providers | Description |
|---|---|---|
redirect |
PayPal, Nexi | User is redirected to provider, returns with query params |
webhook |
Satispay | Provider calls a webhook URL after async confirmation |
direct |
(future) | Synchronous processing (card tokenization, etc.) |
checkout.events
.on('payment.created', ({ provider, paymentId }) => { /* ... */ })
.on('payment.completed', ({ provider, paymentId }) => { /* ... */ })
.on('payment.failed', ({ provider, error }) => { /* ... */ })
.on('payment.refunded', ({ provider, paymentId }) => { /* ... */ })
.on('webhook.received', ({ provider, data }) => { /* ... */ });By default, SatispayProvider uses an in-memory store to correlate webhooks with orders.
For multi-instance deployments, implement ITransactionStore:
import { ITransactionStore, TransactionData } from 'awesome-node-checkout';
class RedisTransactionStore implements ITransactionStore {
async save(key: string, data: TransactionData): Promise<void> { /* ... */ }
async get(key: string): Promise<TransactionData | null> { /* ... */ }
async delete(key: string): Promise<void> { /* ... */ }
}
new SatispayProvider({
keyId: '...',
privateKey: '...',
transactionStore: new RedisTransactionStore(),
});Extend BasePaymentProvider to add any payment provider:
import { BasePaymentProvider, PaymentRequest, PaymentResult } from 'awesome-node-checkout';
export class StripeProvider extends BasePaymentProvider {
readonly name = 'stripe';
readonly flow = 'redirect' as const;
async createPayment(request: PaymentRequest): Promise<PaymentResult> {
// ... call Stripe API
}
// ... implement other methods
async handleRedirect(query: Record<string, any>): Promise<PaymentResult> {
// ... handle Stripe redirect
}
}
checkout.registerProvider(new StripeProvider({ secretKey: '...' }));| Provider | Flow | Notes |
|---|---|---|
| PayPal | redirect | Orders API v2 |
| Nexi | redirect | eCommerce DispatcherServlet + MAC SHA-1 |
| Satispay | webhook | Business API v1, RSA-SHA256 signature |
MIT