@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.
Files changed (46) hide show
  1. package/README.md +473 -0
  2. package/dist/index.d.ts +52 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +149 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/routes/checkout.d.ts +24 -0
  7. package/dist/routes/checkout.d.ts.map +1 -0
  8. package/dist/routes/checkout.js +58 -0
  9. package/dist/routes/checkout.js.map +1 -0
  10. package/dist/routes/index.d.ts +26 -0
  11. package/dist/routes/index.d.ts.map +1 -0
  12. package/dist/routes/index.js +44 -0
  13. package/dist/routes/index.js.map +1 -0
  14. package/dist/routes/portal.d.ts +31 -0
  15. package/dist/routes/portal.d.ts.map +1 -0
  16. package/dist/routes/portal.js +78 -0
  17. package/dist/routes/portal.js.map +1 -0
  18. package/dist/routes/products.d.ts +26 -0
  19. package/dist/routes/products.d.ts.map +1 -0
  20. package/dist/routes/products.js +65 -0
  21. package/dist/routes/products.js.map +1 -0
  22. package/dist/routes/subscription.d.ts +32 -0
  23. package/dist/routes/subscription.d.ts.map +1 -0
  24. package/dist/routes/subscription.js +121 -0
  25. package/dist/routes/subscription.js.map +1 -0
  26. package/dist/routes/webhook.d.ts +15 -0
  27. package/dist/routes/webhook.d.ts.map +1 -0
  28. package/dist/routes/webhook.js +52 -0
  29. package/dist/routes/webhook.js.map +1 -0
  30. package/dist/schemas/index.d.ts +31 -0
  31. package/dist/schemas/index.d.ts.map +1 -0
  32. package/dist/schemas/index.js +140 -0
  33. package/dist/schemas/index.js.map +1 -0
  34. package/dist/services/stripeService.d.ts +58 -0
  35. package/dist/services/stripeService.d.ts.map +1 -0
  36. package/dist/services/stripeService.js +431 -0
  37. package/dist/services/stripeService.js.map +1 -0
  38. package/dist/types.d.ts +236 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +9 -0
  41. package/dist/types.js.map +1 -0
  42. package/dist/utils/loadStripe.d.ts +28 -0
  43. package/dist/utils/loadStripe.d.ts.map +1 -0
  44. package/dist/utils/loadStripe.js +59 -0
  45. package/dist/utils/loadStripe.js.map +1 -0
  46. package/package.json +59 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Stripe Plugin Database Schemas
3
+ *
4
+ * Defines the database tables created by the Stripe plugin:
5
+ * - baasix_StripeCustomer: Maps users to Stripe customers
6
+ * - baasix_StripePayment: Records one-time payments
7
+ * - baasix_StripeSubscription: Tracks subscriptions
8
+ * - baasix_StripeProduct: Caches products/prices from Stripe
9
+ */
10
+ import type { PluginSchemaDefinition } from "../types.js";
11
+ /**
12
+ * Stripe Customer schema - maps Baasix users to Stripe customers
13
+ */
14
+ export declare const stripeCustomerSchema: PluginSchemaDefinition;
15
+ /**
16
+ * Stripe Payment schema - records one-time payments
17
+ */
18
+ export declare const stripePaymentSchema: PluginSchemaDefinition;
19
+ /**
20
+ * Stripe Subscription schema - tracks active and past subscriptions
21
+ */
22
+ export declare const stripeSubscriptionSchema: PluginSchemaDefinition;
23
+ /**
24
+ * Stripe Product schema - caches products and prices from Stripe
25
+ */
26
+ export declare const stripeProductSchema: PluginSchemaDefinition;
27
+ /**
28
+ * All Stripe plugin schemas
29
+ */
30
+ export declare const stripeSchemas: PluginSchemaDefinition[];
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,sBAuBlC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,sBA2BjC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,EAAE,sBA4BtC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,sBA2BjC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,sBAAsB,EAKjD,CAAC"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Stripe Plugin Database Schemas
3
+ *
4
+ * Defines the database tables created by the Stripe plugin:
5
+ * - baasix_StripeCustomer: Maps users to Stripe customers
6
+ * - baasix_StripePayment: Records one-time payments
7
+ * - baasix_StripeSubscription: Tracks subscriptions
8
+ * - baasix_StripeProduct: Caches products/prices from Stripe
9
+ */
10
+ /**
11
+ * Stripe Customer schema - maps Baasix users to Stripe customers
12
+ */
13
+ export const stripeCustomerSchema = {
14
+ collectionName: "baasix_StripeCustomer",
15
+ schema: {
16
+ name: "StripeCustomer",
17
+ timestamps: true,
18
+ fields: {
19
+ id: { type: "UUID", primaryKey: true, defaultValue: { type: "UUIDV4" } },
20
+ user_Id: { type: "UUID", allowNull: false },
21
+ stripeCustomerId: { type: "String", allowNull: false },
22
+ email: { type: "String", allowNull: true },
23
+ metadata: { type: "JSON", allowNull: true },
24
+ user: {
25
+ relType: "BelongsTo",
26
+ target: "baasix_User",
27
+ foreignKey: "user_Id",
28
+ as: "user",
29
+ },
30
+ },
31
+ indexes: [
32
+ { fields: ["stripeCustomerId"], unique: true },
33
+ { fields: ["user_Id"], unique: true },
34
+ ],
35
+ },
36
+ };
37
+ /**
38
+ * Stripe Payment schema - records one-time payments
39
+ */
40
+ export const stripePaymentSchema = {
41
+ collectionName: "baasix_StripePayment",
42
+ schema: {
43
+ name: "StripePayment",
44
+ timestamps: true,
45
+ fields: {
46
+ id: { type: "UUID", primaryKey: true, defaultValue: { type: "UUIDV4" } },
47
+ customer_Id: { type: "UUID", allowNull: false },
48
+ stripePaymentIntentId: { type: "String", allowNull: false },
49
+ stripeCheckoutSessionId: { type: "String", allowNull: true },
50
+ amount: { type: "Integer", allowNull: false },
51
+ currency: { type: "String", defaultValue: "usd" },
52
+ status: {
53
+ type: "ENUM",
54
+ values: ["pending", "processing", "succeeded", "failed", "canceled", "refunded"],
55
+ defaultValue: "pending",
56
+ },
57
+ metadata: { type: "JSON", allowNull: true },
58
+ customer: {
59
+ relType: "BelongsTo",
60
+ target: "baasix_StripeCustomer",
61
+ foreignKey: "customer_Id",
62
+ as: "customer",
63
+ },
64
+ },
65
+ indexes: [{ fields: ["stripePaymentIntentId"], unique: true }],
66
+ },
67
+ };
68
+ /**
69
+ * Stripe Subscription schema - tracks active and past subscriptions
70
+ */
71
+ export const stripeSubscriptionSchema = {
72
+ collectionName: "baasix_StripeSubscription",
73
+ schema: {
74
+ name: "StripeSubscription",
75
+ timestamps: true,
76
+ fields: {
77
+ id: { type: "UUID", primaryKey: true, defaultValue: { type: "UUIDV4" } },
78
+ customer_Id: { type: "UUID", allowNull: false },
79
+ stripeSubscriptionId: { type: "String", allowNull: false },
80
+ stripePriceId: { type: "String", allowNull: false },
81
+ status: {
82
+ type: "ENUM",
83
+ values: ["active", "canceled", "incomplete", "past_due", "trialing", "unpaid", "incomplete_expired", "paused"],
84
+ defaultValue: "incomplete",
85
+ },
86
+ currentPeriodStart: { type: "DateTime", allowNull: true },
87
+ currentPeriodEnd: { type: "DateTime", allowNull: true },
88
+ cancelAtPeriodEnd: { type: "Boolean", defaultValue: false },
89
+ metadata: { type: "JSON", allowNull: true },
90
+ customer: {
91
+ relType: "BelongsTo",
92
+ target: "baasix_StripeCustomer",
93
+ foreignKey: "customer_Id",
94
+ as: "customer",
95
+ },
96
+ },
97
+ indexes: [{ fields: ["stripeSubscriptionId"], unique: true }],
98
+ },
99
+ };
100
+ /**
101
+ * Stripe Product schema - caches products and prices from Stripe
102
+ */
103
+ export const stripeProductSchema = {
104
+ collectionName: "baasix_StripeProduct",
105
+ schema: {
106
+ name: "StripeProduct",
107
+ timestamps: true,
108
+ fields: {
109
+ id: { type: "UUID", primaryKey: true, defaultValue: { type: "UUIDV4" } },
110
+ stripeProductId: { type: "String", allowNull: false },
111
+ stripePriceId: { type: "String", allowNull: false },
112
+ name: { type: "String", allowNull: false },
113
+ description: { type: "Text", allowNull: true },
114
+ amount: { type: "Integer", allowNull: true },
115
+ currency: { type: "String", defaultValue: "usd" },
116
+ interval: {
117
+ type: "ENUM",
118
+ values: ["one_time", "day", "week", "month", "year"],
119
+ defaultValue: "one_time",
120
+ },
121
+ intervalCount: { type: "Integer", defaultValue: 1 },
122
+ active: { type: "Boolean", defaultValue: true },
123
+ metadata: { type: "JSON", allowNull: true },
124
+ },
125
+ indexes: [
126
+ { fields: ["stripeProductId"] },
127
+ { fields: ["stripePriceId"], unique: true },
128
+ ],
129
+ },
130
+ };
131
+ /**
132
+ * All Stripe plugin schemas
133
+ */
134
+ export const stripeSchemas = [
135
+ stripeCustomerSchema,
136
+ stripePaymentSchema,
137
+ stripeSubscriptionSchema,
138
+ stripeProductSchema,
139
+ ];
140
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Stripe Service Implementation
3
+ *
4
+ * Provides all Stripe-related business logic including:
5
+ * - Customer management
6
+ * - Payment processing
7
+ * - Subscription management
8
+ * - Webhook handling
9
+ * - Product synchronization
10
+ *
11
+ * This service is available as a singleton after plugin initialization.
12
+ * You can access it via:
13
+ * - `getStripeService()` - from anywhere in your code
14
+ * - `context.services.stripeService` - in plugin routes/hooks
15
+ */
16
+ import type { PluginContext, StripePluginConfig, StripeServiceInterface } from "../types.js";
17
+ declare global {
18
+ var __baasix_stripeService: StripeServiceInterface | undefined;
19
+ var __baasix_stripeServiceInitialized: boolean | undefined;
20
+ }
21
+ /**
22
+ * Get the Stripe service singleton instance
23
+ *
24
+ * @throws Error if the service hasn't been initialized yet
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * import { getStripeService } from '@baasix/plugin-stripe';
29
+ *
30
+ * // In an endpoint or hook
31
+ * const stripeService = getStripeService();
32
+ * const customer = await stripeService.getOrCreateCustomer(userId);
33
+ * ```
34
+ */
35
+ export declare function getStripeService(): StripeServiceInterface;
36
+ /**
37
+ * Check if the Stripe service has been initialized
38
+ */
39
+ export declare function isStripeServiceInitialized(): boolean;
40
+ /**
41
+ * Initialize the Stripe service singleton (called internally by the plugin)
42
+ * @internal
43
+ */
44
+ export declare function initializeStripeService(service: StripeServiceInterface): void;
45
+ /**
46
+ * Reset the Stripe service singleton (for testing)
47
+ * @internal
48
+ */
49
+ export declare function resetStripeService(): void;
50
+ /**
51
+ * Creates the Stripe service instance
52
+ *
53
+ * @param getStripe - Function to get the Stripe SDK instance
54
+ * @param config - Plugin configuration
55
+ * @param context - Plugin context with database access
56
+ */
57
+ export declare function createStripeService(getStripe: () => Promise<any>, config: StripePluginConfig, context: PluginContext): StripeServiceInterface;
58
+ //# sourceMappingURL=stripeService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripeService.d.ts","sourceRoot":"","sources":["../../src/services/stripeService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,kBAAkB,EAClB,sBAAsB,EAKvB,MAAM,aAAa,CAAC;AAOrB,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,sBAAsB,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAE/D,IAAI,iCAAiC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC5D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,IAAI,sBAAsB,CAOzD;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAG7E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAMD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,EAC7B,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,aAAa,GACrB,sBAAsB,CA8bxB"}
@@ -0,0 +1,431 @@
1
+ /**
2
+ * Stripe Service Implementation
3
+ *
4
+ * Provides all Stripe-related business logic including:
5
+ * - Customer management
6
+ * - Payment processing
7
+ * - Subscription management
8
+ * - Webhook handling
9
+ * - Product synchronization
10
+ *
11
+ * This service is available as a singleton after plugin initialization.
12
+ * You can access it via:
13
+ * - `getStripeService()` - from anywhere in your code
14
+ * - `context.services.stripeService` - in plugin routes/hooks
15
+ */
16
+ /**
17
+ * Get the Stripe service singleton instance
18
+ *
19
+ * @throws Error if the service hasn't been initialized yet
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import { getStripeService } from '@baasix/plugin-stripe';
24
+ *
25
+ * // In an endpoint or hook
26
+ * const stripeService = getStripeService();
27
+ * const customer = await stripeService.getOrCreateCustomer(userId);
28
+ * ```
29
+ */
30
+ export function getStripeService() {
31
+ if (!globalThis.__baasix_stripeService) {
32
+ throw new Error("Stripe service not initialized. Make sure the Stripe plugin is loaded in startServer().");
33
+ }
34
+ return globalThis.__baasix_stripeService;
35
+ }
36
+ /**
37
+ * Check if the Stripe service has been initialized
38
+ */
39
+ export function isStripeServiceInitialized() {
40
+ return globalThis.__baasix_stripeServiceInitialized === true;
41
+ }
42
+ /**
43
+ * Initialize the Stripe service singleton (called internally by the plugin)
44
+ * @internal
45
+ */
46
+ export function initializeStripeService(service) {
47
+ globalThis.__baasix_stripeService = service;
48
+ globalThis.__baasix_stripeServiceInitialized = true;
49
+ }
50
+ /**
51
+ * Reset the Stripe service singleton (for testing)
52
+ * @internal
53
+ */
54
+ export function resetStripeService() {
55
+ globalThis.__baasix_stripeService = undefined;
56
+ globalThis.__baasix_stripeServiceInitialized = false;
57
+ }
58
+ // ============================================================================
59
+ // Service Factory
60
+ // ============================================================================
61
+ /**
62
+ * Creates the Stripe service instance
63
+ *
64
+ * @param getStripe - Function to get the Stripe SDK instance
65
+ * @param config - Plugin configuration
66
+ * @param context - Plugin context with database access
67
+ */
68
+ export function createStripeService(getStripe, config, context) {
69
+ // Cache the stripe instance after first call
70
+ let cachedStripe = null;
71
+ const stripe = async () => {
72
+ if (!cachedStripe) {
73
+ cachedStripe = await getStripe();
74
+ }
75
+ return cachedStripe;
76
+ };
77
+ const service = {
78
+ /**
79
+ * Get the Stripe SDK instance (synchronous, returns cached value)
80
+ */
81
+ get stripe() {
82
+ return cachedStripe;
83
+ },
84
+ /**
85
+ * Get or create a Stripe customer for a user
86
+ */
87
+ async getOrCreateCustomer(userId) {
88
+ const stripeClient = await stripe();
89
+ const ItemsService = context.ItemsService;
90
+ const customerService = new ItemsService("baasix_StripeCustomer");
91
+ // Check for existing customer
92
+ const existing = await customerService.readByQuery({
93
+ filter: { user_Id: { _eq: userId } },
94
+ });
95
+ if (existing.length > 0) {
96
+ return existing[0];
97
+ }
98
+ // Get user details
99
+ const userService = new ItemsService("baasix_User");
100
+ const user = await userService.readOne(userId);
101
+ if (!user) {
102
+ throw new Error("User not found");
103
+ }
104
+ // Create Stripe customer
105
+ const stripeCustomer = await stripeClient.customers.create({
106
+ email: user.email,
107
+ name: [user.firstName, user.lastName].filter(Boolean).join(" ") || undefined,
108
+ metadata: { userId },
109
+ });
110
+ // Save mapping to database
111
+ return customerService.createOne({
112
+ user_Id: userId,
113
+ stripeCustomerId: stripeCustomer.id,
114
+ email: user.email,
115
+ });
116
+ },
117
+ /**
118
+ * Get a Stripe customer by user ID
119
+ */
120
+ async getCustomer(userId) {
121
+ const ItemsService = context.ItemsService;
122
+ const customerService = new ItemsService("baasix_StripeCustomer");
123
+ const results = await customerService.readByQuery({
124
+ filter: { user_Id: { _eq: userId } },
125
+ });
126
+ return results[0] || null;
127
+ },
128
+ /**
129
+ * Handle incoming Stripe webhook events
130
+ */
131
+ async handleWebhook(event) {
132
+ const stripeClient = await stripe();
133
+ const ItemsService = context.ItemsService;
134
+ switch (event.type) {
135
+ case "checkout.session.completed": {
136
+ const session = event.data.object;
137
+ if (session.mode === "payment" && session.payment_intent) {
138
+ // Record the completed payment
139
+ const paymentService = new ItemsService("baasix_StripePayment");
140
+ const customerService = new ItemsService("baasix_StripeCustomer");
141
+ const customers = await customerService.readByQuery({
142
+ filter: { stripeCustomerId: { _eq: session.customer } },
143
+ });
144
+ if (customers.length > 0) {
145
+ await paymentService.createOne({
146
+ customer_Id: customers[0].id,
147
+ stripePaymentIntentId: session.payment_intent,
148
+ stripeCheckoutSessionId: session.id,
149
+ amount: session.amount_total || 0,
150
+ currency: session.currency || config.currency || "usd",
151
+ status: "succeeded",
152
+ metadata: session.metadata,
153
+ });
154
+ }
155
+ }
156
+ break;
157
+ }
158
+ case "payment_intent.succeeded": {
159
+ const paymentIntent = event.data.object;
160
+ const paymentService = new ItemsService("baasix_StripePayment");
161
+ // Update existing payment record if it exists
162
+ const existing = await paymentService.readByQuery({
163
+ filter: { stripePaymentIntentId: { _eq: paymentIntent.id } },
164
+ });
165
+ if (existing.length > 0) {
166
+ await paymentService.updateOne(existing[0].id, { status: "succeeded" });
167
+ }
168
+ break;
169
+ }
170
+ case "payment_intent.payment_failed": {
171
+ const paymentIntent = event.data.object;
172
+ const paymentService = new ItemsService("baasix_StripePayment");
173
+ const existing = await paymentService.readByQuery({
174
+ filter: { stripePaymentIntentId: { _eq: paymentIntent.id } },
175
+ });
176
+ if (existing.length > 0) {
177
+ await paymentService.updateOne(existing[0].id, { status: "failed" });
178
+ }
179
+ break;
180
+ }
181
+ case "customer.subscription.created":
182
+ case "customer.subscription.updated": {
183
+ const subscription = event.data.object;
184
+ await this.syncSubscription(subscription);
185
+ break;
186
+ }
187
+ case "customer.subscription.deleted": {
188
+ const subscription = event.data.object;
189
+ const subService = new ItemsService("baasix_StripeSubscription");
190
+ const existing = await subService.readByQuery({
191
+ filter: { stripeSubscriptionId: { _eq: subscription.id } },
192
+ });
193
+ if (existing.length > 0) {
194
+ await subService.updateOne(existing[0].id, { status: "canceled" });
195
+ }
196
+ break;
197
+ }
198
+ case "invoice.payment_succeeded": {
199
+ // Handle subscription renewal
200
+ const invoice = event.data.object;
201
+ if (invoice.subscription) {
202
+ const subscription = await stripeClient.subscriptions.retrieve(invoice.subscription);
203
+ await this.syncSubscription(subscription);
204
+ }
205
+ break;
206
+ }
207
+ case "invoice.payment_failed": {
208
+ // Handle failed subscription payment
209
+ const invoice = event.data.object;
210
+ if (invoice.subscription) {
211
+ const subService = new ItemsService("baasix_StripeSubscription");
212
+ const existing = await subService.readByQuery({
213
+ filter: { stripeSubscriptionId: { _eq: invoice.subscription } },
214
+ });
215
+ if (existing.length > 0) {
216
+ await subService.updateOne(existing[0].id, { status: "past_due" });
217
+ }
218
+ }
219
+ break;
220
+ }
221
+ default:
222
+ console.log(`[Stripe Plugin] Unhandled webhook event: ${event.type}`);
223
+ }
224
+ },
225
+ /**
226
+ * Sync a subscription from Stripe to the database
227
+ */
228
+ async syncSubscription(subscription) {
229
+ const ItemsService = context.ItemsService;
230
+ const subService = new ItemsService("baasix_StripeSubscription");
231
+ const customerService = new ItemsService("baasix_StripeCustomer");
232
+ // Find the customer
233
+ const customers = await customerService.readByQuery({
234
+ filter: { stripeCustomerId: { _eq: subscription.customer } },
235
+ });
236
+ if (customers.length === 0) {
237
+ console.warn(`[Stripe Plugin] Customer not found for subscription: ${subscription.id}`);
238
+ return;
239
+ }
240
+ const priceId = subscription.items.data[0]?.price?.id;
241
+ if (!priceId) {
242
+ console.warn(`[Stripe Plugin] No price ID found for subscription: ${subscription.id}`);
243
+ return;
244
+ }
245
+ const data = {
246
+ customer_Id: customers[0].id,
247
+ stripeSubscriptionId: subscription.id,
248
+ stripePriceId: priceId,
249
+ status: subscription.status,
250
+ currentPeriodStart: new Date(subscription.current_period_start * 1000),
251
+ currentPeriodEnd: new Date(subscription.current_period_end * 1000),
252
+ cancelAtPeriodEnd: subscription.cancel_at_period_end,
253
+ metadata: subscription.metadata,
254
+ };
255
+ // Upsert the subscription
256
+ const existing = await subService.readByQuery({
257
+ filter: { stripeSubscriptionId: { _eq: subscription.id } },
258
+ });
259
+ if (existing.length > 0) {
260
+ await subService.updateOne(existing[0].id, data);
261
+ }
262
+ else {
263
+ await subService.createOne(data);
264
+ }
265
+ },
266
+ /**
267
+ * Manage a subscription (cancel or resume)
268
+ */
269
+ async manageSubscription(userId, subscriptionId, action) {
270
+ const stripeClient = await stripe();
271
+ const ItemsService = context.ItemsService;
272
+ const subService = new ItemsService("baasix_StripeSubscription");
273
+ // Get customer to verify ownership
274
+ const customer = await this.getCustomer(userId);
275
+ if (!customer) {
276
+ throw new Error("Customer not found");
277
+ }
278
+ // Verify subscription belongs to user
279
+ const subs = await subService.readByQuery({
280
+ filter: {
281
+ id: { _eq: subscriptionId },
282
+ customer_Id: { _eq: customer.id },
283
+ },
284
+ });
285
+ if (subs.length === 0) {
286
+ throw new Error("Subscription not found");
287
+ }
288
+ const stripeSubId = subs[0].stripeSubscriptionId;
289
+ if (action === "cancel") {
290
+ await stripeClient.subscriptions.update(stripeSubId, { cancel_at_period_end: true });
291
+ await subService.updateOne(subscriptionId, { cancelAtPeriodEnd: true });
292
+ }
293
+ else if (action === "resume") {
294
+ await stripeClient.subscriptions.update(stripeSubId, { cancel_at_period_end: false });
295
+ await subService.updateOne(subscriptionId, { cancelAtPeriodEnd: false });
296
+ }
297
+ return { success: true };
298
+ },
299
+ /**
300
+ * Get a user's payment history
301
+ */
302
+ async getUserPayments(userId) {
303
+ const customer = await this.getCustomer(userId);
304
+ if (!customer)
305
+ return [];
306
+ const ItemsService = context.ItemsService;
307
+ const paymentService = new ItemsService("baasix_StripePayment");
308
+ return paymentService.readByQuery({
309
+ filter: { customer_Id: { _eq: customer.id } },
310
+ sort: ["-createdAt"],
311
+ });
312
+ },
313
+ /**
314
+ * Get a user's subscriptions
315
+ */
316
+ async getUserSubscriptions(userId) {
317
+ const customer = await this.getCustomer(userId);
318
+ if (!customer)
319
+ return [];
320
+ const ItemsService = context.ItemsService;
321
+ const subService = new ItemsService("baasix_StripeSubscription");
322
+ return subService.readByQuery({
323
+ filter: { customer_Id: { _eq: customer.id } },
324
+ sort: ["-createdAt"],
325
+ });
326
+ },
327
+ /**
328
+ * Get all active products
329
+ */
330
+ async getProducts() {
331
+ const ItemsService = context.ItemsService;
332
+ const productService = new ItemsService("baasix_StripeProduct");
333
+ return productService.readByQuery({
334
+ filter: { active: { _eq: true } },
335
+ sort: ["name"],
336
+ });
337
+ },
338
+ /**
339
+ * Sync products and prices from Stripe
340
+ */
341
+ async syncProducts() {
342
+ const stripeClient = await stripe();
343
+ const products = await stripeClient.products.list({ active: true, limit: 100 });
344
+ const prices = await stripeClient.prices.list({ active: true, limit: 100 });
345
+ const ItemsService = context.ItemsService;
346
+ const productService = new ItemsService("baasix_StripeProduct");
347
+ for (const price of prices.data) {
348
+ const product = products.data.find((p) => p.id === price.product);
349
+ if (!product)
350
+ continue;
351
+ const data = {
352
+ stripeProductId: product.id,
353
+ stripePriceId: price.id,
354
+ name: product.name,
355
+ description: product.description || null,
356
+ amount: price.unit_amount,
357
+ currency: price.currency,
358
+ interval: price.recurring?.interval || "one_time",
359
+ intervalCount: price.recurring?.interval_count || 1,
360
+ active: price.active && product.active,
361
+ metadata: product.metadata,
362
+ };
363
+ // Upsert by price ID
364
+ const existing = await productService.readByQuery({
365
+ filter: { stripePriceId: { _eq: price.id } },
366
+ });
367
+ if (existing.length > 0) {
368
+ await productService.updateOne(existing[0].id, data);
369
+ }
370
+ else {
371
+ await productService.createOne(data);
372
+ }
373
+ }
374
+ console.log(`[Stripe Plugin] Synced ${prices.data.length} products/prices`);
375
+ },
376
+ /**
377
+ * Create a checkout session for one-time payment
378
+ */
379
+ async createCheckoutSession(options) {
380
+ const stripeClient = await stripe();
381
+ const customer = await this.getOrCreateCustomer(options.userId);
382
+ const session = await stripeClient.checkout.sessions.create({
383
+ customer: customer.stripeCustomerId,
384
+ mode: "payment",
385
+ line_items: [{ price: options.priceId, quantity: options.quantity || 1 }],
386
+ success_url: options.successUrl,
387
+ cancel_url: options.cancelUrl,
388
+ metadata: { userId: options.userId, ...options.metadata },
389
+ });
390
+ return { sessionId: session.id, url: session.url };
391
+ },
392
+ /**
393
+ * Create a checkout session for subscription
394
+ */
395
+ async createSubscriptionCheckout(options) {
396
+ const stripeClient = await stripe();
397
+ const customer = await this.getOrCreateCustomer(options.userId);
398
+ const session = await stripeClient.checkout.sessions.create({
399
+ customer: customer.stripeCustomerId,
400
+ mode: "subscription",
401
+ line_items: [{ price: options.priceId, quantity: 1 }],
402
+ success_url: options.successUrl,
403
+ cancel_url: options.cancelUrl,
404
+ subscription_data: options.trialDays
405
+ ? { trial_period_days: options.trialDays }
406
+ : undefined,
407
+ metadata: { userId: options.userId, ...options.metadata },
408
+ });
409
+ return { sessionId: session.id, url: session.url };
410
+ },
411
+ /**
412
+ * Create a billing portal session for customer self-service
413
+ */
414
+ async createPortalSession(userId, returnUrl) {
415
+ const stripeClient = await stripe();
416
+ const customer = await this.getCustomer(userId);
417
+ if (!customer) {
418
+ throw new Error("Customer not found");
419
+ }
420
+ const session = await stripeClient.billingPortal.sessions.create({
421
+ customer: customer.stripeCustomerId,
422
+ return_url: returnUrl,
423
+ });
424
+ return { url: session.url };
425
+ },
426
+ };
427
+ // Initialize the singleton
428
+ initializeStripeService(service);
429
+ return service;
430
+ }
431
+ //# sourceMappingURL=stripeService.js.map