@flightdev/payments
v0.0.3
Published
Agnostic payments layer for Flight Framework. Choose your processor: Stripe, Paddle, MercadoPago.
Downloads
45
Readme
@flightdev/payments
Payment processing for Flight Framework. Unified API for Stripe, Paddle, and LemonSqueezy.
Table of Contents
- Features
- Installation
- Quick Start
- Adapters
- Checkout Sessions
- Subscriptions
- Webhooks
- Customer Portal
- API Reference
- 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
npm install @flightdev/payments
# Install your provider's SDK
npm install stripe # For Stripe
npm install @paddle/paddle-node-sdk # For PaddleQuick Start
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.urlAdapters
Stripe
Full-featured payment processing.
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.
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.
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
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: '[email protected]',
metadata: {
userId: 'user_123',
},
});Subscription Checkout
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
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
const subscription = await payments.getSubscription('sub_123');
console.log(subscription.status); // 'active'
console.log(subscription.currentPeriodEnd); // Date
console.log(subscription.cancelAtPeriodEnd); // booleanCancel Subscription
// Cancel at period end
await payments.cancelSubscription('sub_123', {
immediately: false
});
// Cancel immediately
await payments.cancelSubscription('sub_123', {
immediately: true
});Update Subscription
await payments.updateSubscription('sub_123', {
priceId: 'price_yearly_pro', // Upgrade/downgrade
prorationBehavior: 'create_prorations',
});Webhooks
Webhook Handler
// 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:
const portalUrl = await payments.createPortalSession({
customerId: 'cus_123',
returnUrl: 'https://example.com/account',
});
// Redirect user to portalUrlAPI 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
