npm package: @super-dp/payments-core
Description: A framework-agnostic payments core for Node.js with provider-based subscription management (Stripe implementation included)
Latest version: 0.2.1

# @super-dp/payments-core

A framework-agnostic payments core for Node.js with provider-based subscription management (Stripe implementation included). Perfect for use in any Node.js project (Express, Fastify, NestJS, Next.js API routes, etc.).

## Features

- ✅ **Framework-agnostic** - Works with any Node.js framework
- ✅ **TypeScript-first** - Full type safety with Stripe types
- ✅ **Subscription-focused** - Streamlined API for subscription workflows
- ✅ **Webhook handling** - Built-in webhook verification and event handling
- ✅ **Customer management** - Easy customer creation and lookup
- ✅ **Product & Price management** - Create products with multiple pricing tiers

## Installation

```bash
pnpm add @super-dp/payments-core stripe
# or
npm install @super-dp/payments-core stripe
```

## Quick Start

```typescript
import { PaymentsClient, StripeProvider } from '@super-dp/payments-core';

// Initialize provider and client
const stripeProvider = new StripeProvider({
  apiKey: process.env.STRIPE_SECRET_KEY!,
  webhookSecret: process.env.STRIPE_WEBHOOK_SECRET, // Optional
});

const payments = new PaymentsClient(stripeProvider);

// Create a customer (returns generic Customer type, not Stripe.Customer)
const customer = await payments.createCustomer({
  email: 'user@example.com',
  name: 'John Doe',
});

// Create a checkout session (returns generic CheckoutSession type)
const session = await payments.createCheckoutSession({
  customerEmail: 'user@example.com',
  priceId: 'price_1234567890',
  successUrl: 'https://yourapp.com/success',
  cancelUrl: 'https://yourapp.com/cancel',
});

// Get a subscription (returns generic Subscription type)
const subscription = await payments.getSubscription('sub_1234567890');

// Cancel a subscription
await payments.cancelSubscription({
  subscriptionId: 'sub_1234567890',
  cancelImmediately: false, // Cancel at period end
});
```

## API Reference

### Customer Methods

#### `createCustomer(options)`

Create a new Stripe customer.

```typescript
const customer = await payments.createCustomer({
  email: 'user@example.com',
  name: 'John Doe',
  metadata: { userId: '123' },
});
```

#### `findCustomerByEmail(email)`

Find a customer by email address.

```typescript
const customer = await payments.findCustomerByEmail('user@example.com');
```

#### `createCustomerIfNotExists(email, options?)`

Create a customer only if one doesn't already exist with that email.

```typescript
const customer = await payments.createCustomerIfNotExists('user@example.com', {
  name: 'John Doe',
});
```

### Subscription Methods

#### `createCheckoutSession(options)`

Create a Stripe Checkout session for subscription.

```typescript
const session = await payments.createCheckoutSession({
  customerEmail: 'user@example.com',
  priceId: 'price_1234567890',
  successUrl: 'https://yourapp.com/success',
  cancelUrl: 'https://yourapp.com/cancel',
});
```

#### `getSubscription(subscriptionId)`

Retrieve a subscription by ID.

```typescript
const subscription = await payments.getSubscription('sub_1234567890');
```

#### `listCustomerSubscriptions(customerId, options?)`

List all subscriptions for a customer.

```typescript
const subscriptions = await payments.listCustomerSubscriptions('cus_1234567890', {
  status: 'active',
  limit: 10,
});
```

#### `cancelSubscription(options)`

Cancel a subscription.

```typescript
// Cancel at period end (default)
await payments.cancelSubscription({
  subscriptionId: 'sub_1234567890',
});

// Cancel immediately
await payments.cancelSubscription({
  subscriptionId: 'sub_1234567890',
  cancelImmediately: true,
});
```

#### `reactivateSubscription(subscriptionId)`

Reactivate a subscription that was set to cancel at period end.

```typescript
await payments.reactivateSubscription('sub_1234567890');
```

#### `updateSubscriptionPrice(subscriptionId, newPriceId, prorationBehavior?)`

Update a subscription to a new price.

```typescript
await payments.updateSubscriptionPrice(
  'sub_1234567890',
  'price_new123',
  'create_prorations' // or 'none' or 'always_invoice'
);
```

#### `toSubscription(stripeSubscription)`

Convert a Stripe subscription object to a simplified format.

```typescript
const stripeSub = await payments.getSubscription('sub_1234567890');
const simplified = payments.toSubscription(stripeSub);
// Returns: { id, customerId, status, currentPeriodStart, currentPeriodEnd, ... }
```

### Product & Price Methods

#### `createProductWithPrices(options)`

Create a product with multiple pricing tiers.

```typescript
const { product, prices } = await payments.createProductWithPrices({
  name: 'Pro Plan',
  description: 'Professional features',
  prices: [
    { unitAmount: 2900, currency: 'usd', interval: 'month' },
    { unitAmount: 29000, currency: 'usd', interval: 'year' },
  ],
});
```

### Webhook Methods

#### `constructWebhookEvent(payload, signature)`

Verify and construct a webhook event from raw payload.

```typescript
const event = payments.constructWebhookEvent(req.body, req.headers['stripe-signature']);
```

#### `handleWebhookEvent(payload, signature, handlers)`

Handle webhook events with type-safe handlers.

```typescript
// Note: handleWebhookEvent is available on StripeProvider, not PaymentsClient
await stripeProvider.handleWebhookEvent(req.body, req.headers['stripe-signature'], {
  onSubscriptionCreated: async (event) => {
    // event is WebhookEvent type (generic, provider-agnostic)
    const subscriptionData = event.data.object;
    // Handle subscription created
  },
  onSubscriptionUpdated: async (event) => {
    // Handle subscription updated
  },
  onInvoicePaymentSucceeded: async (event) => {
    // Handle successful payment
  },
});
```

## Error Handling

The library provides custom error classes:

```typescript
import {
  PaymentsError,
  SubscriptionError,
  CustomerError,
  WebhookVerificationError,
} from '@super-dp/payments-core';

try {
  await payments.cancelSubscription({ subscriptionId: 'sub_123' });
} catch (error) {
  if (error instanceof SubscriptionError) {
    console.error('Subscription error:', error.message, error.subscriptionId);
  }
}
```

## Accessing Provider-Specific Features

For provider-specific features (like advanced Stripe operations), access the provider directly:

```typescript
// Access raw Stripe client (StripeProvider only)
const stripe = stripeProvider.getStripeClient();
// Use stripe.* methods directly for advanced Stripe features
```

## License

MIT