@sdods/payments
v1.0.0
Published
Universal payment abstraction layer - vendor-agnostic payment contracts
Downloads
59
Maintainers
Readme
@sdods/payments
Universal payment abstraction layer — vendor-agnostic payment contracts for use across multiple projects.
Features
- 🔄 Vendor Agnostic — Switch between Stripe, PayPal, etc. without code changes
- 📝 Strong Contracts — TypeScript interfaces for all payment operations
- 🔒 PCI Compliant Patterns — Follows industry standards
- 🏗️ Multi-Provider Support — Run multiple providers with fallback
- 💰 Full Payment Lifecycle — Customers, payments, subscriptions, refunds, invoices
Installation
npm install @sdods/payments
# For Stripe support:
npm install stripeQuick Start
import { createPaymentService } from '@sdods/payments';
import { createStripeProvider } from '@sdods/payments/stripe';
// Initialize provider
const stripe = createStripeProvider({
secretKey: process.env.STRIPE_SECRET_KEY!,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
});
// Create payment service
const payments = createPaymentService({
defaultProvider: 'stripe',
providers: { stripe },
});
// Use consistent API across all providers
const { data: customer } = await payments.createCustomer({
email: '[email protected]',
name: 'John Doe',
});
const { data: payment } = await payments.createPaymentIntent({
amount: { amount: 2000, currency: 'USD' }, // $20.00
customerId: customer.id,
description: 'Premium subscription',
});
console.log('Client secret:', payment.clientSecret);API Reference
Customers
// Create
const { data } = await payments.createCustomer({ email, name, phone, metadata });
// Read
const { data } = await payments.getCustomer(customerId);
// Update
const { data } = await payments.updateCustomer(customerId, { name: 'New Name' });
// Delete
await payments.deleteCustomer(customerId);Payment Methods
// Attach (from frontend token)
const { data } = await payments.attachPaymentMethod({
customerId,
paymentMethodToken: 'pm_xxx',
setAsDefault: true,
});
// List
const { data: methods } = await payments.listPaymentMethods(customerId);
// Detach
await payments.detachPaymentMethod(paymentMethodId);Payments (One-time)
// Create payment intent
const { data: intent } = await payments.createPaymentIntent({
amount: { amount: 5000, currency: 'USD' },
customerId,
paymentMethodId,
confirm: true, // Auto-confirm
});
// Or manual confirmation
const { data } = await payments.confirmPayment({
paymentIntentId: intent.id,
paymentMethodId,
returnUrl: 'https://example.com/return',
});
// Cancel
await payments.cancelPayment(paymentIntentId);
// Capture (for manual capture)
await payments.capturePayment(paymentIntentId);Subscriptions
// Create
const { data: sub } = await payments.createSubscription({
customerId,
priceId: 'price_xxx',
trialDays: 14,
});
// Update
await payments.updateSubscription({
subscriptionId: sub.id,
priceId: 'price_new', // Upgrade/downgrade
});
// Cancel at period end
await payments.cancelSubscription(sub.id, false);
// Cancel immediately
await payments.cancelSubscription(sub.id, true);
// List
const { data: subs } = await payments.listSubscriptions(customerId);Refunds
// Full refund
const { data } = await payments.createRefund({
paymentIntentId,
});
// Partial refund
const { data } = await payments.createRefund({
paymentIntentId,
amount: { amount: 1000, currency: 'USD' }, // $10.00
reason: 'Customer requested',
});Hosted Checkout
const { data: session } = await payments.createCheckoutSession({
mode: 'subscription',
customerId,
lineItems: [{ priceId: 'price_xxx', quantity: 1 }],
successUrl: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
cancelUrl: 'https://example.com/cancel',
allowPromoCodes: true,
});
// Redirect user to session.urlWebhooks
// In your webhook handler
const { data: event } = await payments.verifyWebhook({
payload: req.body,
signature: req.headers['stripe-signature'],
secret: process.env.STRIPE_WEBHOOK_SECRET!,
});
switch (event.type) {
case 'payment.succeeded':
// Handle successful payment
break;
case 'subscription.canceled':
// Handle cancellation
break;
}Universal Types
All money values use a consistent format:
interface Money {
amount: number; // Smallest unit (cents)
currency: string; // ISO 4217 (USD, EUR, etc.)
}All results use a consistent wrapper:
interface PaymentResult<T> {
success: boolean;
data?: T;
error?: {
code: string;
message: string;
retriable?: boolean;
};
}Adding Custom Providers
Implement the PaymentProvider interface:
import type { PaymentProvider } from '@sdods/payments';
export function createMyProvider(config: MyConfig): PaymentProvider {
return {
name: 'my-provider',
isConfigured: true,
async createCustomer(input) { /* ... */ },
async getCustomer(id) { /* ... */ },
// ... implement all methods
};
}License
MIT © Yarlis
