npm package: @flightdev/payments
Description: Agnostic payments layer for Flight Framework. Choose your processor: Stripe, Paddle, MercadoPago.
Latest version: 0.0.3

# @flightdev/payments

Payment processing for Flight Framework. Unified API for Stripe, Paddle, and LemonSqueezy.

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Adapters](#adapters)
- [Checkout Sessions](#checkout-sessions)
- [Subscriptions](#subscriptions)
- [Webhooks](#webhooks)
- [Customer Portal](#customer-portal)
- [API Reference](#api-reference)
- [License](#license)

---

## Features

- Single API for multiple payment providers
- Checkout session management
- Subscription lifecycle handling
- Webhook verification and parsing
- Customer portal links
- Type-safe event handling
- Idempotency key support

---

## Installation

```bash
npm install @flightdev/payments

# Install your provider's SDK
npm install stripe           # For Stripe
npm install @paddle/paddle-node-sdk  # For Paddle
```

---

## Quick Start

```typescript
import { createPayments } from '@flightdev/payments';
import { stripe } from '@flightdev/payments/stripe';

const payments = createPayments(stripe({
    secretKey: process.env.STRIPE_SECRET_KEY,
    webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
}));

// Create a checkout session
const session = await payments.createCheckout({
    items: [
        { name: 'Pro Plan', price: 2999, quantity: 1 },
    ],
    successUrl: 'https://example.com/success',
    cancelUrl: 'https://example.com/cancel',
});

// Redirect user to session.url
```

---

## Adapters

### Stripe

Full-featured payment processing.

```typescript
import { stripe } from '@flightdev/payments/stripe';

const adapter = stripe({
    secretKey: process.env.STRIPE_SECRET_KEY,
    webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
    apiVersion: '2024-12-18.acacia',
});
```

### Paddle

Merchant of record for global sales.

```typescript
import { paddle } from '@flightdev/payments/paddle';

const adapter = paddle({
    apiKey: process.env.PADDLE_API_KEY,
    environment: 'sandbox',  // or 'production'
    webhookSecret: process.env.PADDLE_WEBHOOK_SECRET,
});
```

### LemonSqueezy

Digital products and subscriptions.

```typescript
import { lemonsqueezy } from '@flightdev/payments/lemonsqueezy';

const adapter = lemonsqueezy({
    apiKey: process.env.LEMON_SQUEEZY_API_KEY,
    storeId: process.env.LEMON_SQUEEZY_STORE_ID,
    webhookSecret: process.env.LEMON_SQUEEZY_WEBHOOK_SECRET,
});
```

---

## Checkout Sessions

### One-Time Payment

```typescript
const session = await payments.createCheckout({
    mode: 'payment',
    items: [
        { 
            name: 'Flight Framework License',
            price: 9900,  // $99.00 in cents
            quantity: 1,
        },
    ],
    successUrl: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
    cancelUrl: 'https://example.com/cancel',
    customerEmail: 'user@example.com',
    metadata: {
        userId: 'user_123',
    },
});
```

### Subscription Checkout

```typescript
const session = await payments.createCheckout({
    mode: 'subscription',
    items: [
        { priceId: 'price_monthly_pro' },  // Use existing price ID
    ],
    successUrl: 'https://example.com/success',
    cancelUrl: 'https://example.com/cancel',
    trialDays: 14,
});
```

### Custom Line Items

```typescript
const session = await payments.createCheckout({
    items: [
        { name: 'Consulting Hour', price: 15000, quantity: 2 },
        { name: 'Setup Fee', price: 5000, quantity: 1 },
    ],
    allowPromoCodes: true,
    collectBillingAddress: true,
    collectShippingAddress: false,
});
```

---

## Subscriptions

### Retrieve Subscription

```typescript
const subscription = await payments.getSubscription('sub_123');

console.log(subscription.status);       // 'active'
console.log(subscription.currentPeriodEnd);  // Date
console.log(subscription.cancelAtPeriodEnd); // boolean
```

### Cancel Subscription

```typescript
// Cancel at period end
await payments.cancelSubscription('sub_123', { 
    immediately: false 
});

// Cancel immediately
await payments.cancelSubscription('sub_123', { 
    immediately: true 
});
```

### Update Subscription

```typescript
await payments.updateSubscription('sub_123', {
    priceId: 'price_yearly_pro',  // Upgrade/downgrade
    prorationBehavior: 'create_prorations',
});
```

---

## Webhooks

### Webhook Handler

```typescript
// src/routes/api/webhooks/payments.post.ts
import { payments } from '~/lib/payments';

export async function POST(request: Request) {
    const payload = await request.text();
    const signature = request.headers.get('stripe-signature');
    
    if (!signature) {
        return new Response('Missing signature', { status: 400 });
    }
    
    try {
        const event = payments.verifyWebhook(payload, signature);
        
        switch (event.type) {
            case 'checkout.session.completed':
                await handleCheckoutComplete(event.data);
                break;
                
            case 'customer.subscription.created':
                await handleSubscriptionCreated(event.data);
                break;
                
            case 'customer.subscription.updated':
                await handleSubscriptionUpdated(event.data);
                break;
                
            case 'customer.subscription.deleted':
                await handleSubscriptionCanceled(event.data);
                break;
                
            case 'invoice.payment_failed':
                await handlePaymentFailed(event.data);
                break;
        }
        
        return new Response('OK', { status: 200 });
    } catch (error) {
        console.error('Webhook error:', error);
        return new Response('Webhook error', { status: 400 });
    }
}
```

### Event Types

| Event | Description |
|-------|-------------|
| `checkout.session.completed` | Checkout was successful |
| `customer.subscription.created` | New subscription created |
| `customer.subscription.updated` | Subscription changed |
| `customer.subscription.deleted` | Subscription canceled |
| `invoice.payment_failed` | Payment failed |
| `invoice.paid` | Invoice was paid |

---

## Customer Portal

Generate a link for customers to manage their subscriptions:

```typescript
const portalUrl = await payments.createPortalSession({
    customerId: 'cus_123',
    returnUrl: 'https://example.com/account',
});

// Redirect user to portalUrl
```

---

## API Reference

### createCheckout Options

| Option | Type | Description |
|--------|------|-------------|
| `mode` | `'payment' \| 'subscription'` | Checkout mode |
| `items` | `LineItem[]` | Items to purchase |
| `successUrl` | `string` | Redirect after success |
| `cancelUrl` | `string` | Redirect on cancel |
| `customerEmail` | `string` | Pre-fill email |
| `customerId` | `string` | Existing customer ID |
| `trialDays` | `number` | Subscription trial days |
| `allowPromoCodes` | `boolean` | Enable promo codes |
| `metadata` | `object` | Custom metadata |

### LineItem

| Property | Type | Description |
|----------|------|-------------|
| `name` | `string` | Item name (for one-time) |
| `price` | `number` | Price in cents |
| `priceId` | `string` | Existing price ID |
| `quantity` | `number` | Quantity |

### Subscription

| Property | Type | Description |
|----------|------|-------------|
| `id` | `string` | Subscription ID |
| `status` | `string` | active, past_due, canceled |
| `customerId` | `string` | Customer ID |
| `priceId` | `string` | Price ID |
| `currentPeriodStart` | `Date` | Period start |
| `currentPeriodEnd` | `Date` | Period end |
| `cancelAtPeriodEnd` | `boolean` | Will cancel at end |

---

## License

MIT