@baasix/plugin-stripe 0.1.0
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/README.md +473 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +149 -0
- package/dist/index.js.map +1 -0
- package/dist/routes/checkout.d.ts +24 -0
- package/dist/routes/checkout.d.ts.map +1 -0
- package/dist/routes/checkout.js +58 -0
- package/dist/routes/checkout.js.map +1 -0
- package/dist/routes/index.d.ts +26 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +44 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/portal.d.ts +31 -0
- package/dist/routes/portal.d.ts.map +1 -0
- package/dist/routes/portal.js +78 -0
- package/dist/routes/portal.js.map +1 -0
- package/dist/routes/products.d.ts +26 -0
- package/dist/routes/products.d.ts.map +1 -0
- package/dist/routes/products.js +65 -0
- package/dist/routes/products.js.map +1 -0
- package/dist/routes/subscription.d.ts +32 -0
- package/dist/routes/subscription.d.ts.map +1 -0
- package/dist/routes/subscription.js +121 -0
- package/dist/routes/subscription.js.map +1 -0
- package/dist/routes/webhook.d.ts +15 -0
- package/dist/routes/webhook.d.ts.map +1 -0
- package/dist/routes/webhook.js +52 -0
- package/dist/routes/webhook.js.map +1 -0
- package/dist/schemas/index.d.ts +31 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +140 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/services/stripeService.d.ts +58 -0
- package/dist/services/stripeService.d.ts.map +1 -0
- package/dist/services/stripeService.js +431 -0
- package/dist/services/stripeService.js.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/loadStripe.d.ts +28 -0
- package/dist/utils/loadStripe.d.ts.map +1 -0
- package/dist/utils/loadStripe.js +59 -0
- package/dist/utils/loadStripe.js.map +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
# @baasix/plugin-stripe
|
|
2
|
+
|
|
3
|
+
Stripe payment integration plugin for Baasix. Supports one-time payments and subscriptions with Stripe Checkout.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- One-time payments via Stripe Checkout
|
|
8
|
+
- Subscription management with recurring billing
|
|
9
|
+
- Customer portal for self-service billing management
|
|
10
|
+
- Webhook handling for payment events
|
|
11
|
+
- Product/price synchronization from Stripe
|
|
12
|
+
- Automatic customer creation and mapping
|
|
13
|
+
- **Singleton service** - use anywhere in your extensions
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @baasix/plugin-stripe stripe
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { startServer } from '@baasix/baasix';
|
|
25
|
+
import { stripePlugin } from '@baasix/plugin-stripe';
|
|
26
|
+
|
|
27
|
+
startServer({
|
|
28
|
+
port: 8055,
|
|
29
|
+
plugins: [
|
|
30
|
+
stripePlugin({
|
|
31
|
+
secretKey: process.env.STRIPE_SECRET_KEY!,
|
|
32
|
+
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
|
|
33
|
+
currency: 'usd',
|
|
34
|
+
syncProductsOnStartup: true,
|
|
35
|
+
})
|
|
36
|
+
]
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Configuration Options
|
|
41
|
+
|
|
42
|
+
| Option | Type | Required | Default | Description |
|
|
43
|
+
|--------|------|----------|---------|-------------|
|
|
44
|
+
| `secretKey` | string | Yes | - | Your Stripe secret key |
|
|
45
|
+
| `webhookSecret` | string | Yes | - | Webhook signing secret from Stripe |
|
|
46
|
+
| `currency` | string | No | 'usd' | Default currency for payments |
|
|
47
|
+
| `syncProductsOnStartup` | boolean | No | false | Sync products from Stripe on server start |
|
|
48
|
+
| `apiVersion` | string | No | '2025-01-27.acacia' | Stripe API version |
|
|
49
|
+
|
|
50
|
+
## Using the Service in Extensions
|
|
51
|
+
|
|
52
|
+
The Stripe service is available as a **singleton** after plugin initialization. You can use it anywhere in your code without needing to reinitialize.
|
|
53
|
+
|
|
54
|
+
### In Custom Endpoints
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// extensions/endpoints/custom-payment.ts
|
|
58
|
+
import { getStripeService } from '@baasix/plugin-stripe';
|
|
59
|
+
|
|
60
|
+
export default (router) => {
|
|
61
|
+
router.post('/custom-checkout', async (req, res) => {
|
|
62
|
+
const stripeService = getStripeService();
|
|
63
|
+
const userId = req.accountability?.user?.id;
|
|
64
|
+
|
|
65
|
+
// Create a custom checkout session
|
|
66
|
+
const session = await stripeService.createCheckoutSession({
|
|
67
|
+
userId,
|
|
68
|
+
priceId: req.body.priceId,
|
|
69
|
+
successUrl: req.body.successUrl,
|
|
70
|
+
cancelUrl: req.body.cancelUrl,
|
|
71
|
+
metadata: { orderId: req.body.orderId },
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
res.json(session);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
router.get('/my-subscriptions', async (req, res) => {
|
|
78
|
+
const stripeService = getStripeService();
|
|
79
|
+
const userId = req.accountability?.user?.id;
|
|
80
|
+
|
|
81
|
+
const subscriptions = await stripeService.getUserSubscriptions(userId);
|
|
82
|
+
res.json(subscriptions);
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### In Hooks
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// extensions/hooks/stripe-hooks.ts
|
|
91
|
+
import { getStripeService } from '@baasix/plugin-stripe';
|
|
92
|
+
|
|
93
|
+
export default {
|
|
94
|
+
// Pre-create Stripe customer when a user registers
|
|
95
|
+
'items.create.after': async (context) => {
|
|
96
|
+
if (context.collection === 'baasix_User') {
|
|
97
|
+
const stripeService = getStripeService();
|
|
98
|
+
await stripeService.getOrCreateCustomer(context.data.id);
|
|
99
|
+
console.log('Stripe customer created for new user');
|
|
100
|
+
}
|
|
101
|
+
return context;
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
// Clean up when user is deleted (optional)
|
|
105
|
+
'items.delete': async (context) => {
|
|
106
|
+
if (context.collection === 'baasix_User') {
|
|
107
|
+
// Handle Stripe customer cleanup if needed
|
|
108
|
+
const stripeService = getStripeService();
|
|
109
|
+
const customer = await stripeService.getCustomer(context.id);
|
|
110
|
+
if (customer) {
|
|
111
|
+
// Cancel active subscriptions, etc.
|
|
112
|
+
const subscriptions = await stripeService.getUserSubscriptions(context.id);
|
|
113
|
+
for (const sub of subscriptions) {
|
|
114
|
+
if (sub.status === 'active') {
|
|
115
|
+
await stripeService.manageSubscription(context.id, sub.id, 'cancel');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return context;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### In Other Services or Utilities
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// services/billing.ts
|
|
129
|
+
import { getStripeService, isStripeServiceInitialized } from '@baasix/plugin-stripe';
|
|
130
|
+
|
|
131
|
+
export async function checkUserSubscription(userId: string) {
|
|
132
|
+
// Optionally check if service is ready
|
|
133
|
+
if (!isStripeServiceInitialized()) {
|
|
134
|
+
throw new Error('Stripe plugin not loaded');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const stripeService = getStripeService();
|
|
138
|
+
const subscriptions = await stripeService.getUserSubscriptions(userId);
|
|
139
|
+
|
|
140
|
+
return subscriptions.some(sub =>
|
|
141
|
+
sub.status === 'active' || sub.status === 'trialing'
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function getActiveSubscriptionTier(userId: string) {
|
|
146
|
+
const stripeService = getStripeService();
|
|
147
|
+
const subscriptions = await stripeService.getUserSubscriptions(userId);
|
|
148
|
+
|
|
149
|
+
const active = subscriptions.find(sub => sub.status === 'active');
|
|
150
|
+
if (!active) return null;
|
|
151
|
+
|
|
152
|
+
// Get the product details
|
|
153
|
+
const products = await stripeService.getProducts();
|
|
154
|
+
return products.find(p => p.stripePriceId === active.stripePriceId);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Accessing the Stripe SDK Directly
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { getStripeService } from '@baasix/plugin-stripe';
|
|
162
|
+
|
|
163
|
+
async function createCustomPaymentIntent() {
|
|
164
|
+
const stripeService = getStripeService();
|
|
165
|
+
|
|
166
|
+
// Access the Stripe SDK directly for advanced operations
|
|
167
|
+
const stripe = stripeService.stripe;
|
|
168
|
+
|
|
169
|
+
const paymentIntent = await stripe.paymentIntents.create({
|
|
170
|
+
amount: 2000,
|
|
171
|
+
currency: 'usd',
|
|
172
|
+
automatic_payment_methods: { enabled: true },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return paymentIntent;
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Database Tables
|
|
180
|
+
|
|
181
|
+
The plugin creates the following tables:
|
|
182
|
+
|
|
183
|
+
### baasix_StripeCustomer
|
|
184
|
+
|
|
185
|
+
Maps Baasix users to Stripe customers.
|
|
186
|
+
|
|
187
|
+
| Column | Type | Description |
|
|
188
|
+
|--------|------|-------------|
|
|
189
|
+
| id | UUID | Primary key |
|
|
190
|
+
| user_Id | UUID | Reference to baasix_User |
|
|
191
|
+
| stripeCustomerId | String | Stripe customer ID |
|
|
192
|
+
| email | String | Customer email |
|
|
193
|
+
| metadata | JSON | Additional metadata |
|
|
194
|
+
| createdAt | DateTime | Created timestamp |
|
|
195
|
+
| updatedAt | DateTime | Updated timestamp |
|
|
196
|
+
|
|
197
|
+
### baasix_StripePayment
|
|
198
|
+
|
|
199
|
+
Records one-time payments.
|
|
200
|
+
|
|
201
|
+
| Column | Type | Description |
|
|
202
|
+
|--------|------|-------------|
|
|
203
|
+
| id | UUID | Primary key |
|
|
204
|
+
| customer_Id | UUID | Reference to StripeCustomer |
|
|
205
|
+
| stripePaymentIntentId | String | Stripe payment intent ID |
|
|
206
|
+
| stripeCheckoutSessionId | String | Stripe checkout session ID |
|
|
207
|
+
| amount | Integer | Amount in cents |
|
|
208
|
+
| currency | String | Currency code |
|
|
209
|
+
| status | Enum | Payment status |
|
|
210
|
+
| metadata | JSON | Additional metadata |
|
|
211
|
+
|
|
212
|
+
### baasix_StripeSubscription
|
|
213
|
+
|
|
214
|
+
Tracks subscriptions.
|
|
215
|
+
|
|
216
|
+
| Column | Type | Description |
|
|
217
|
+
|--------|------|-------------|
|
|
218
|
+
| id | UUID | Primary key |
|
|
219
|
+
| customer_Id | UUID | Reference to StripeCustomer |
|
|
220
|
+
| stripeSubscriptionId | String | Stripe subscription ID |
|
|
221
|
+
| stripePriceId | String | Stripe price ID |
|
|
222
|
+
| status | Enum | Subscription status |
|
|
223
|
+
| currentPeriodStart | DateTime | Current billing period start |
|
|
224
|
+
| currentPeriodEnd | DateTime | Current billing period end |
|
|
225
|
+
| cancelAtPeriodEnd | Boolean | Whether subscription cancels at period end |
|
|
226
|
+
|
|
227
|
+
### baasix_StripeProduct
|
|
228
|
+
|
|
229
|
+
Cached products and prices from Stripe.
|
|
230
|
+
|
|
231
|
+
| Column | Type | Description |
|
|
232
|
+
|--------|------|-------------|
|
|
233
|
+
| id | UUID | Primary key |
|
|
234
|
+
| stripeProductId | String | Stripe product ID |
|
|
235
|
+
| stripePriceId | String | Stripe price ID |
|
|
236
|
+
| name | String | Product name |
|
|
237
|
+
| description | Text | Product description |
|
|
238
|
+
| amount | Integer | Price in cents |
|
|
239
|
+
| currency | String | Currency code |
|
|
240
|
+
| interval | Enum | Billing interval |
|
|
241
|
+
| active | Boolean | Whether product is active |
|
|
242
|
+
|
|
243
|
+
## API Endpoints
|
|
244
|
+
|
|
245
|
+
### One-Time Payments
|
|
246
|
+
|
|
247
|
+
#### Create Checkout Session
|
|
248
|
+
```http
|
|
249
|
+
POST /payments/stripe/checkout
|
|
250
|
+
Authorization: Bearer <token>
|
|
251
|
+
Content-Type: application/json
|
|
252
|
+
|
|
253
|
+
{
|
|
254
|
+
"priceId": "price_xxx",
|
|
255
|
+
"quantity": 1,
|
|
256
|
+
"successUrl": "https://example.com/success",
|
|
257
|
+
"cancelUrl": "https://example.com/cancel",
|
|
258
|
+
"metadata": { "orderId": "123" }
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Response:**
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"sessionId": "cs_xxx",
|
|
266
|
+
"url": "https://checkout.stripe.com/..."
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Subscriptions
|
|
271
|
+
|
|
272
|
+
#### Create Subscription Checkout
|
|
273
|
+
```http
|
|
274
|
+
POST /payments/stripe/subscribe
|
|
275
|
+
Authorization: Bearer <token>
|
|
276
|
+
Content-Type: application/json
|
|
277
|
+
|
|
278
|
+
{
|
|
279
|
+
"priceId": "price_xxx",
|
|
280
|
+
"successUrl": "https://example.com/success",
|
|
281
|
+
"cancelUrl": "https://example.com/cancel",
|
|
282
|
+
"trialDays": 14,
|
|
283
|
+
"metadata": { "plan": "pro" }
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### Manage Subscription
|
|
288
|
+
```http
|
|
289
|
+
PATCH /payments/stripe/subscription/:id
|
|
290
|
+
Authorization: Bearer <token>
|
|
291
|
+
Content-Type: application/json
|
|
292
|
+
|
|
293
|
+
{
|
|
294
|
+
"action": "cancel" | "resume"
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
#### Get User's Subscriptions
|
|
299
|
+
```http
|
|
300
|
+
GET /payments/stripe/subscriptions
|
|
301
|
+
Authorization: Bearer <token>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Customer Portal
|
|
305
|
+
|
|
306
|
+
#### Create Portal Session
|
|
307
|
+
```http
|
|
308
|
+
POST /payments/stripe/portal
|
|
309
|
+
Authorization: Bearer <token>
|
|
310
|
+
Content-Type: application/json
|
|
311
|
+
|
|
312
|
+
{
|
|
313
|
+
"returnUrl": "https://example.com/account"
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**Response:**
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"url": "https://billing.stripe.com/..."
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### User Data
|
|
325
|
+
|
|
326
|
+
#### Get User's Payments
|
|
327
|
+
```http
|
|
328
|
+
GET /payments/stripe/payments
|
|
329
|
+
Authorization: Bearer <token>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Products
|
|
333
|
+
|
|
334
|
+
#### Get Available Products
|
|
335
|
+
```http
|
|
336
|
+
GET /payments/stripe/products
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### Sync Products (Admin Only)
|
|
340
|
+
```http
|
|
341
|
+
POST /payments/stripe/sync-products
|
|
342
|
+
Authorization: Bearer <admin-token>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Webhook
|
|
346
|
+
|
|
347
|
+
#### Handle Stripe Events
|
|
348
|
+
```http
|
|
349
|
+
POST /payments/stripe/webhook
|
|
350
|
+
Stripe-Signature: <signature>
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Configure this URL in your Stripe Dashboard under Developers > Webhooks.
|
|
354
|
+
|
|
355
|
+
**Handled Events:**
|
|
356
|
+
- `checkout.session.completed` - Records completed payments
|
|
357
|
+
- `payment_intent.succeeded` - Updates payment status
|
|
358
|
+
- `payment_intent.payment_failed` - Marks payment as failed
|
|
359
|
+
- `customer.subscription.created` - Creates subscription record
|
|
360
|
+
- `customer.subscription.updated` - Updates subscription
|
|
361
|
+
- `customer.subscription.deleted` - Marks subscription as canceled
|
|
362
|
+
- `invoice.payment_succeeded` - Updates subscription after renewal
|
|
363
|
+
- `invoice.payment_failed` - Marks subscription as past_due
|
|
364
|
+
|
|
365
|
+
## Testing Webhooks Locally
|
|
366
|
+
|
|
367
|
+
Use the Stripe CLI to forward webhooks to your local server:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
# Install Stripe CLI
|
|
371
|
+
brew install stripe/stripe-cli/stripe
|
|
372
|
+
|
|
373
|
+
# Login to your Stripe account
|
|
374
|
+
stripe login
|
|
375
|
+
|
|
376
|
+
# Forward webhooks to your local server
|
|
377
|
+
stripe listen --forward-to localhost:8055/payments/stripe/webhook
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
This will output a webhook signing secret to use in your configuration.
|
|
381
|
+
|
|
382
|
+
## Service API Reference
|
|
383
|
+
|
|
384
|
+
### getStripeService()
|
|
385
|
+
|
|
386
|
+
Returns the Stripe service singleton. Throws an error if the plugin hasn't been initialized.
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
import { getStripeService } from '@baasix/plugin-stripe';
|
|
390
|
+
|
|
391
|
+
const stripeService = getStripeService();
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### isStripeServiceInitialized()
|
|
395
|
+
|
|
396
|
+
Check if the Stripe service has been initialized.
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
import { isStripeServiceInitialized } from '@baasix/plugin-stripe';
|
|
400
|
+
|
|
401
|
+
if (isStripeServiceInitialized()) {
|
|
402
|
+
// Safe to use getStripeService()
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Service Methods
|
|
407
|
+
|
|
408
|
+
| Method | Description |
|
|
409
|
+
|--------|-------------|
|
|
410
|
+
| `getOrCreateCustomer(userId)` | Get or create a Stripe customer for a user |
|
|
411
|
+
| `getCustomer(userId)` | Get a Stripe customer by user ID |
|
|
412
|
+
| `createCheckoutSession(options)` | Create a checkout session for one-time payment |
|
|
413
|
+
| `createSubscriptionCheckout(options)` | Create a checkout session for subscription |
|
|
414
|
+
| `createPortalSession(userId, returnUrl)` | Create a billing portal session |
|
|
415
|
+
| `manageSubscription(userId, subscriptionId, action)` | Cancel or resume a subscription |
|
|
416
|
+
| `getUserPayments(userId)` | Get a user's payment history |
|
|
417
|
+
| `getUserSubscriptions(userId)` | Get a user's subscriptions |
|
|
418
|
+
| `getProducts()` | Get all active products |
|
|
419
|
+
| `syncProducts()` | Sync products from Stripe |
|
|
420
|
+
| `handleWebhook(event)` | Handle a Stripe webhook event |
|
|
421
|
+
| `syncSubscription(subscription)` | Sync a subscription from Stripe |
|
|
422
|
+
| `stripe` | Access the Stripe SDK instance directly |
|
|
423
|
+
|
|
424
|
+
## Type Definitions
|
|
425
|
+
|
|
426
|
+
Import types for TypeScript support:
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import type {
|
|
430
|
+
StripePluginConfig,
|
|
431
|
+
StripeCustomerRecord,
|
|
432
|
+
StripePaymentRecord,
|
|
433
|
+
StripeSubscriptionRecord,
|
|
434
|
+
StripeProductRecord,
|
|
435
|
+
StripeServiceInterface
|
|
436
|
+
} from '@baasix/plugin-stripe';
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Plugin Structure
|
|
440
|
+
|
|
441
|
+
```
|
|
442
|
+
src/
|
|
443
|
+
├── index.ts # Main entry point & exports
|
|
444
|
+
├── types.ts # All type definitions
|
|
445
|
+
├── schemas/
|
|
446
|
+
│ └── index.ts # Database schema definitions
|
|
447
|
+
├── services/
|
|
448
|
+
│ └── stripeService.ts # Service implementation & singleton
|
|
449
|
+
├── routes/
|
|
450
|
+
│ ├── index.ts # Routes aggregator
|
|
451
|
+
│ ├── checkout.ts # One-time payment checkout
|
|
452
|
+
│ ├── subscription.ts # Subscription management
|
|
453
|
+
│ ├── portal.ts # Customer portal & payments
|
|
454
|
+
│ ├── products.ts # Product listing & sync
|
|
455
|
+
│ └── webhook.ts # Webhook handler
|
|
456
|
+
└── utils/
|
|
457
|
+
└── loadStripe.ts # Dynamic Stripe SDK loading
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Creating Your Own Plugin
|
|
461
|
+
|
|
462
|
+
This plugin serves as an example for creating Baasix plugins. Key patterns:
|
|
463
|
+
|
|
464
|
+
1. **Types** - Define all types in `types.ts`
|
|
465
|
+
2. **Schemas** - Define database schemas in `schemas/`
|
|
466
|
+
3. **Services** - Implement business logic in `services/`
|
|
467
|
+
4. **Routes** - Define API endpoints in `routes/`
|
|
468
|
+
5. **Singleton** - Use `globalThis` for services that should be accessible everywhere
|
|
469
|
+
6. **Lifecycle** - Use `onInit`, `onReady`, `onShutdown` for setup/teardown
|
|
470
|
+
|
|
471
|
+
## License
|
|
472
|
+
|
|
473
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baasix Stripe Plugin
|
|
3
|
+
*
|
|
4
|
+
* A comprehensive Stripe payment integration plugin for Baasix that provides:
|
|
5
|
+
* - One-time payments via Stripe Checkout
|
|
6
|
+
* - Subscription management with recurring billing
|
|
7
|
+
* - Customer portal for self-service billing
|
|
8
|
+
* - Webhook handling for payment events
|
|
9
|
+
* - Product/price synchronization from Stripe
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { startServer } from '@baasix/baasix';
|
|
16
|
+
* import { stripePlugin } from '@baasix/plugin-stripe';
|
|
17
|
+
*
|
|
18
|
+
* startServer({
|
|
19
|
+
* port: 8055,
|
|
20
|
+
* plugins: [
|
|
21
|
+
* stripePlugin({
|
|
22
|
+
* secretKey: process.env.STRIPE_SECRET_KEY!,
|
|
23
|
+
* webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
|
|
24
|
+
* currency: 'usd',
|
|
25
|
+
* syncProductsOnStartup: true,
|
|
26
|
+
* })
|
|
27
|
+
* ]
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
import type { PluginDefinition, StripePluginConfig } from "./types.js";
|
|
32
|
+
/**
|
|
33
|
+
* Creates the Stripe plugin
|
|
34
|
+
*
|
|
35
|
+
* @param config - Plugin configuration
|
|
36
|
+
* @returns The plugin definition
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const plugin = stripePlugin({
|
|
41
|
+
* secretKey: 'sk_test_...',
|
|
42
|
+
* webhookSecret: 'whsec_...',
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function stripePlugin(config: StripePluginConfig): PluginDefinition;
|
|
47
|
+
export type { StripePluginConfig, StripeCustomerRecord, StripePaymentRecord, StripeSubscriptionRecord, StripeProductRecord, StripeServiceInterface, } from "./types.js";
|
|
48
|
+
export { stripeSchemas } from "./schemas/index.js";
|
|
49
|
+
export { createStripeService, getStripeService, isStripeServiceInitialized, } from "./services/stripeService.js";
|
|
50
|
+
export { createStripeRoutes, checkoutRoute, subscribeRoute, manageSubscriptionRoute, getUserSubscriptionsRoute, portalRoute, getPaymentsRoute, getProductsRoute, syncProductsRoute, createWebhookRoute, } from "./routes/index.js";
|
|
51
|
+
export { loadStripeSDK, createStripeGetter } from "./utils/loadStripe.js";
|
|
52
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAEV,gBAAgB,EAChB,kBAAkB,EAEnB,MAAM,YAAY,CAAC;AA8BpB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,gBAAgB,CAgFzE;AAGD,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,yBAAyB,EACzB,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baasix Stripe Plugin
|
|
3
|
+
*
|
|
4
|
+
* A comprehensive Stripe payment integration plugin for Baasix that provides:
|
|
5
|
+
* - One-time payments via Stripe Checkout
|
|
6
|
+
* - Subscription management with recurring billing
|
|
7
|
+
* - Customer portal for self-service billing
|
|
8
|
+
* - Webhook handling for payment events
|
|
9
|
+
* - Product/price synchronization from Stripe
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { startServer } from '@baasix/baasix';
|
|
16
|
+
* import { stripePlugin } from '@baasix/plugin-stripe';
|
|
17
|
+
*
|
|
18
|
+
* startServer({
|
|
19
|
+
* port: 8055,
|
|
20
|
+
* plugins: [
|
|
21
|
+
* stripePlugin({
|
|
22
|
+
* secretKey: process.env.STRIPE_SECRET_KEY!,
|
|
23
|
+
* webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
|
|
24
|
+
* currency: 'usd',
|
|
25
|
+
* syncProductsOnStartup: true,
|
|
26
|
+
* })
|
|
27
|
+
* ]
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
// Schemas
|
|
32
|
+
import { stripeSchemas } from "./schemas/index.js";
|
|
33
|
+
// Services
|
|
34
|
+
import { createStripeService } from "./services/stripeService.js";
|
|
35
|
+
// Routes
|
|
36
|
+
import { createStripeRoutes } from "./routes/index.js";
|
|
37
|
+
// Utils
|
|
38
|
+
import { createStripeGetter } from "./utils/loadStripe.js";
|
|
39
|
+
/**
|
|
40
|
+
* Helper function to define a plugin with validation
|
|
41
|
+
*/
|
|
42
|
+
function definePlugin(definition) {
|
|
43
|
+
if (!definition.meta?.name) {
|
|
44
|
+
throw new Error("Plugin must have a name");
|
|
45
|
+
}
|
|
46
|
+
if (!definition.meta?.version) {
|
|
47
|
+
throw new Error("Plugin must have a version");
|
|
48
|
+
}
|
|
49
|
+
if (!definition.meta?.type) {
|
|
50
|
+
throw new Error("Plugin must have a type");
|
|
51
|
+
}
|
|
52
|
+
return definition;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates the Stripe plugin
|
|
56
|
+
*
|
|
57
|
+
* @param config - Plugin configuration
|
|
58
|
+
* @returns The plugin definition
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const plugin = stripePlugin({
|
|
63
|
+
* secretKey: 'sk_test_...',
|
|
64
|
+
* webhookSecret: 'whsec_...',
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function stripePlugin(config) {
|
|
69
|
+
// Validate required configuration
|
|
70
|
+
if (!config.secretKey) {
|
|
71
|
+
throw new Error("Stripe plugin requires 'secretKey' configuration");
|
|
72
|
+
}
|
|
73
|
+
if (!config.webhookSecret) {
|
|
74
|
+
throw new Error("Stripe plugin requires 'webhookSecret' configuration");
|
|
75
|
+
}
|
|
76
|
+
// Create lazy Stripe instance getter
|
|
77
|
+
const getStripe = createStripeGetter(config);
|
|
78
|
+
return definePlugin({
|
|
79
|
+
meta: {
|
|
80
|
+
name: "baasix-plugin-stripe",
|
|
81
|
+
version: "1.0.0",
|
|
82
|
+
type: "payment",
|
|
83
|
+
description: "Stripe payment integration with one-time and subscription support",
|
|
84
|
+
},
|
|
85
|
+
// Database schemas
|
|
86
|
+
schemas: stripeSchemas,
|
|
87
|
+
// API routes
|
|
88
|
+
routes: createStripeRoutes(config, getStripe),
|
|
89
|
+
// Services
|
|
90
|
+
services: [
|
|
91
|
+
{
|
|
92
|
+
name: "stripeService",
|
|
93
|
+
factory: (context) => {
|
|
94
|
+
return createStripeService(getStripe, config, context);
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
// Lifecycle: Initialize
|
|
99
|
+
onInit: async (context) => {
|
|
100
|
+
console.log("[Stripe Plugin] Initializing...");
|
|
101
|
+
// Pre-load Stripe SDK to catch errors early
|
|
102
|
+
try {
|
|
103
|
+
await getStripe();
|
|
104
|
+
console.log("[Stripe Plugin] Stripe SDK loaded successfully");
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
console.error("[Stripe Plugin] Failed to load Stripe SDK:", error);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
// Optionally sync products on startup
|
|
111
|
+
if (config.syncProductsOnStartup) {
|
|
112
|
+
try {
|
|
113
|
+
const stripeService = context.services.stripeService;
|
|
114
|
+
await stripeService.syncProducts();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
console.error("[Stripe Plugin] Failed to sync products on startup:", error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
// Lifecycle: Ready
|
|
122
|
+
onReady: async () => {
|
|
123
|
+
console.log("[Stripe Plugin] Ready!");
|
|
124
|
+
console.log("[Stripe Plugin] Webhook URL: POST /payments/stripe/webhook");
|
|
125
|
+
console.log("[Stripe Plugin] Available endpoints:");
|
|
126
|
+
console.log(" - POST /payments/stripe/checkout (one-time payment)");
|
|
127
|
+
console.log(" - POST /payments/stripe/subscribe (subscription)");
|
|
128
|
+
console.log(" - PATCH /payments/stripe/subscription/:id (manage subscription)");
|
|
129
|
+
console.log(" - POST /payments/stripe/portal (billing portal)");
|
|
130
|
+
console.log(" - GET /payments/stripe/payments (user payments)");
|
|
131
|
+
console.log(" - GET /payments/stripe/subscriptions (user subscriptions)");
|
|
132
|
+
console.log(" - GET /payments/stripe/products (available products)");
|
|
133
|
+
console.log(" - POST /payments/stripe/sync-products (admin: sync from Stripe)");
|
|
134
|
+
},
|
|
135
|
+
// Lifecycle: Shutdown
|
|
136
|
+
onShutdown: async () => {
|
|
137
|
+
console.log("[Stripe Plugin] Shutting down...");
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// Re-export schemas for customization
|
|
142
|
+
export { stripeSchemas } from "./schemas/index.js";
|
|
143
|
+
// Re-export service and singleton getter
|
|
144
|
+
export { createStripeService, getStripeService, isStripeServiceInitialized, } from "./services/stripeService.js";
|
|
145
|
+
// Re-export routes for customization
|
|
146
|
+
export { createStripeRoutes, checkoutRoute, subscribeRoute, manageSubscriptionRoute, getUserSubscriptionsRoute, portalRoute, getPaymentsRoute, getProductsRoute, syncProductsRoute, createWebhookRoute, } from "./routes/index.js";
|
|
147
|
+
// Re-export utilities
|
|
148
|
+
export { loadStripeSDK, createStripeGetter } from "./utils/loadStripe.js";
|
|
149
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|