@flightdev/payments 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Flight Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,323 @@
1
+ # @flight-framework/payments
2
+
3
+ Payment processing for Flight Framework. Unified API for Stripe, Paddle, and LemonSqueezy.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Features](#features)
8
+ - [Installation](#installation)
9
+ - [Quick Start](#quick-start)
10
+ - [Adapters](#adapters)
11
+ - [Checkout Sessions](#checkout-sessions)
12
+ - [Subscriptions](#subscriptions)
13
+ - [Webhooks](#webhooks)
14
+ - [Customer Portal](#customer-portal)
15
+ - [API Reference](#api-reference)
16
+ - [License](#license)
17
+
18
+ ---
19
+
20
+ ## Features
21
+
22
+ - Single API for multiple payment providers
23
+ - Checkout session management
24
+ - Subscription lifecycle handling
25
+ - Webhook verification and parsing
26
+ - Customer portal links
27
+ - Type-safe event handling
28
+ - Idempotency key support
29
+
30
+ ---
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ npm install @flight-framework/payments
36
+
37
+ # Install your provider's SDK
38
+ npm install stripe # For Stripe
39
+ npm install @paddle/paddle-node-sdk # For Paddle
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Quick Start
45
+
46
+ ```typescript
47
+ import { createPayments } from '@flight-framework/payments';
48
+ import { stripe } from '@flight-framework/payments/stripe';
49
+
50
+ const payments = createPayments(stripe({
51
+ secretKey: process.env.STRIPE_SECRET_KEY,
52
+ webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
53
+ }));
54
+
55
+ // Create a checkout session
56
+ const session = await payments.createCheckout({
57
+ items: [
58
+ { name: 'Pro Plan', price: 2999, quantity: 1 },
59
+ ],
60
+ successUrl: 'https://example.com/success',
61
+ cancelUrl: 'https://example.com/cancel',
62
+ });
63
+
64
+ // Redirect user to session.url
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Adapters
70
+
71
+ ### Stripe
72
+
73
+ Full-featured payment processing.
74
+
75
+ ```typescript
76
+ import { stripe } from '@flight-framework/payments/stripe';
77
+
78
+ const adapter = stripe({
79
+ secretKey: process.env.STRIPE_SECRET_KEY,
80
+ webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
81
+ apiVersion: '2024-12-18.acacia',
82
+ });
83
+ ```
84
+
85
+ ### Paddle
86
+
87
+ Merchant of record for global sales.
88
+
89
+ ```typescript
90
+ import { paddle } from '@flight-framework/payments/paddle';
91
+
92
+ const adapter = paddle({
93
+ apiKey: process.env.PADDLE_API_KEY,
94
+ environment: 'sandbox', // or 'production'
95
+ webhookSecret: process.env.PADDLE_WEBHOOK_SECRET,
96
+ });
97
+ ```
98
+
99
+ ### LemonSqueezy
100
+
101
+ Digital products and subscriptions.
102
+
103
+ ```typescript
104
+ import { lemonsqueezy } from '@flight-framework/payments/lemonsqueezy';
105
+
106
+ const adapter = lemonsqueezy({
107
+ apiKey: process.env.LEMON_SQUEEZY_API_KEY,
108
+ storeId: process.env.LEMON_SQUEEZY_STORE_ID,
109
+ webhookSecret: process.env.LEMON_SQUEEZY_WEBHOOK_SECRET,
110
+ });
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Checkout Sessions
116
+
117
+ ### One-Time Payment
118
+
119
+ ```typescript
120
+ const session = await payments.createCheckout({
121
+ mode: 'payment',
122
+ items: [
123
+ {
124
+ name: 'Flight Framework License',
125
+ price: 9900, // $99.00 in cents
126
+ quantity: 1,
127
+ },
128
+ ],
129
+ successUrl: 'https://example.com/success?session_id={CHECKOUT_SESSION_ID}',
130
+ cancelUrl: 'https://example.com/cancel',
131
+ customerEmail: 'user@example.com',
132
+ metadata: {
133
+ userId: 'user_123',
134
+ },
135
+ });
136
+ ```
137
+
138
+ ### Subscription Checkout
139
+
140
+ ```typescript
141
+ const session = await payments.createCheckout({
142
+ mode: 'subscription',
143
+ items: [
144
+ { priceId: 'price_monthly_pro' }, // Use existing price ID
145
+ ],
146
+ successUrl: 'https://example.com/success',
147
+ cancelUrl: 'https://example.com/cancel',
148
+ trialDays: 14,
149
+ });
150
+ ```
151
+
152
+ ### Custom Line Items
153
+
154
+ ```typescript
155
+ const session = await payments.createCheckout({
156
+ items: [
157
+ { name: 'Consulting Hour', price: 15000, quantity: 2 },
158
+ { name: 'Setup Fee', price: 5000, quantity: 1 },
159
+ ],
160
+ allowPromoCodes: true,
161
+ collectBillingAddress: true,
162
+ collectShippingAddress: false,
163
+ });
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Subscriptions
169
+
170
+ ### Retrieve Subscription
171
+
172
+ ```typescript
173
+ const subscription = await payments.getSubscription('sub_123');
174
+
175
+ console.log(subscription.status); // 'active'
176
+ console.log(subscription.currentPeriodEnd); // Date
177
+ console.log(subscription.cancelAtPeriodEnd); // boolean
178
+ ```
179
+
180
+ ### Cancel Subscription
181
+
182
+ ```typescript
183
+ // Cancel at period end
184
+ await payments.cancelSubscription('sub_123', {
185
+ immediately: false
186
+ });
187
+
188
+ // Cancel immediately
189
+ await payments.cancelSubscription('sub_123', {
190
+ immediately: true
191
+ });
192
+ ```
193
+
194
+ ### Update Subscription
195
+
196
+ ```typescript
197
+ await payments.updateSubscription('sub_123', {
198
+ priceId: 'price_yearly_pro', // Upgrade/downgrade
199
+ prorationBehavior: 'create_prorations',
200
+ });
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Webhooks
206
+
207
+ ### Webhook Handler
208
+
209
+ ```typescript
210
+ // src/routes/api/webhooks/payments.post.ts
211
+ import { payments } from '~/lib/payments';
212
+
213
+ export async function POST(request: Request) {
214
+ const payload = await request.text();
215
+ const signature = request.headers.get('stripe-signature');
216
+
217
+ if (!signature) {
218
+ return new Response('Missing signature', { status: 400 });
219
+ }
220
+
221
+ try {
222
+ const event = payments.verifyWebhook(payload, signature);
223
+
224
+ switch (event.type) {
225
+ case 'checkout.session.completed':
226
+ await handleCheckoutComplete(event.data);
227
+ break;
228
+
229
+ case 'customer.subscription.created':
230
+ await handleSubscriptionCreated(event.data);
231
+ break;
232
+
233
+ case 'customer.subscription.updated':
234
+ await handleSubscriptionUpdated(event.data);
235
+ break;
236
+
237
+ case 'customer.subscription.deleted':
238
+ await handleSubscriptionCanceled(event.data);
239
+ break;
240
+
241
+ case 'invoice.payment_failed':
242
+ await handlePaymentFailed(event.data);
243
+ break;
244
+ }
245
+
246
+ return new Response('OK', { status: 200 });
247
+ } catch (error) {
248
+ console.error('Webhook error:', error);
249
+ return new Response('Webhook error', { status: 400 });
250
+ }
251
+ }
252
+ ```
253
+
254
+ ### Event Types
255
+
256
+ | Event | Description |
257
+ |-------|-------------|
258
+ | `checkout.session.completed` | Checkout was successful |
259
+ | `customer.subscription.created` | New subscription created |
260
+ | `customer.subscription.updated` | Subscription changed |
261
+ | `customer.subscription.deleted` | Subscription canceled |
262
+ | `invoice.payment_failed` | Payment failed |
263
+ | `invoice.paid` | Invoice was paid |
264
+
265
+ ---
266
+
267
+ ## Customer Portal
268
+
269
+ Generate a link for customers to manage their subscriptions:
270
+
271
+ ```typescript
272
+ const portalUrl = await payments.createPortalSession({
273
+ customerId: 'cus_123',
274
+ returnUrl: 'https://example.com/account',
275
+ });
276
+
277
+ // Redirect user to portalUrl
278
+ ```
279
+
280
+ ---
281
+
282
+ ## API Reference
283
+
284
+ ### createCheckout Options
285
+
286
+ | Option | Type | Description |
287
+ |--------|------|-------------|
288
+ | `mode` | `'payment' \| 'subscription'` | Checkout mode |
289
+ | `items` | `LineItem[]` | Items to purchase |
290
+ | `successUrl` | `string` | Redirect after success |
291
+ | `cancelUrl` | `string` | Redirect on cancel |
292
+ | `customerEmail` | `string` | Pre-fill email |
293
+ | `customerId` | `string` | Existing customer ID |
294
+ | `trialDays` | `number` | Subscription trial days |
295
+ | `allowPromoCodes` | `boolean` | Enable promo codes |
296
+ | `metadata` | `object` | Custom metadata |
297
+
298
+ ### LineItem
299
+
300
+ | Property | Type | Description |
301
+ |----------|------|-------------|
302
+ | `name` | `string` | Item name (for one-time) |
303
+ | `price` | `number` | Price in cents |
304
+ | `priceId` | `string` | Existing price ID |
305
+ | `quantity` | `number` | Quantity |
306
+
307
+ ### Subscription
308
+
309
+ | Property | Type | Description |
310
+ |----------|------|-------------|
311
+ | `id` | `string` | Subscription ID |
312
+ | `status` | `string` | active, past_due, canceled |
313
+ | `customerId` | `string` | Customer ID |
314
+ | `priceId` | `string` | Price ID |
315
+ | `currentPeriodStart` | `Date` | Period start |
316
+ | `currentPeriodEnd` | `Date` | Period end |
317
+ | `cancelAtPeriodEnd` | `boolean` | Will cancel at end |
318
+
319
+ ---
320
+
321
+ ## License
322
+
323
+ MIT
@@ -0,0 +1,13 @@
1
+ import { PaymentsAdapterFactory } from '../index.js';
2
+
3
+ /**
4
+ * Stripe Adapter for @flightdev/payments
5
+ */
6
+
7
+ interface StripeConfig {
8
+ secretKey: string;
9
+ webhookSecret?: string;
10
+ }
11
+ declare const stripe: PaymentsAdapterFactory<StripeConfig>;
12
+
13
+ export { type StripeConfig, stripe as default, stripe };
@@ -0,0 +1,97 @@
1
+ // src/adapters/stripe.ts
2
+ var stripe = (config) => {
3
+ let stripeClient = null;
4
+ async function getStripe() {
5
+ if (!stripeClient) {
6
+ try {
7
+ const Stripe = (await import("stripe")).default;
8
+ stripeClient = new Stripe(config.secretKey);
9
+ } catch {
10
+ throw new Error("@flightdev/payments: stripe not installed. Run: npm install stripe");
11
+ }
12
+ }
13
+ return stripeClient;
14
+ }
15
+ const adapter = {
16
+ name: "stripe",
17
+ async createCheckout(options) {
18
+ const stripe2 = await getStripe();
19
+ const session = await stripe2.checkout.sessions.create({
20
+ mode: options.mode ?? "payment",
21
+ line_items: options.items.map((item) => ({
22
+ price_data: {
23
+ currency: "usd",
24
+ unit_amount: item.price,
25
+ product_data: { name: item.name, description: item.description }
26
+ },
27
+ quantity: item.quantity
28
+ })),
29
+ success_url: options.successUrl,
30
+ cancel_url: options.cancelUrl,
31
+ customer_email: options.customerEmail,
32
+ customer: options.customerId,
33
+ metadata: options.metadata
34
+ });
35
+ return {
36
+ id: session.id,
37
+ url: session.url ?? "",
38
+ status: session.status === "complete" ? "complete" : "pending"
39
+ };
40
+ },
41
+ async getSession(sessionId) {
42
+ const stripe2 = await getStripe();
43
+ try {
44
+ const session = await stripe2.checkout.sessions.retrieve(sessionId);
45
+ return {
46
+ id: session.id,
47
+ url: session.url ?? "",
48
+ status: session.status === "complete" ? "complete" : "pending"
49
+ };
50
+ } catch {
51
+ return null;
52
+ }
53
+ },
54
+ async createCustomer(email, metadata) {
55
+ const stripe2 = await getStripe();
56
+ const customer = await stripe2.customers.create({ email, metadata });
57
+ return { id: customer.id, email: customer.email, name: customer.name ?? void 0, metadata: customer.metadata };
58
+ },
59
+ async getCustomer(customerId) {
60
+ const stripe2 = await getStripe();
61
+ try {
62
+ const customer = await stripe2.customers.retrieve(customerId);
63
+ return { id: customer.id, email: customer.email, name: customer.name ?? void 0, metadata: customer.metadata };
64
+ } catch {
65
+ return null;
66
+ }
67
+ },
68
+ async createSubscription(customerId, priceId) {
69
+ const stripe2 = await getStripe();
70
+ const sub = await stripe2.subscriptions.create({ customer: customerId, items: [{ price: priceId }] });
71
+ return {
72
+ id: sub.id,
73
+ customerId: sub.customer,
74
+ status: sub.status,
75
+ currentPeriodEnd: new Date(sub.current_period_end * 1e3),
76
+ priceId: sub.items.data[0]?.price.id ?? ""
77
+ };
78
+ },
79
+ async cancelSubscription(subscriptionId) {
80
+ const stripe2 = await getStripe();
81
+ await stripe2.subscriptions.cancel(subscriptionId);
82
+ },
83
+ verifyWebhook(payload, _signature) {
84
+ if (!config.webhookSecret) {
85
+ throw new Error("Webhook secret not configured");
86
+ }
87
+ const event = JSON.parse(payload);
88
+ return { id: event.id, type: event.type, data: event.data.object };
89
+ }
90
+ };
91
+ return adapter;
92
+ };
93
+ var stripe_default = stripe;
94
+ export {
95
+ stripe_default as default,
96
+ stripe
97
+ };
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @flightdev/payments - Agnostic Payments Layer
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * import { createPayments } from '@flightdev/payments';
7
+ * import { stripe } from '@flightdev/payments/stripe';
8
+ *
9
+ * const payments = createPayments(stripe({
10
+ * secretKey: process.env.STRIPE_SECRET_KEY,
11
+ * }));
12
+ *
13
+ * const session = await payments.createCheckout({
14
+ * items: [{ name: 'Pro Plan', price: 2999, quantity: 1 }],
15
+ * successUrl: '/success',
16
+ * cancelUrl: '/cancel',
17
+ * });
18
+ * ```
19
+ */
20
+ interface LineItem {
21
+ name: string;
22
+ price: number;
23
+ quantity: number;
24
+ description?: string;
25
+ image?: string;
26
+ }
27
+ interface CheckoutOptions {
28
+ items: LineItem[];
29
+ successUrl: string;
30
+ cancelUrl: string;
31
+ customerEmail?: string;
32
+ customerId?: string;
33
+ metadata?: Record<string, string>;
34
+ mode?: 'payment' | 'subscription';
35
+ }
36
+ interface CheckoutSession {
37
+ id: string;
38
+ url: string;
39
+ status: 'pending' | 'complete' | 'expired';
40
+ }
41
+ interface Customer {
42
+ id: string;
43
+ email: string;
44
+ name?: string;
45
+ metadata?: Record<string, string>;
46
+ }
47
+ interface Subscription {
48
+ id: string;
49
+ customerId: string;
50
+ status: 'active' | 'canceled' | 'past_due' | 'trialing';
51
+ currentPeriodEnd: Date;
52
+ priceId: string;
53
+ }
54
+ interface WebhookEvent {
55
+ id: string;
56
+ type: string;
57
+ data: unknown;
58
+ }
59
+ interface PaymentsAdapter {
60
+ readonly name: string;
61
+ createCheckout(options: CheckoutOptions): Promise<CheckoutSession>;
62
+ getSession(sessionId: string): Promise<CheckoutSession | null>;
63
+ createCustomer(email: string, metadata?: Record<string, string>): Promise<Customer>;
64
+ getCustomer(customerId: string): Promise<Customer | null>;
65
+ createSubscription(customerId: string, priceId: string): Promise<Subscription>;
66
+ cancelSubscription(subscriptionId: string): Promise<void>;
67
+ verifyWebhook(payload: string, signature: string): WebhookEvent;
68
+ }
69
+ type PaymentsAdapterFactory<TConfig = unknown> = (config: TConfig) => PaymentsAdapter;
70
+ interface PaymentsService {
71
+ readonly adapter: PaymentsAdapter;
72
+ createCheckout(options: CheckoutOptions): Promise<CheckoutSession>;
73
+ getSession(sessionId: string): Promise<CheckoutSession | null>;
74
+ createCustomer(email: string, metadata?: Record<string, string>): Promise<Customer>;
75
+ getCustomer(customerId: string): Promise<Customer | null>;
76
+ createSubscription(customerId: string, priceId: string): Promise<Subscription>;
77
+ cancelSubscription(subscriptionId: string): Promise<void>;
78
+ handleWebhook(payload: string, signature: string): WebhookEvent;
79
+ }
80
+ declare function createPayments(adapter: PaymentsAdapter): PaymentsService;
81
+ declare function formatCurrency(amount: number, currency: string, locale?: string): string;
82
+
83
+ export { type CheckoutOptions, type CheckoutSession, type Customer, type LineItem, type PaymentsAdapter, type PaymentsAdapterFactory, type PaymentsService, type Subscription, type WebhookEvent, createPayments, formatCurrency };
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ // src/index.ts
2
+ function createPayments(adapter) {
3
+ return {
4
+ adapter,
5
+ createCheckout: (options) => adapter.createCheckout(options),
6
+ getSession: (id) => adapter.getSession(id),
7
+ createCustomer: (email, meta) => adapter.createCustomer(email, meta),
8
+ getCustomer: (id) => adapter.getCustomer(id),
9
+ createSubscription: (customerId, priceId) => adapter.createSubscription(customerId, priceId),
10
+ cancelSubscription: (id) => adapter.cancelSubscription(id),
11
+ handleWebhook: (payload, signature) => adapter.verifyWebhook(payload, signature)
12
+ };
13
+ }
14
+ function formatCurrency(amount, currency, locale = "en-US") {
15
+ return new Intl.NumberFormat(locale, {
16
+ style: "currency",
17
+ currency: currency.toUpperCase()
18
+ }).format(amount / 100);
19
+ }
20
+ export {
21
+ createPayments,
22
+ formatCurrency
23
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@flightdev/payments",
3
+ "version": "0.0.2",
4
+ "description": "Agnostic payments layer for Flight Framework. Choose your processor: Stripe, Paddle, MercadoPago.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ },
11
+ "./stripe": {
12
+ "types": "./dist/adapters/stripe.d.ts",
13
+ "import": "./dist/adapters/stripe.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "peerDependencies": {
20
+ "stripe": ">=14.0.0"
21
+ },
22
+ "peerDependenciesMeta": {
23
+ "stripe": {
24
+ "optional": true
25
+ }
26
+ },
27
+ "devDependencies": {
28
+ "tsup": "^8.0.0",
29
+ "typescript": "^5.7.0",
30
+ "vitest": "^2.0.0"
31
+ },
32
+ "license": "MIT",
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "test": "vitest run",
36
+ "typecheck": "tsc --noEmit"
37
+ }
38
+ }