@reeboot/strapi-payment-plugin 0.0.1 → 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.
Files changed (84) hide show
  1. package/README.md +378 -119
  2. package/dist/_chunks/Analytics-C2GKvPDX.js +355 -0
  3. package/dist/_chunks/Analytics-Chydh3dg.mjs +355 -0
  4. package/dist/_chunks/App-BCbEuN6h.mjs +68 -0
  5. package/dist/_chunks/App-E2k2mo5p.js +70 -0
  6. package/dist/_chunks/Customers-B8BlQ1BO.mjs +273 -0
  7. package/dist/_chunks/Customers-D0O5ET5C.js +273 -0
  8. package/dist/_chunks/Dashboard-CuqclLak.js +180 -0
  9. package/dist/_chunks/Dashboard-D8-ifJqB.mjs +180 -0
  10. package/dist/_chunks/Orders-5WGSzPGo.js +308 -0
  11. package/dist/_chunks/Orders-ClJJaQrX.mjs +308 -0
  12. package/dist/_chunks/Payments-CIMyOC3n.js +489 -0
  13. package/dist/_chunks/Payments-CRnRBf48.mjs +489 -0
  14. package/dist/_chunks/Settings-BXMLePI_.js +357 -0
  15. package/dist/_chunks/Settings-TeAvV4RH.mjs +357 -0
  16. package/dist/_chunks/en-BJocyOVu.mjs +240 -0
  17. package/dist/_chunks/en-BkVAf_R4.js +240 -0
  18. package/dist/_chunks/index-BTk7a2T5.js +66 -0
  19. package/dist/_chunks/index-Bz8V_5zH.mjs +67 -0
  20. package/dist/admin/index.js +2 -63
  21. package/dist/admin/index.mjs +2 -63
  22. package/dist/admin/src/components/AnalyticsChart.d.ts +19 -0
  23. package/dist/admin/src/components/CustomerList.d.ts +21 -0
  24. package/dist/admin/src/components/OrderList.d.ts +27 -0
  25. package/dist/admin/src/components/PaymentCard.d.ts +39 -0
  26. package/dist/admin/src/components/PaymentList.d.ts +19 -0
  27. package/dist/admin/src/components/RefundModal.d.ts +15 -0
  28. package/dist/admin/src/pages/Analytics.d.ts +2 -0
  29. package/dist/admin/src/pages/Customers.d.ts +2 -0
  30. package/dist/admin/src/pages/Dashboard.d.ts +2 -0
  31. package/dist/admin/src/pages/HomePage.d.ts +1 -1
  32. package/dist/admin/src/pages/Orders.d.ts +2 -0
  33. package/dist/admin/src/pages/Payments.d.ts +2 -0
  34. package/dist/admin/src/pages/Settings.d.ts +2 -0
  35. package/dist/admin/src/pluginId.d.ts +1 -1
  36. package/dist/server/index.js +1746 -984
  37. package/dist/server/index.mjs +1748 -986
  38. package/dist/server/src/bootstrap.d.ts +5 -11
  39. package/dist/server/src/config/index.d.ts +0 -10
  40. package/dist/server/src/content-types/customer/index.d.ts +69 -0
  41. package/dist/server/src/content-types/index.d.ts +123 -39
  42. package/dist/server/src/content-types/{product.d.ts → order/index.d.ts} +26 -19
  43. package/dist/server/src/content-types/{subscription.d.ts → payment/index.d.ts} +30 -21
  44. package/dist/server/src/controllers/controller.d.ts +5 -12
  45. package/dist/server/src/controllers/index.d.ts +29 -34
  46. package/dist/server/src/controllers/stripe.d.ts +104 -0
  47. package/dist/server/src/index.d.ts +179 -139
  48. package/dist/server/src/middlewares/index.d.ts +19 -1
  49. package/dist/server/src/policies/index.d.ts +3 -1
  50. package/dist/server/src/routes/{admin-routes.d.ts → admin/index.d.ts} +4 -4
  51. package/dist/server/src/routes/content-api/index.d.ts +21 -0
  52. package/dist/server/src/routes/index.d.ts +11 -16
  53. package/dist/server/src/services/index.d.ts +2 -38
  54. package/dist/server/src/services/{stripeDriver.d.ts → stripe.d.ts} +52 -59
  55. package/dist/server/src/types/index.d.ts +179 -0
  56. package/package.json +20 -25
  57. package/dist/_chunks/App-DD7GyuRr.mjs +0 -1424
  58. package/dist/_chunks/App-KZVBFRwo.js +0 -1424
  59. package/dist/_chunks/en-B4KWt_jN.js +0 -4
  60. package/dist/_chunks/en-Byx4XI2L.mjs +0 -4
  61. package/dist/admin/src/components/Header.d.ts +0 -2
  62. package/dist/admin/src/components/NavigationMenu.d.ts +0 -2
  63. package/dist/admin/src/components/Sidebar.d.ts +0 -2
  64. package/dist/admin/src/components/TransactionDetailsModal.d.ts +0 -18
  65. package/dist/admin/src/components/TransactionList.d.ts +0 -18
  66. package/dist/admin/src/pages/ConfigurationPage.d.ts +0 -2
  67. package/dist/admin/src/pages/DashboardPage.d.ts +0 -2
  68. package/dist/admin/src/pages/ProductsPage.d.ts +0 -2
  69. package/dist/admin/src/pages/SubscriptionsPage.d.ts +0 -2
  70. package/dist/admin/src/pages/TransactionsPage.d.ts +0 -2
  71. package/dist/server/src/controllers/product.d.ts +0 -18
  72. package/dist/server/src/controllers/subscription.d.ts +0 -16
  73. package/dist/server/src/controllers/webhook.d.ts +0 -10
  74. package/dist/server/src/routes/content-api.d.ts +0 -12
  75. package/dist/server/src/routes/product.d.ts +0 -2
  76. package/dist/server/src/routes/refund-routes.d.ts +0 -13
  77. package/dist/server/src/routes/subscription.d.ts +0 -5
  78. package/dist/server/src/routes/webhook.d.ts +0 -15
  79. package/dist/server/src/services/paypalDriver.d.ts +0 -47
  80. package/dist/server/src/services/product.d.ts +0 -7
  81. package/dist/server/src/services/service.d.ts +0 -26
  82. package/dist/server/src/services/subscription.d.ts +0 -9
  83. package/dist/server/src/services/sync.d.ts +0 -13
  84. package/jest.config.js +0 -13
@@ -1,12 +1,8 @@
1
1
  "use strict";
2
2
  const Stripe = require("stripe");
3
- const strapi = require("@strapi/strapi");
4
3
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
5
4
  const Stripe__default = /* @__PURE__ */ _interopDefault(Stripe);
6
- const bootstrap = async ({ strapi: strapi2 }) => {
7
- const syncService = strapi2.service("plugin::strapi-payment-plugin.sync");
8
- syncService?.startPeriodicSync();
9
- console.log("Periodic sync started");
5
+ const bootstrap = ({ strapi: strapi2 }) => {
10
6
  };
11
7
  const destroy = ({ strapi: strapi2 }) => {
12
8
  };
@@ -15,1226 +11,1992 @@ const register = ({ strapi: strapi2 }) => {
15
11
  const config = {
16
12
  default: {},
17
13
  validator() {
14
+ }
15
+ };
16
+ const schema$2 = {
17
+ kind: "collectionType",
18
+ collectionName: "customers",
19
+ info: {
20
+ singularName: "customer",
21
+ pluralName: "customers",
22
+ displayName: "Customer",
23
+ description: "Customer information for payment processing"
24
+ },
25
+ options: {
26
+ draftAndPublish: false
18
27
  },
19
- stripe: {
20
- secretKey: process.env.STRIPE_SECRET_KEY,
21
- publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
22
- webhookSecret: process.env.STRIPE_WEBHOOK_SECRET
28
+ pluginOptions: {
29
+ i18n: {
30
+ localized: true
31
+ }
23
32
  },
24
- paypal: {
25
- clientId: process.env.PAYPAL_CLIENT_ID,
26
- clientSecret: process.env.PAYPAL_CLIENT_SECRET,
27
- webhookId: process.env.PAYPAL_WEBHOOK_ID
33
+ attributes: {
34
+ email: {
35
+ type: "email",
36
+ unique: true,
37
+ required: true,
38
+ configurable: false
39
+ },
40
+ stripe_customer_id: {
41
+ type: "string",
42
+ unique: true,
43
+ required: true,
44
+ configurable: false
45
+ },
46
+ first_name: {
47
+ type: "string",
48
+ required: true,
49
+ configurable: false
50
+ },
51
+ last_name: {
52
+ type: "string",
53
+ required: true,
54
+ configurable: false
55
+ },
56
+ phone: {
57
+ type: "string",
58
+ configurable: false
59
+ },
60
+ address: {
61
+ type: "json",
62
+ configurable: false
63
+ },
64
+ metadata: {
65
+ type: "json",
66
+ configurable: false
67
+ },
68
+ orders: {
69
+ type: "relation",
70
+ relation: "oneToMany",
71
+ target: "plugin::payment-plugin.order",
72
+ mappedBy: "customer"
73
+ },
74
+ payments: {
75
+ type: "relation",
76
+ relation: "oneToMany",
77
+ target: "plugin::payment-plugin.payment",
78
+ mappedBy: "customer"
79
+ }
28
80
  }
29
81
  };
82
+ const customer = { schema: schema$2 };
30
83
  const schema$1 = {
31
- "kind": "collectionType",
32
- "collectionName": "products",
33
- "info": {
34
- "singularName": "product",
35
- "pluralName": "products",
36
- "displayName": "Product",
37
- "description": "A product that can be purchased"
84
+ kind: "collectionType",
85
+ collectionName: "orders",
86
+ info: {
87
+ singularName: "order",
88
+ pluralName: "orders",
89
+ displayName: "Order",
90
+ description: "Customer orders linked to payments"
38
91
  },
39
- "options": {
40
- "draftAndPublish": false
92
+ options: {
93
+ draftAndPublish: false
41
94
  },
42
- "pluginOptions": {
43
- "content-manager": {
44
- "visible": true
45
- },
46
- "content-type-builder": {
47
- "visible": true
95
+ pluginOptions: {
96
+ i18n: {
97
+ localized: true
48
98
  }
49
99
  },
50
- "attributes": {
51
- "name": {
52
- "type": "string",
53
- "required": true
54
- },
55
- "description": {
56
- "type": "text"
57
- },
58
- "price": {
59
- "type": "decimal",
60
- "required": true
100
+ attributes: {
101
+ order_number: {
102
+ type: "string",
103
+ unique: true,
104
+ required: true,
105
+ configurable: false
61
106
  },
62
- "currency": {
63
- "type": "string",
64
- "required": true,
65
- "default": "USD"
107
+ total_amount: {
108
+ type: "integer",
109
+ required: true,
110
+ min: 0,
111
+ configurable: false,
112
+ default: 0
66
113
  },
67
- "isSubscription": {
68
- "type": "boolean",
69
- "default": false
114
+ currency: {
115
+ type: "string",
116
+ required: true,
117
+ default: "usd",
118
+ configurable: false
70
119
  },
71
- "subscriptionInterval": {
72
- "type": "enumeration",
73
- "enum": ["day", "week", "month", "year"],
74
- "default": "month"
120
+ status: {
121
+ type: "enumeration",
122
+ enum: ["pending", "processing", "completed", "failed", "refunded"],
123
+ required: true,
124
+ default: "pending",
125
+ configurable: false
75
126
  },
76
- "stripeProductId": {
77
- "type": "string"
127
+ customer: {
128
+ type: "relation",
129
+ relation: "manyToOne",
130
+ target: "plugin::payment-plugin.customer",
131
+ inversedBy: "orders"
78
132
  },
79
- "paypalProductId": {
80
- "type": "string"
133
+ payments: {
134
+ type: "relation",
135
+ relation: "oneToMany",
136
+ target: "plugin::payment-plugin.payment",
137
+ mappedBy: "order"
81
138
  },
82
- "createdAt": {
83
- "type": "datetime"
139
+ items: {
140
+ type: "json",
141
+ required: true,
142
+ configurable: false
84
143
  },
85
- "updatedAt": {
86
- "type": "datetime"
144
+ metadata: {
145
+ type: "json",
146
+ configurable: false
87
147
  }
88
148
  }
89
149
  };
90
- const product$2 = {
91
- schema: schema$1
92
- };
150
+ const order = { schema: schema$1 };
93
151
  const schema = {
94
- "kind": "collectionType",
95
- "collectionName": "subscriptions",
96
- "info": {
97
- "singularName": "subscription",
98
- "pluralName": "subscriptions",
99
- "displayName": "Subscription",
100
- "description": "A subscription to a product"
152
+ kind: "collectionType",
153
+ collectionName: "payments",
154
+ info: {
155
+ singularName: "payment",
156
+ pluralName: "payments",
157
+ displayName: "Payment",
158
+ description: "Payment records for Stripe payment processing"
101
159
  },
102
- "options": {
103
- "draftAndPublish": false
160
+ options: {
161
+ draftAndPublish: false
104
162
  },
105
- "pluginOptions": {
106
- "content-manager": {
107
- "visible": true
108
- },
109
- "content-type-builder": {
110
- "visible": true
163
+ pluginOptions: {
164
+ i18n: {
165
+ localized: true
111
166
  }
112
167
  },
113
- "attributes": {
114
- "user": {
115
- "type": "relation",
116
- "relation": "morphOne",
117
- "target": "plugin::users-permissions.user"
118
- },
119
- "product": {
120
- "type": "relation",
121
- "relation": "oneToOne",
122
- "target": "plugin::strapi-payment-plugin.product"
168
+ attributes: {
169
+ stripe_payment_intent_id: {
170
+ type: "string",
171
+ unique: true,
172
+ required: true,
173
+ configurable: false
123
174
  },
124
- "status": {
125
- "type": "enumeration",
126
- "enum": ["active", "canceled", "past_due", "unpaid", "trialing"],
127
- "default": "active"
175
+ amount: {
176
+ type: "integer",
177
+ required: true,
178
+ min: 0,
179
+ configurable: false,
180
+ default: 0
128
181
  },
129
- "startDate": {
130
- "type": "date",
131
- "required": true
182
+ currency: {
183
+ type: "string",
184
+ required: true,
185
+ default: "usd",
186
+ configurable: false
132
187
  },
133
- "endDate": {
134
- "type": "date"
188
+ status: {
189
+ type: "enumeration",
190
+ enum: ["pending", "succeeded", "failed", "canceled"],
191
+ required: true,
192
+ default: "pending",
193
+ configurable: false
135
194
  },
136
- "stripeSubscriptionId": {
137
- "type": "string"
195
+ payment_method: {
196
+ type: "string",
197
+ configurable: false
138
198
  },
139
- "paypalSubscriptionId": {
140
- "type": "string"
199
+ metadata: {
200
+ type: "json",
201
+ configurable: false
141
202
  },
142
- "createdAt": {
143
- "type": "datetime"
203
+ customer: {
204
+ type: "relation",
205
+ relation: "manyToOne",
206
+ target: "plugin::payment-plugin.customer",
207
+ inversedBy: "payments"
144
208
  },
145
- "updatedAt": {
146
- "type": "datetime"
209
+ order: {
210
+ type: "relation",
211
+ relation: "manyToOne",
212
+ target: "plugin::payment-plugin.order",
213
+ inversedBy: "payments"
147
214
  }
148
215
  }
149
216
  };
150
- const subscription$2 = {
151
- schema
152
- };
217
+ const payment = { schema };
153
218
  const contentTypes = {
154
- product: product$2,
155
- subscription: subscription$2
219
+ customer,
220
+ order,
221
+ payment
156
222
  };
157
- const stripe = new Stripe__default.default(config.stripe.secretKey, {
158
- apiVersion: "2025-07-30.basil"
223
+ const controller = ({ strapi: strapi2 }) => ({
224
+ index(ctx) {
225
+ ctx.body = strapi2.plugin("payment-plugin").service("service").getWelcomeMessage();
226
+ }
159
227
  });
160
- const stripeDriver = {
161
- customers: stripe.customers,
162
- async initializePayment(amount, currency, orderId, options) {
163
- const paymentIntent = await stripe.paymentIntents.create({
164
- amount,
165
- currency,
166
- metadata: { orderId },
167
- ...options
168
- });
169
- return paymentIntent;
170
- },
171
- async processWebhook(payload, signature) {
172
- const event = stripe.webhooks.constructEvent(
173
- payload,
174
- signature,
175
- config.stripe.webhookSecret
176
- );
177
- return event;
178
- },
179
- async refundPayment(transactionId, amount, options) {
180
- return stripe.refunds.create({
181
- payment_intent: transactionId,
182
- amount,
183
- ...options
184
- });
185
- },
186
- async getStripeProducts() {
187
- const products = await stripe.products.list({
188
- limit: 100,
189
- expand: ["data.default_price"]
190
- });
191
- return products.data;
192
- },
193
- async getStripeProduct(productId) {
194
- const product2 = await stripe.products.retrieve(productId, {
195
- expand: ["default_price"]
196
- });
197
- return product2;
198
- },
199
- async getProductPrice(productId) {
200
- const product2 = await this.getStripeProduct(productId);
201
- if (product2.default_price) {
202
- if (typeof product2.default_price === "string") {
203
- const price = await stripe.prices.retrieve(product2.default_price);
204
- return {
205
- unit_amount: price.unit_amount,
206
- currency: price.currency
207
- };
208
- } else {
209
- return {
210
- unit_amount: product2.default_price.unit_amount,
211
- currency: product2.default_price.currency
212
- };
228
+ const stripeController = {
229
+ /**
230
+ * Create a payment intent
231
+ */
232
+ async createPaymentIntent(ctx) {
233
+ try {
234
+ const { amount, currency, customerId, orderId, metadata = {} } = ctx.request.body;
235
+ if (!amount || !currency) {
236
+ return ctx.badRequest("Amount and currency are required");
213
237
  }
238
+ if (amount <= 0) {
239
+ return ctx.badRequest("Amount must be greater than 0");
240
+ }
241
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
242
+ const paymentIntent = await stripeService2.createPaymentIntent({
243
+ amount,
244
+ currency,
245
+ metadata,
246
+ customerId,
247
+ orderId
248
+ });
249
+ ctx.body = {
250
+ success: true,
251
+ data: {
252
+ paymentIntentId: paymentIntent.id,
253
+ clientSecret: paymentIntent.client_secret,
254
+ status: paymentIntent.status,
255
+ amount: paymentIntent.amount,
256
+ currency: paymentIntent.currency
257
+ }
258
+ };
259
+ } catch (error) {
260
+ strapi.log.error("Failed to create payment intent", { error });
261
+ ctx.internalServerError("Failed to create payment intent");
214
262
  }
215
- return null;
216
- },
217
- async getPaymentDetails(transactionId) {
218
- return stripe.paymentIntents.retrieve(transactionId);
219
263
  },
220
- async createProduct(productData) {
221
- return stripe.products.create({
222
- name: productData.name,
223
- description: productData.description || "",
224
- default_price_data: {
225
- currency: productData.currency || "usd",
226
- unit_amount: Math.round(productData.price * 100)
227
- // Convert to cents
264
+ /**
265
+ * Confirm a payment intent
266
+ */
267
+ async confirmPayment(ctx) {
268
+ try {
269
+ const { paymentIntentId } = ctx.params;
270
+ if (!paymentIntentId) {
271
+ return ctx.badRequest("Payment intent ID is required");
228
272
  }
229
- });
230
- },
231
- async updateProduct(productId, productData) {
232
- return stripe.products.update(productId, {
233
- name: productData.name,
234
- description: productData.description || "",
235
- default_price: productData.stripePriceId
236
- });
237
- },
238
- async deleteProduct(productId) {
239
- return stripe.products.del(productId);
240
- },
241
- async createSubscription(customerId, priceId, options = {}) {
242
- return stripe.subscriptions.create({
243
- customer: customerId,
244
- items: [{ price: priceId }],
245
- ...options
246
- });
247
- },
248
- async updateSubscription(subscriptionId, options = {}) {
249
- return stripe.subscriptions.update(subscriptionId, options);
250
- },
251
- async cancelSubscription(subscriptionId) {
252
- return stripe.subscriptions.cancel(subscriptionId);
253
- },
254
- async getSubscription(subscriptionId) {
255
- return stripe.subscriptions.retrieve(subscriptionId);
256
- },
257
- async getTransactions(limit = 10, startingAfter = null) {
258
- const params = {
259
- limit: Math.min(limit, 100)
260
- // Max 100 per Stripe API
261
- };
262
- if (startingAfter) {
263
- params.starting_after = startingAfter;
273
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
274
+ const paymentIntent = await stripeService2.confirmPayment(paymentIntentId);
275
+ ctx.body = {
276
+ success: true,
277
+ data: {
278
+ paymentIntentId: paymentIntent.id,
279
+ status: paymentIntent.status,
280
+ amount: paymentIntent.amount,
281
+ currency: paymentIntent.currency
282
+ }
283
+ };
284
+ } catch (error) {
285
+ strapi.log.error("Failed to confirm payment", { error });
286
+ ctx.internalServerError("Failed to confirm payment");
264
287
  }
265
- const payments = await stripe.paymentIntents.list(params);
266
- return payments.data;
267
288
  },
268
- async importProductFromStripe(stripeProductId) {
269
- const product2 = await stripe.products.retrieve(stripeProductId, {
270
- expand: ["default_price"]
271
- });
272
- let priceInfo = null;
273
- if (product2.default_price) {
274
- if (typeof product2.default_price === "string") {
275
- const price = await stripe.prices.retrieve(product2.default_price);
276
- priceInfo = {
277
- unit_amount: price.unit_amount,
278
- currency: price.currency
279
- };
280
- } else {
281
- priceInfo = {
282
- unit_amount: product2.default_price.unit_amount,
283
- currency: product2.default_price.currency
284
- };
289
+ /**
290
+ * Create a Stripe customer
291
+ */
292
+ async createCustomer(ctx) {
293
+ try {
294
+ const { email, firstName, lastName, phone, address, metadata = {} } = ctx.request.body;
295
+ if (!email || !firstName || !lastName) {
296
+ return ctx.badRequest("Email, first name, and last name are required");
285
297
  }
298
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
299
+ const customer2 = await stripeService2.createCustomer({
300
+ email,
301
+ firstName,
302
+ lastName,
303
+ phone,
304
+ address,
305
+ metadata
306
+ });
307
+ ctx.body = {
308
+ success: true,
309
+ data: {
310
+ customerId: customer2.id,
311
+ email: customer2.email,
312
+ name: customer2.name,
313
+ created: customer2.created
314
+ }
315
+ };
316
+ } catch (error) {
317
+ strapi.log.error("Failed to create customer", { error });
318
+ ctx.internalServerError("Failed to create customer");
286
319
  }
287
- return {
288
- name: product2.name,
289
- description: product2.description,
290
- price: priceInfo ? priceInfo.unit_amount / 100 : 0,
291
- currency: priceInfo ? priceInfo.currency : "USD",
292
- stripeProductId: product2.id
293
- };
294
- },
295
- async createPayment(amount, currency, paymentMethod, returnUrl = "https://example.com/return") {
296
- const paymentIntent = await stripe.paymentIntents.create({
297
- amount,
298
- currency,
299
- payment_method: paymentMethod,
300
- confirmation_method: "automatic",
301
- confirm: true,
302
- return_url: returnUrl
303
- });
304
- return {
305
- paymentIntent,
306
- redirectUrl: paymentIntent.next_action?.redirect_to_url?.url || null
307
- };
308
320
  },
309
- async createPaymentWithUrl(amount, currency, paymentMethod, returnUrl = "https://example.com/return") {
310
- const paymentIntent = await stripe.paymentIntents.create({
311
- amount,
312
- currency,
313
- payment_method: paymentMethod,
314
- confirmation_method: "automatic",
315
- confirm: true,
316
- return_url: returnUrl
317
- });
318
- if (paymentIntent.next_action?.type === "redirect_to_url") {
319
- return {
320
- paymentIntent,
321
- redirectUrl: paymentIntent.next_action.redirect_to_url.url
321
+ /**
322
+ * Retrieve a Stripe customer
323
+ */
324
+ async retrieveCustomer(ctx) {
325
+ try {
326
+ const { customerId } = ctx.params;
327
+ if (!customerId) {
328
+ return ctx.badRequest("Customer ID is required");
329
+ }
330
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
331
+ const customer2 = await stripeService2.retrieveCustomer(customerId);
332
+ if ("deleted" in customer2 && customer2.deleted) {
333
+ return ctx.notFound("Customer not found");
334
+ }
335
+ ctx.body = {
336
+ success: true,
337
+ data: {
338
+ customerId: customer2.id,
339
+ email: customer2.email,
340
+ name: customer2.name,
341
+ created: customer2.created,
342
+ metadata: customer2.metadata
343
+ }
322
344
  };
345
+ } catch (error) {
346
+ strapi.log.error("Failed to retrieve customer", { error });
347
+ ctx.internalServerError("Failed to retrieve customer");
323
348
  }
324
- return {
325
- paymentIntent,
326
- redirectUrl: null
327
- };
328
349
  },
329
- async createPaymentLink(productId, options = {}) {
330
- const paymentLink = await stripe.paymentLinks.create({
331
- line_items: [
332
- {
333
- price: productId,
334
- quantity: 1
350
+ /**
351
+ * Create a refund
352
+ */
353
+ async createRefund(ctx) {
354
+ try {
355
+ const { paymentIntentId, amount, reason, metadata = {} } = ctx.request.body;
356
+ if (!paymentIntentId) {
357
+ return ctx.badRequest("Payment intent ID is required");
358
+ }
359
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
360
+ const refund = await stripeService2.createRefund({
361
+ paymentIntentId,
362
+ amount,
363
+ reason,
364
+ metadata
365
+ });
366
+ ctx.body = {
367
+ success: true,
368
+ data: {
369
+ refundId: refund.id,
370
+ amount: refund.amount,
371
+ currency: refund.currency,
372
+ status: refund.status,
373
+ reason: refund.reason
335
374
  }
336
- ],
337
- ...options
338
- });
339
- return paymentLink;
340
- }
341
- };
342
- const controller = ({ strapi: strapi2 }) => ({
343
- async initiatePayment(ctx) {
344
- const { gateway, amount, currency, orderId, options } = ctx.request.body;
345
- const result = await strapi2.plugin("strapi-payment-plugin").service("service").initializePayment(gateway, amount, currency, orderId, options);
346
- ctx.body = result;
375
+ };
376
+ } catch (error) {
377
+ strapi.log.error("Failed to create refund", { error });
378
+ ctx.internalServerError("Failed to create refund");
379
+ }
347
380
  },
348
- async processWebhook(ctx) {
349
- const { gateway } = ctx.params;
350
- const payload = ctx.request.body;
351
- const signature = ctx.request.headers["stripe-signature"] || ctx.request.headers["paypal-transmission-id"];
352
- const result = await strapi2.plugin("strapi-payment-plugin").service("service").processWebhook(gateway, payload, signature);
353
- ctx.body = result;
381
+ /**
382
+ * Handle Stripe webhooks
383
+ */
384
+ async handleWebhook(ctx) {
385
+ try {
386
+ const signature = ctx.request.headers["stripe-signature"];
387
+ let payload;
388
+ if (typeof ctx.request.body === "string") {
389
+ payload = ctx.request.body;
390
+ } else if (Buffer.isBuffer(ctx.request.body)) {
391
+ payload = ctx.request.body;
392
+ } else {
393
+ payload = JSON.stringify(ctx.request.body);
394
+ }
395
+ if (!signature) {
396
+ return ctx.badRequest("Missing Stripe signature");
397
+ }
398
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
399
+ const event = await stripeService2.constructEvent(payload, signature);
400
+ await stripeService2.handleWebhook(event);
401
+ ctx.body = { received: true };
402
+ } catch (error) {
403
+ strapi.log.error("Failed to handle webhook", { error });
404
+ ctx.badRequest(`Webhook Error: ${error.message}`);
405
+ }
354
406
  },
355
- async refundPayment(ctx) {
356
- const { gateway, transactionId, amount, options } = ctx.request.body;
357
- const result = await strapi2.plugin("strapi-payment-plugin").service("service").refundPayment(gateway, transactionId, amount, options);
358
- ctx.body = result;
407
+ /**
408
+ * Get Stripe service status/health check
409
+ */
410
+ async getServiceStatus(ctx) {
411
+ try {
412
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
413
+ let stripe;
414
+ try {
415
+ stripe = await stripeService2.initializeStripe();
416
+ } catch (e) {
417
+ stripe = null;
418
+ }
419
+ ctx.body = {
420
+ success: true,
421
+ data: {
422
+ service: "Stripe Payment Service",
423
+ status: stripe ? "initialized" : "not initialized",
424
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
425
+ }
426
+ };
427
+ } catch (error) {
428
+ strapi.log.error("Failed to get service status", { error });
429
+ ctx.internalServerError("Failed to get service status");
430
+ }
359
431
  },
432
+ /**
433
+ * Get payment details by Stripe payment intent ID
434
+ */
360
435
  async getPaymentDetails(ctx) {
361
- const { gateway, transactionId } = ctx.params;
362
- const result = await strapi2.plugin("strapi-payment-plugin").service("service").getPaymentDetails(gateway, transactionId);
363
- ctx.body = result;
436
+ try {
437
+ const { id: paymentIntentId } = ctx.params;
438
+ if (!paymentIntentId) {
439
+ return ctx.badRequest("Payment intent ID is required");
440
+ }
441
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
442
+ const stripe = await stripeService2.initializeStripe();
443
+ if (!stripe) {
444
+ return ctx.serviceUnavailable("Stripe service not initialized");
445
+ }
446
+ const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId);
447
+ const strapiPayments = await strapi.documents("plugin::payment-plugin.payment").findMany({
448
+ filters: {
449
+ stripe_payment_intent_id: paymentIntentId
450
+ },
451
+ populate: ["customer", "order"]
452
+ });
453
+ const strapiPayment = strapiPayments.length > 0 ? strapiPayments[0] : null;
454
+ ctx.body = {
455
+ success: true,
456
+ data: {
457
+ stripe: {
458
+ id: paymentIntent.id,
459
+ amount: paymentIntent.amount,
460
+ currency: paymentIntent.currency,
461
+ status: paymentIntent.status,
462
+ created: paymentIntent.created,
463
+ metadata: paymentIntent.metadata
464
+ },
465
+ strapi: strapiPayment
466
+ }
467
+ };
468
+ } catch (error) {
469
+ strapi.log.error("Failed to get payment details", { error });
470
+ ctx.internalServerError("Failed to get payment details");
471
+ }
364
472
  },
365
- async getTransactions(ctx) {
366
- console.log("Fetching transactions with params:", ctx.query);
367
- const { gateway, status, limit = 100, offset = 0 } = ctx.query;
368
- const result = await strapi2.plugin("strapi-payment-plugin").service("service").getTransactions(gateway, status, limit, offset);
369
- ctx.body = result;
473
+ /**
474
+ * List payments with filtering and pagination
475
+ */
476
+ async listPayments(ctx) {
477
+ try {
478
+ const { page = 1, pageSize = 25, status, customer: customer2, order: order2 } = ctx.query;
479
+ const { id: userId } = ctx.state.user;
480
+ const filters = {};
481
+ if (status) filters.status = { $eq: status };
482
+ if (customer2) filters.customer = { $eq: customer2 };
483
+ if (order2) filters.order = { $eq: order2 };
484
+ const isAdmin = ctx.state.user.role?.name === "Administrator";
485
+ if (!isAdmin) {
486
+ filters.$or = [
487
+ { customer: { $eq: userId } },
488
+ { order: { $eq: userId } }
489
+ ];
490
+ }
491
+ const pageNum = parseInt(page);
492
+ const pageSizeNum = parseInt(pageSize);
493
+ const start = (pageNum - 1) * pageSizeNum;
494
+ const [payments, total] = await Promise.all([
495
+ strapi.documents("plugin::payment-plugin.payment").findMany({
496
+ filters,
497
+ populate: ["customer", "order"],
498
+ sort: { createdAt: "desc" },
499
+ start,
500
+ limit: pageSizeNum
501
+ }),
502
+ strapi.documents("plugin::payment-plugin.payment").count({ filters })
503
+ ]);
504
+ ctx.body = {
505
+ success: true,
506
+ data: payments,
507
+ meta: {
508
+ pagination: {
509
+ page: pageNum,
510
+ pageSize: pageSizeNum,
511
+ pageCount: Math.ceil(total / pageSizeNum),
512
+ total
513
+ }
514
+ }
515
+ };
516
+ } catch (error) {
517
+ strapi.log.error("Failed to list payments", { error });
518
+ ctx.internalServerError("Failed to list payments");
519
+ }
370
520
  },
371
- async saveConfiguration(ctx) {
372
- const configData = ctx.request.body;
373
- const result = await strapi2.plugin("strapi-payment-plugin").service("service").saveConfiguration(configData);
374
- ctx.body = result;
521
+ /**
522
+ * List customers with filtering and pagination
523
+ */
524
+ async listCustomers(ctx) {
525
+ try {
526
+ const { page = 1, pageSize = 25, email } = ctx.query;
527
+ const { id: userId } = ctx.state.user;
528
+ const filters = {};
529
+ if (email) filters.email = { $containsi: email };
530
+ const isAdmin = ctx.state.user.role?.name === "Administrator";
531
+ if (!isAdmin) {
532
+ filters.documentId = userId;
533
+ }
534
+ const pageNum = parseInt(page);
535
+ const pageSizeNum = parseInt(pageSize);
536
+ const start = (pageNum - 1) * pageSizeNum;
537
+ const [customers, total] = await Promise.all([
538
+ strapi.documents("plugin::payment-plugin.customer").findMany({
539
+ filters,
540
+ sort: { createdAt: "desc" },
541
+ start,
542
+ limit: pageSizeNum
543
+ }),
544
+ strapi.documents("plugin::payment-plugin.customer").count({ filters })
545
+ ]);
546
+ ctx.body = {
547
+ success: true,
548
+ data: customers,
549
+ meta: {
550
+ pagination: {
551
+ page: pageNum,
552
+ pageSize: pageSizeNum,
553
+ pageCount: Math.ceil(total / pageSizeNum),
554
+ total
555
+ }
556
+ }
557
+ };
558
+ } catch (error) {
559
+ strapi.log.error("Failed to list customers", { error });
560
+ ctx.internalServerError("Failed to list customers");
561
+ }
375
562
  },
376
- async getConfigStatus(ctx) {
377
- console.log("Fetching configuration status");
378
- const result = await strapi2.plugin("strapi-payment-plugin").service("service").getConfigStatus();
379
- ctx.body = result;
563
+ /**
564
+ * Create a new order
565
+ */
566
+ async createOrder(ctx) {
567
+ try {
568
+ const { orderNumber, totalAmount, currency, items, customerId, metadata = {} } = ctx.request.body;
569
+ if (!orderNumber || !totalAmount || !currency || !items) {
570
+ return ctx.badRequest("Order number, total amount, currency, and items are required");
571
+ }
572
+ if (totalAmount <= 0) {
573
+ return ctx.badRequest("Total amount must be greater than 0");
574
+ }
575
+ if (customerId) {
576
+ const customers = await strapi.documents("plugin::payment-plugin.customer").findMany({
577
+ filters: {
578
+ documentId: customerId
579
+ }
580
+ });
581
+ if (customers.length === 0) {
582
+ return ctx.badRequest("Customer not found");
583
+ }
584
+ }
585
+ const orderData = {
586
+ order_number: orderNumber,
587
+ total_amount: totalAmount,
588
+ currency,
589
+ status: "pending",
590
+ customer: customerId,
591
+ items,
592
+ metadata
593
+ };
594
+ const order2 = await strapi.documents("plugin::payment-plugin.order").create({
595
+ data: orderData
596
+ });
597
+ ctx.body = {
598
+ success: true,
599
+ data: {
600
+ orderId: order2.documentId,
601
+ orderNumber: order2.order_number,
602
+ status: order2.status,
603
+ totalAmount: order2.total_amount,
604
+ currency: order2.currency,
605
+ created: order2.createdAt
606
+ }
607
+ };
608
+ } catch (error) {
609
+ strapi.log.error("Failed to create order", { error });
610
+ ctx.internalServerError("Failed to create order");
611
+ }
380
612
  },
381
- async testPayment(ctx) {
382
- const { amount, currency, paymentMethod, returnUrl } = ctx.request.body;
613
+ /**
614
+ * Get order details by ID
615
+ */
616
+ async getOrderDetails(ctx) {
383
617
  try {
384
- const result = await stripeDriver.createPayment(amount, currency, paymentMethod, returnUrl);
618
+ const { id } = ctx.params;
619
+ const { id: userId } = ctx.state.user;
620
+ if (!id) {
621
+ return ctx.badRequest("Order ID is required");
622
+ }
623
+ const isAdmin = ctx.state.user.role?.name === "Administrator";
624
+ const filters = { documentId: id };
625
+ if (!isAdmin) {
626
+ filters.$or = [
627
+ { customer: { $eq: userId } },
628
+ { customer: { $eq: userId } }
629
+ ];
630
+ }
631
+ const orders = await strapi.documents("plugin::payment-plugin.order").findMany({
632
+ filters,
633
+ populate: ["customer"]
634
+ });
635
+ if (orders.length === 0) {
636
+ return ctx.notFound("Order not found");
637
+ }
638
+ const order2 = orders[0];
639
+ const payments = await strapi.documents("plugin::payment-plugin.payment").findMany({
640
+ filters: { order: { $eq: order2.documentId } },
641
+ sort: { createdAt: "desc" }
642
+ });
385
643
  ctx.body = {
386
644
  success: true,
387
- paymentId: result.paymentIntent.id,
388
- message: "Payment successful",
389
- redirectUrl: result.redirectUrl
645
+ data: {
646
+ order: order2,
647
+ payments
648
+ }
390
649
  };
391
650
  } catch (error) {
392
- console.error("Payment test error:", error);
651
+ strapi.log.error("Failed to get order details", { error });
652
+ ctx.internalServerError("Failed to get order details");
653
+ }
654
+ },
655
+ /**
656
+ * List orders with filtering and pagination
657
+ */
658
+ async listOrders(ctx) {
659
+ try {
660
+ const { page = 1, pageSize = 25, status, customer: customer2 } = ctx.query;
661
+ const { id: userId } = ctx.state.user;
662
+ const filters = {};
663
+ if (status) filters.status = { $eq: status };
664
+ if (customer2) filters.customer = { $eq: customer2 };
665
+ const isAdmin = ctx.state.user.role?.name === "Administrator";
666
+ if (!isAdmin) {
667
+ filters.customer = { $eq: userId };
668
+ }
669
+ const pageNum = parseInt(page);
670
+ const pageSizeNum = parseInt(pageSize);
671
+ const start = (pageNum - 1) * pageSizeNum;
672
+ const [orders, total] = await Promise.all([
673
+ strapi.documents("plugin::payment-plugin.order").findMany({
674
+ filters,
675
+ populate: ["customer"],
676
+ sort: { createdAt: "desc" },
677
+ start,
678
+ limit: pageSizeNum
679
+ }),
680
+ strapi.documents("plugin::payment-plugin.order").count({ filters })
681
+ ]);
393
682
  ctx.body = {
394
- success: false,
395
- message: error.message
683
+ success: true,
684
+ data: orders,
685
+ meta: {
686
+ pagination: {
687
+ page: pageNum,
688
+ pageSize: pageSizeNum,
689
+ pageCount: Math.ceil(total / pageSizeNum),
690
+ total
691
+ }
692
+ }
396
693
  };
397
- ctx.status = 400;
694
+ } catch (error) {
695
+ strapi.log.error("Failed to list orders", { error });
696
+ ctx.internalServerError("Failed to list orders");
398
697
  }
399
698
  },
400
- async testPaymentWithRedirect(ctx) {
401
- const { productId } = ctx.request.body;
699
+ // Admin Controller Methods
700
+ /**
701
+ * Get dashboard data for admin panel
702
+ */
703
+ async getDashboardData(ctx) {
402
704
  try {
403
- const result = await stripeDriver.createPaymentLink(productId);
404
- console.log("Payment link created:", result);
705
+ const { period = "30d" } = ctx.query;
706
+ const now = /* @__PURE__ */ new Date();
707
+ const days = parseInt(period);
708
+ const startDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
709
+ const [totalPayments, successfulPayments, failedPayments, totalRevenue] = await Promise.all([
710
+ strapi.documents("plugin::payment-plugin.payment").count({}),
711
+ strapi.documents("plugin::payment-plugin.payment").count({ filters: { status: { $eq: "succeeded" } } }),
712
+ strapi.documents("plugin::payment-plugin.payment").count({ filters: { status: { $eq: "failed" } } }),
713
+ strapi.documents("plugin::payment-plugin.payment").findMany({
714
+ filters: {
715
+ status: { $eq: "succeeded" },
716
+ createdAt: { $gte: startDate.toISOString() }
717
+ },
718
+ fields: ["amount"]
719
+ })
720
+ ]);
721
+ const revenue = totalRevenue.reduce((sum, payment2) => sum + payment2.amount, 0);
722
+ const recentTransactions = await strapi.documents("plugin::payment-plugin.payment").findMany({
723
+ filters: { createdAt: { $gte: startDate.toISOString() } },
724
+ populate: ["customer", "order"],
725
+ sort: { createdAt: "desc" },
726
+ limit: 10
727
+ });
728
+ const topCustomers = await strapi.documents("plugin::payment-plugin.customer").findMany({
729
+ sort: { createdAt: "desc" },
730
+ limit: 5
731
+ });
405
732
  ctx.body = {
406
733
  success: true,
407
- paymentLink: result.url,
408
- message: "Payment link created successfully"
734
+ data: {
735
+ summary: {
736
+ totalPayments,
737
+ successfulPayments,
738
+ failedPayments,
739
+ successRate: totalPayments > 0 ? successfulPayments / totalPayments * 100 : 0,
740
+ totalRevenue: revenue,
741
+ period
742
+ },
743
+ recentTransactions,
744
+ topCustomers
745
+ }
409
746
  };
410
747
  } catch (error) {
411
- console.error("Payment test error:", error);
748
+ strapi.log.error("Failed to get dashboard data", { error });
749
+ ctx.internalServerError("Failed to get dashboard data");
750
+ }
751
+ },
752
+ /**
753
+ * Get payment reports
754
+ */
755
+ async getPaymentReports(ctx) {
756
+ try {
757
+ const { startDate, endDate, format = "json" } = ctx.query;
758
+ const filters = {};
759
+ if (startDate || endDate) {
760
+ filters.createdAt = {};
761
+ if (startDate) filters.createdAt.$gte = new Date(startDate).toISOString();
762
+ if (endDate) filters.createdAt.$lte = new Date(endDate).toISOString();
763
+ }
764
+ const payments = await strapi.documents("plugin::payment-plugin.payment").findMany({
765
+ filters,
766
+ populate: ["customer", "order"],
767
+ sort: { createdAt: "desc" }
768
+ });
769
+ const summary = {
770
+ totalPayments: payments.length,
771
+ totalAmount: payments.reduce((sum, p) => sum + p.amount, 0),
772
+ byStatus: payments.reduce((acc, p) => {
773
+ acc[p.status] = (acc[p.status] || 0) + 1;
774
+ return acc;
775
+ }, {}),
776
+ byCurrency: payments.reduce((acc, p) => {
777
+ acc[p.currency] = (acc[p.currency] || 0) + 1;
778
+ return acc;
779
+ }, {})
780
+ };
412
781
  ctx.body = {
413
- success: false,
414
- message: error.message
782
+ success: true,
783
+ data: {
784
+ summary,
785
+ payments,
786
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
787
+ dateRange: { startDate, endDate }
788
+ }
415
789
  };
416
- ctx.status = 400;
790
+ } catch (error) {
791
+ strapi.log.error("Failed to get payment reports", { error });
792
+ ctx.internalServerError("Failed to get payment reports");
417
793
  }
418
- }
419
- });
420
- const product$1 = strapi.factories.createCoreController("plugin::strapi-payment-plugin.product", ({ strapi: strapi2 }) => ({
421
- async create(ctx) {
794
+ },
795
+ /**
796
+ * Get plugin configuration
797
+ */
798
+ async getPluginConfig(ctx) {
422
799
  try {
423
- const { data } = ctx.request.body;
424
- const product2 = await strapi2.service("plugin::strapi-payment-plugin.product").createProduct(data);
425
- ctx.body = product2;
800
+ const store = strapi.store({ type: "plugin", name: "payment-plugin" });
801
+ const config2 = await store.get({ key: "settings" }) || {};
802
+ const staticConfig = strapi.config.get("plugin::payment-plugin") || {};
803
+ const mergedConfig = { ...staticConfig || {}, ...config2 || {} };
804
+ const sanitizedConfig = {
805
+ ...mergedConfig,
806
+ stripe: mergedConfig.stripe ? {
807
+ ...mergedConfig.stripe,
808
+ publishableKey: mergedConfig.stripe.publishableKey || process.env.STRIPE_PUBLISHABLE_KEY || (process.env.STRIPE_API_KEY?.startsWith("pk_") ? process.env.STRIPE_API_KEY : ""),
809
+ secretKey: mergedConfig.stripe.secretKey || process.env.STRIPE_SECRET_KEY || process.env.STRIPE_WEBHOOK_SECRET?.startsWith("sk_") ? "***configured***" : null,
810
+ webhookSecret: mergedConfig.stripe.webhookSecret || process.env.STRIPE_WEBHOOK_SECRET?.startsWith("whsec_") ? "***configured***" : null
811
+ } : {}
812
+ };
813
+ ctx.body = {
814
+ success: true,
815
+ data: sanitizedConfig
816
+ };
426
817
  } catch (error) {
427
- ctx.throw(500, error);
818
+ strapi.log.error("Failed to get plugin config", { error });
819
+ ctx.internalServerError("Failed to get plugin config");
428
820
  }
429
821
  },
430
- async update(ctx) {
822
+ /**
823
+ * Update plugin configuration
824
+ */
825
+ async updatePluginConfig(ctx) {
431
826
  try {
432
- const { id } = ctx.params;
433
- const { data } = ctx.request.body;
434
- const product2 = await strapi2.service("plugin::strapi-payment-plugin.product").updateProduct(id, data);
435
- ctx.body = product2;
827
+ const { stripe: newStripe, payment: payment2, logging } = ctx.request.body;
828
+ const store = strapi.store({ type: "plugin", name: "payment-plugin" });
829
+ const currentConfig = await store.get({ key: "settings" }) || {};
830
+ const currentStripe = currentConfig.stripe || {};
831
+ const stripe = {
832
+ ...newStripe,
833
+ secretKey: newStripe.secretKey === "***configured***" ? currentStripe.secretKey : newStripe.secretKey,
834
+ webhookSecret: newStripe.webhookSecret === "***configured***" ? currentStripe.webhookSecret : newStripe.webhookSecret
835
+ };
836
+ const config2 = {
837
+ stripe: stripe || {},
838
+ payment: payment2 || {},
839
+ logging: logging || {}
840
+ };
841
+ await store.set({ key: "settings", value: config2 });
842
+ ctx.body = {
843
+ success: true,
844
+ data: { message: "Configuration updated successfully" }
845
+ };
436
846
  } catch (error) {
437
- ctx.throw(500, error);
847
+ strapi.log.error("Failed to update plugin config", { error });
848
+ ctx.internalServerError("Failed to update plugin config");
438
849
  }
439
850
  },
440
- async find(ctx) {
851
+ /**
852
+ * Get analytics data
853
+ */
854
+ async getAnalytics(ctx) {
441
855
  try {
442
- const products = await strapi2.db.query("plugin::strapi-payment-plugin.product").findMany({
443
- orderBy: { name: "asc" }
856
+ const { period = "30d" } = ctx.query;
857
+ const days = parseInt(period);
858
+ const now = /* @__PURE__ */ new Date();
859
+ const startDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
860
+ const payments = await strapi.documents("plugin::payment-plugin.payment").findMany({
861
+ filters: {
862
+ createdAt: { $gte: startDate.toISOString() },
863
+ status: { $eq: "succeeded" }
864
+ },
865
+ fields: ["amount", "createdAt", "currency"],
866
+ sort: { createdAt: "asc" }
444
867
  });
445
- ctx.body = products;
868
+ const dailyStats = payments.reduce((acc, payment2) => {
869
+ const date = new Date(payment2.createdAt).toISOString().split("T")[0];
870
+ if (!acc[date]) {
871
+ acc[date] = { count: 0, amount: 0, currency: payment2.currency };
872
+ }
873
+ acc[date].count += 1;
874
+ acc[date].amount += payment2.amount;
875
+ return acc;
876
+ }, {});
877
+ ctx.body = {
878
+ success: true,
879
+ data: {
880
+ period,
881
+ dailyStats,
882
+ totalVolume: payments.reduce((sum, p) => sum + p.amount, 0),
883
+ totalTransactions: payments.length,
884
+ averageTransaction: payments.length > 0 ? payments.reduce((sum, p) => sum + p.amount, 0) / payments.length : 0
885
+ }
886
+ };
446
887
  } catch (error) {
447
- ctx.throw(500, error);
888
+ strapi.log.error("Failed to get analytics", { error });
889
+ ctx.internalServerError("Failed to get analytics");
448
890
  }
449
891
  },
450
- async findOne(ctx) {
892
+ /**
893
+ * Admin create refund
894
+ */
895
+ async adminCreateRefund(ctx) {
451
896
  try {
452
- const { id } = ctx.params;
453
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
454
- where: { id }
897
+ const { id: paymentId } = ctx.params;
898
+ const { amount, reason, metadata = {} } = ctx.request.body;
899
+ if (!paymentId) {
900
+ return ctx.badRequest("Payment ID is required");
901
+ }
902
+ const payments = await strapi.documents("plugin::payment-plugin.payment").findMany({
903
+ filters: { documentId: paymentId }
455
904
  });
456
- if (!product2) {
457
- return ctx.notFound("Product not found");
905
+ if (payments.length === 0) {
906
+ return ctx.notFound("Payment not found");
458
907
  }
459
- ctx.body = product2;
908
+ const payment2 = payments[0];
909
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
910
+ const refund = await stripeService2.createRefund({
911
+ paymentIntentId: payment2.stripe_payment_intent_id,
912
+ amount,
913
+ reason,
914
+ metadata: { ...metadata, admin_refund: true }
915
+ });
916
+ await strapi.documents("plugin::payment-plugin.payment").update({
917
+ documentId: payment2.documentId,
918
+ data: { status: "refunded" }
919
+ });
920
+ ctx.body = {
921
+ success: true,
922
+ data: {
923
+ refundId: refund.id,
924
+ amount: refund.amount,
925
+ currency: refund.currency,
926
+ status: refund.status,
927
+ reason: refund.reason
928
+ }
929
+ };
460
930
  } catch (error) {
461
- ctx.throw(500, error);
931
+ strapi.log.error("Failed to create admin refund", { error });
932
+ ctx.internalServerError("Failed to create admin refund");
462
933
  }
463
934
  },
464
- async delete(ctx) {
935
+ /**
936
+ * Admin list payments (no user restrictions)
937
+ */
938
+ async adminListPayments(ctx) {
465
939
  try {
466
- const { id } = ctx.params;
467
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
468
- where: { id }
469
- });
470
- if (!product2) {
471
- return ctx.notFound("Product not found");
472
- }
473
- if (product2.stripeProductId) {
474
- try {
475
- await stripeDriver.deleteProduct(product2.stripeProductId);
476
- } catch (stripeError) {
477
- console.error("Error deleting product from Stripe:", stripeError);
940
+ const { page = 1, pageSize = 25, status, customer: customer2, order: order2 } = ctx.query;
941
+ const filters = {};
942
+ if (status) filters.status = { $eq: status };
943
+ if (customer2) filters.customer = { $eq: customer2 };
944
+ if (order2) filters.order = { $eq: order2 };
945
+ const pageNum = parseInt(page);
946
+ const pageSizeNum = parseInt(pageSize);
947
+ const start = (pageNum - 1) * pageSizeNum;
948
+ const [payments, total] = await Promise.all([
949
+ strapi.documents("plugin::payment-plugin.payment").findMany({
950
+ filters,
951
+ populate: ["customer", "order"],
952
+ sort: { createdAt: "desc" },
953
+ start,
954
+ limit: pageSizeNum
955
+ }),
956
+ strapi.documents("plugin::payment-plugin.payment").count({ filters })
957
+ ]);
958
+ ctx.body = {
959
+ success: true,
960
+ data: payments,
961
+ meta: {
962
+ pagination: {
963
+ page: pageNum,
964
+ pageSize: pageSizeNum,
965
+ pageCount: Math.ceil(total / pageSizeNum),
966
+ total
967
+ }
478
968
  }
479
- }
480
- await strapi2.db.query("plugin::strapi-payment-plugin.product").delete({
481
- where: { id }
482
- });
483
- ctx.body = { message: "Product deleted successfully" };
969
+ };
484
970
  } catch (error) {
485
- ctx.throw(500, error);
971
+ strapi.log.error("Failed to list admin payments", { error });
972
+ ctx.internalServerError("Failed to list admin payments");
486
973
  }
487
974
  },
488
- async sync(ctx) {
975
+ /**
976
+ * Admin list customers (no user restrictions)
977
+ */
978
+ async adminListCustomers(ctx) {
489
979
  try {
490
- const { id } = ctx.params;
491
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
492
- where: { id }
980
+ const { page = 1, pageSize = 25, email } = ctx.query;
981
+ const filters = {};
982
+ if (email) filters.email = { $containsi: email };
983
+ const pageNum = parseInt(page);
984
+ const pageSizeNum = parseInt(pageSize);
985
+ const start = (pageNum - 1) * pageSizeNum;
986
+ const [customers, total] = await Promise.all([
987
+ strapi.documents("plugin::payment-plugin.customer").findMany({
988
+ filters,
989
+ populate: {
990
+ payments: {
991
+ filters: { status: { $eq: "succeeded" } },
992
+ fields: ["amount"]
993
+ }
994
+ },
995
+ sort: { createdAt: "desc" },
996
+ start,
997
+ limit: pageSizeNum
998
+ }),
999
+ strapi.documents("plugin::payment-plugin.customer").count({ filters })
1000
+ ]);
1001
+ const customersWithTotals = customers.map((customer2) => {
1002
+ const successfulPayments = customer2.payments || [];
1003
+ const totalSpent = successfulPayments.reduce((sum, p) => sum + p.amount, 0);
1004
+ const paymentCount = successfulPayments.length;
1005
+ return {
1006
+ ...customer2,
1007
+ totalSpent,
1008
+ paymentCount
1009
+ };
493
1010
  });
494
- if (!product2) {
495
- return ctx.notFound("Product not found");
496
- }
497
- if (product2.stripeProductId) {
498
- const stripeProduct = await stripeDriver.updateProduct(product2.stripeProductId, {
499
- name: product2.name,
500
- description: product2.description
501
- });
502
- ctx.body = { message: "Product synced successfully", stripeProduct };
503
- } else {
504
- const stripeProduct = await stripeDriver.createProduct(product2);
505
- await strapi2.db.query("plugin::strapi-payment-plugin.product").update({
506
- where: { id },
507
- data: { stripeProductId: stripeProduct.id }
508
- });
509
- ctx.body = { message: "Product created in Stripe successfully", stripeProduct };
510
- }
1011
+ ctx.body = {
1012
+ success: true,
1013
+ data: customersWithTotals,
1014
+ meta: {
1015
+ pagination: {
1016
+ page: pageNum,
1017
+ pageSize: pageSizeNum,
1018
+ pageCount: Math.ceil(total / pageSizeNum),
1019
+ total
1020
+ }
1021
+ }
1022
+ };
1023
+ } catch (error) {
1024
+ strapi.log.error("Failed to list admin customers", { error });
1025
+ ctx.internalServerError("Failed to list admin customers");
1026
+ }
1027
+ },
1028
+ /**
1029
+ * Admin list orders (no user restrictions)
1030
+ */
1031
+ async adminListOrders(ctx) {
1032
+ try {
1033
+ const { page = 1, pageSize = 25, status, customer: customer2 } = ctx.query;
1034
+ const filters = {};
1035
+ if (status) filters.status = { $eq: status };
1036
+ if (customer2) filters.customer = { $eq: customer2 };
1037
+ const pageNum = parseInt(page);
1038
+ const pageSizeNum = parseInt(pageSize);
1039
+ const start = (pageNum - 1) * pageSizeNum;
1040
+ const [orders, total] = await Promise.all([
1041
+ strapi.documents("plugin::payment-plugin.order").findMany({
1042
+ filters,
1043
+ populate: ["customer"],
1044
+ sort: { createdAt: "desc" },
1045
+ start,
1046
+ limit: pageSizeNum
1047
+ }),
1048
+ strapi.documents("plugin::payment-plugin.order").count({ filters })
1049
+ ]);
1050
+ ctx.body = {
1051
+ success: true,
1052
+ data: orders,
1053
+ meta: {
1054
+ pagination: {
1055
+ page: pageNum,
1056
+ pageSize: pageSizeNum,
1057
+ pageCount: Math.ceil(total / pageSizeNum),
1058
+ total
1059
+ }
1060
+ }
1061
+ };
511
1062
  } catch (error) {
512
- ctx.throw(500, error);
1063
+ strapi.log.error("Failed to list admin orders", { error });
1064
+ ctx.internalServerError("Failed to list admin orders");
513
1065
  }
514
1066
  },
515
- async getStripeProducts(ctx) {
1067
+ /**
1068
+ * Test webhook endpoint
1069
+ */
1070
+ async testWebhook(ctx) {
516
1071
  try {
517
- const stripeProducts = await stripeDriver.getStripeProducts();
518
- ctx.body = stripeProducts;
1072
+ const { eventType = "payment_intent.succeeded" } = ctx.request.body;
1073
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
1074
+ const testEvent = {
1075
+ id: `evt_test_${Date.now()}`,
1076
+ object: "event",
1077
+ type: eventType,
1078
+ data: {
1079
+ object: {
1080
+ id: `pi_test_${Date.now()}`,
1081
+ object: "payment_intent",
1082
+ amount: 1e3,
1083
+ currency: "usd",
1084
+ status: "succeeded",
1085
+ metadata: { test: true }
1086
+ }
1087
+ }
1088
+ };
1089
+ await stripeService2.handleWebhook(testEvent);
1090
+ ctx.body = {
1091
+ success: true,
1092
+ data: {
1093
+ message: "Test webhook processed successfully",
1094
+ eventId: testEvent.id,
1095
+ eventType: testEvent.type
1096
+ }
1097
+ };
519
1098
  } catch (error) {
520
- ctx.throw(500, error);
1099
+ strapi.log.error("Failed to test webhook", { error });
1100
+ ctx.internalServerError("Failed to test webhook");
521
1101
  }
522
1102
  },
523
- async importFromStripe(ctx) {
1103
+ /**
1104
+ * Initialize a functional payment flow (Customer -> Order -> PI)
1105
+ */
1106
+ async initializePayment(ctx) {
524
1107
  try {
525
- const { stripeProductId } = ctx.request.body;
526
- const productData = await stripeDriver.importProductFromStripe(stripeProductId);
527
- const existingProduct = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
528
- where: { stripeProductId }
529
- });
530
- if (existingProduct) {
531
- return ctx.badRequest("Product already imported");
532
- }
533
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").create({
534
- data: productData
1108
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
1109
+ const {
1110
+ amount = 5e3,
1111
+ currency = "usd",
1112
+ email = `customer-${Date.now()}@example.com`,
1113
+ firstName = "John",
1114
+ lastName = "Doe",
1115
+ confirm = false,
1116
+ payment_method
1117
+ } = ctx.request.body;
1118
+ const flowResult = await stripeService2.initializePaymentFlow({
1119
+ amount,
1120
+ currency,
1121
+ email,
1122
+ firstName,
1123
+ lastName,
1124
+ confirm,
1125
+ payment_method,
1126
+ metadata: { source: "admin_dashboard_init" }
535
1127
  });
536
- ctx.body = { message: "Product imported successfully", product: product2 };
1128
+ ctx.body = {
1129
+ success: true,
1130
+ data: flowResult
1131
+ };
537
1132
  } catch (error) {
538
- ctx.throw(500, error);
1133
+ strapi.log.error("Failed to initialize payment", { error });
1134
+ ctx.internalServerError(error instanceof Error ? error.message : "Failed to initialize payment");
539
1135
  }
540
1136
  },
541
- async createPaymentLink(ctx) {
1137
+ /**
1138
+ * Create and confirm a sample payment for dashboard demonstration
1139
+ */
1140
+ async createSamplePayment(ctx) {
542
1141
  try {
543
- const { id } = ctx.params;
544
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
545
- where: { id }
1142
+ const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
1143
+ const flowResult = await stripeService2.initializePaymentFlow({
1144
+ amount: 2500,
1145
+ currency: "usd",
1146
+ email: `sample@example.com`,
1147
+ firstName: "Sample",
1148
+ lastName: "Customer",
1149
+ confirm: true,
1150
+ payment_method: "pm_card_visa",
1151
+ // Still using this for the 'test' button to show a success
1152
+ metadata: { is_sample: "true" }
546
1153
  });
547
- if (!product2) {
548
- return ctx.notFound("Product not found");
549
- }
550
- if (!product2.stripeProductId) {
551
- return ctx.badRequest("Product is not synced with Stripe");
1154
+ if (flowResult.stripePaymentIntent.status === "succeeded") {
1155
+ await stripeService2.updateStrapiOrder(flowResult.order.documentId, {
1156
+ status: "completed"
1157
+ });
552
1158
  }
553
- const paymentLink = await stripeDriver.createPaymentLink(product2.stripeProductId, {
554
- metadata: {
555
- product_id: product2.id
556
- }
557
- });
558
- ctx.body = { paymentLink };
1159
+ ctx.body = {
1160
+ success: true,
1161
+ data: flowResult
1162
+ };
559
1163
  } catch (error) {
560
- ctx.throw(500, error);
1164
+ strapi.log.error("Failed to create sample payment", { error });
1165
+ ctx.internalServerError(error instanceof Error ? error.message : "Failed to create sample payment");
561
1166
  }
562
1167
  }
563
- }));
564
- const subscription$1 = strapi.factories.createCoreController(
565
- "plugin::strapi-payment-plugin.subscription",
566
- ({ strapi: strapi2 }) => ({
567
- async create(ctx) {
568
- try {
569
- const { data } = ctx.request.body;
570
- const subscription2 = await strapi2.service("plugin::strapi-payment-plugin.subscription").createSubscription(data);
571
- ctx.body = subscription2;
572
- } catch (error) {
573
- ctx.throw(500, error);
1168
+ };
1169
+ const controllers = {
1170
+ controller,
1171
+ stripe: stripeController
1172
+ };
1173
+ const middlewares = {
1174
+ /**
1175
+ * Webhook signature validator
1176
+ */
1177
+ "webhook-validator": async (ctx, next) => {
1178
+ try {
1179
+ const signature = ctx.request.headers["stripe-signature"];
1180
+ if (!signature) {
1181
+ return ctx.badRequest("Missing Stripe signature header");
574
1182
  }
575
- },
576
- async update(ctx) {
577
- try {
578
- const { id } = ctx.params;
579
- const { data } = ctx.request.body;
580
- const subscription2 = await strapi2.service("plugin::strapi-payment-plugin.subscription").updateSubscription(id, data);
581
- ctx.body = subscription2;
582
- } catch (error) {
583
- ctx.throw(500, error);
1183
+ if (!ctx.request.body || typeof ctx.request.body === "object") {
1184
+ strapi.log.warn("Webhook body already parsed - signature verification may fail");
584
1185
  }
585
- },
586
- async find(ctx) {
587
- try {
588
- console.log("Fetching subscriptions...");
589
- const subscriptions = await strapi2.db.query("plugin::strapi-payment-plugin.subscription").findMany({
590
- orderBy: { startDate: "desc" },
591
- populate: ["product", "user"]
592
- });
593
- ctx.body = subscriptions;
594
- } catch (error) {
595
- ctx.throw(500, error);
1186
+ ctx.state.isWebhook = true;
1187
+ ctx.state.webhookSignature = signature;
1188
+ await next();
1189
+ } catch (error) {
1190
+ strapi.log.error("Webhook validation failed", { error });
1191
+ return ctx.badRequest("Invalid webhook signature");
1192
+ }
1193
+ },
1194
+ /**
1195
+ * Rate limiter middleware for sensitive operations
1196
+ */
1197
+ rateLimiter: async (ctx, next) => {
1198
+ const clientIP = ctx.request.ip || ctx.request.connection?.remoteAddress || "unknown";
1199
+ const now = Date.now();
1200
+ const windowMs = 60 * 1e3;
1201
+ const maxRequests = 10;
1202
+ if (!strapi.plugin("payment-plugin").service("rateLimiter")) {
1203
+ const config2 = strapi.config.get("plugin::payment-plugin") || {};
1204
+ config2.rateLimits = config2.rateLimits || {};
1205
+ await strapi.config.set("plugin::payment-plugin", config2);
1206
+ }
1207
+ const pluginConfig = strapi.config.get("plugin::payment-plugin") || {};
1208
+ const rateLimits = pluginConfig.rateLimits || {};
1209
+ const clientLimits = rateLimits[clientIP] || { count: 0, resetTime: now + windowMs };
1210
+ if (now > clientLimits.resetTime) {
1211
+ clientLimits.count = 0;
1212
+ clientLimits.resetTime = now + windowMs;
1213
+ }
1214
+ if (clientLimits.count >= maxRequests) {
1215
+ strapi.log.warn("Rate limit exceeded", { clientIP, endpoint: ctx.path });
1216
+ return ctx.tooManyRequests("Too many requests. Please try again later.");
1217
+ }
1218
+ clientLimits.count++;
1219
+ rateLimits[clientIP] = clientLimits;
1220
+ pluginConfig.rateLimits = rateLimits;
1221
+ await strapi.config.set("plugin::payment-plugin", pluginConfig);
1222
+ await next();
1223
+ },
1224
+ /**
1225
+ * Input sanitization middleware
1226
+ */
1227
+ inputSanitizer: async (ctx, next) => {
1228
+ if (ctx.request.body && typeof ctx.request.body === "object") {
1229
+ ctx.request.body = sanitizeObject(ctx.request.body);
1230
+ }
1231
+ if (ctx.query && typeof ctx.query === "object") {
1232
+ ctx.query = sanitizeObject(ctx.query);
1233
+ }
1234
+ await next();
1235
+ }
1236
+ };
1237
+ function sanitizeObject(obj) {
1238
+ if (typeof obj === "string") {
1239
+ return sanitizeString(obj);
1240
+ }
1241
+ if (Array.isArray(obj)) {
1242
+ return obj.map(sanitizeObject);
1243
+ }
1244
+ if (obj && typeof obj === "object") {
1245
+ const sanitized = {};
1246
+ for (const [key, value] of Object.entries(obj)) {
1247
+ if (/^[a-zA-Z0-9_.-]+$/.test(key)) {
1248
+ sanitized[key] = sanitizeObject(value);
1249
+ }
1250
+ }
1251
+ return sanitized;
1252
+ }
1253
+ return obj;
1254
+ }
1255
+ function sanitizeString(str) {
1256
+ return str.replace(/[<>\"']/g, "").trim();
1257
+ }
1258
+ const policies = {
1259
+ isAuthenticated: (ctx) => {
1260
+ if (!ctx.state.user) {
1261
+ return false;
1262
+ }
1263
+ return true;
1264
+ }
1265
+ };
1266
+ const routes$2 = {
1267
+ type: "content-api",
1268
+ routes: [
1269
+ // Stripe Webhook Handler
1270
+ {
1271
+ method: "POST",
1272
+ path: "/webhook",
1273
+ handler: "stripe.handleWebhook",
1274
+ config: {
1275
+ auth: false,
1276
+ policies: []
596
1277
  }
597
1278
  },
598
- async findOne(ctx) {
599
- try {
600
- const { id } = ctx.params;
601
- const subscription2 = await strapi2.db.query("plugin::strapi-payment-plugin.subscription").findOne({
602
- where: { id },
603
- populate: ["product", "user"]
604
- });
605
- if (!subscription2) {
606
- return ctx.notFound("Subscription not found");
607
- }
608
- ctx.body = subscription2;
609
- } catch (error) {
610
- ctx.throw(500, error);
1279
+ // Payment Intent Operations
1280
+ {
1281
+ method: "POST",
1282
+ path: "/payments/create-intent",
1283
+ handler: "stripe.createPaymentIntent",
1284
+ config: {
1285
+ policies: []
611
1286
  }
612
1287
  },
613
- async delete(ctx) {
614
- try {
615
- const { id } = ctx.params;
616
- const subscription2 = await strapi2.db.query("plugin::strapi-payment-plugin.subscription").findOne({
617
- where: { id }
618
- });
619
- if (!subscription2) {
620
- return ctx.notFound("Subscription not found");
621
- }
622
- if (subscription2.stripeSubscriptionId) {
623
- try {
624
- const stripeDriver2 = strapi2.service("plugin::strapi-payment-plugin.stripeDriver");
625
- await stripeDriver2.cancelSubscription(subscription2.stripeSubscriptionId);
626
- } catch (stripeError) {
627
- console.error("Error canceling subscription in Stripe:", stripeError);
628
- }
629
- }
630
- await strapi2.db.query("plugin::strapi-payment-plugin.subscription").delete({
631
- where: { id }
632
- });
633
- ctx.body = { message: "Subscription deleted successfully" };
634
- } catch (error) {
635
- ctx.throw(500, error);
1288
+ {
1289
+ method: "POST",
1290
+ path: "/payments/confirm/:id",
1291
+ handler: "stripe.confirmPayment",
1292
+ config: {
1293
+ policies: []
636
1294
  }
637
1295
  },
638
- async sync(ctx) {
639
- try {
640
- const { id } = ctx.params;
641
- const subscription2 = await strapi2.service("plugin::strapi-payment-plugin.subscription").syncSubscriptionStatus(id);
642
- ctx.body = { message: "Subscription synced successfully", subscription: subscription2 };
643
- } catch (error) {
644
- ctx.throw(500, error);
1296
+ {
1297
+ method: "GET",
1298
+ path: "/payments/:id",
1299
+ handler: "stripe.getPaymentDetails",
1300
+ config: {
1301
+ policies: []
645
1302
  }
646
1303
  },
647
- async cancel(ctx) {
648
- try {
649
- const { id } = ctx.params;
650
- const subscription2 = await strapi2.service("plugin::strapi-payment-plugin.subscription").cancelSubscription(id);
651
- ctx.body = { message: "Subscription canceled successfully", subscription: subscription2 };
652
- } catch (error) {
653
- ctx.throw(500, error);
654
- }
655
- }
656
- })
657
- );
658
- const controllers = {
659
- controller,
660
- product: product$1,
661
- subscription: subscription$1
662
- };
663
- const middlewares = {};
664
- const policies = {};
665
- const adminRoutes = {
666
- type: "admin",
667
- routes: [
668
1304
  {
669
1305
  method: "GET",
670
- path: "/transactions",
671
- handler: "controller.getTransactions",
1306
+ path: "/payments",
1307
+ handler: "stripe.listPayments",
1308
+ config: {
1309
+ policies: []
1310
+ }
1311
+ },
1312
+ // Customer Operations
1313
+ {
1314
+ method: "POST",
1315
+ path: "/customers",
1316
+ handler: "stripe.createCustomer",
672
1317
  config: {
673
- policies: [],
674
- auth: false
1318
+ policies: []
675
1319
  }
676
1320
  },
677
1321
  {
678
1322
  method: "GET",
679
- path: "/products",
680
- handler: "product.find",
1323
+ path: "/customers/:id",
1324
+ handler: "stripe.retrieveCustomer",
681
1325
  config: {
682
- policies: [],
683
- auth: false
1326
+ policies: []
684
1327
  }
685
1328
  },
686
1329
  {
687
1330
  method: "GET",
688
- path: "/subscriptions",
689
- handler: "subscription.find",
1331
+ path: "/customers",
1332
+ handler: "stripe.listCustomers",
690
1333
  config: {
691
- policies: [],
692
- auth: false
1334
+ policies: []
693
1335
  }
694
1336
  },
1337
+ // Order Operations
695
1338
  {
696
1339
  method: "POST",
697
- path: "/config",
698
- handler: "controller.saveConfiguration",
1340
+ path: "/orders",
1341
+ handler: "stripe.createOrder",
699
1342
  config: {
700
- policies: [],
701
- auth: false
1343
+ policies: []
702
1344
  }
703
1345
  },
704
1346
  {
705
1347
  method: "GET",
706
- path: "/config-status",
707
- handler: "controller.getConfigStatus",
1348
+ path: "/orders/:id",
1349
+ handler: "stripe.getOrderDetails",
708
1350
  config: {
709
- policies: [],
710
- auth: false
1351
+ policies: []
711
1352
  }
712
1353
  },
713
1354
  {
714
- method: "POST",
715
- path: "/products",
716
- handler: "product.create",
1355
+ method: "GET",
1356
+ path: "/orders",
1357
+ handler: "stripe.listOrders",
717
1358
  config: {
718
- policies: [],
719
- auth: false
1359
+ policies: []
720
1360
  }
721
1361
  },
1362
+ // Refund Operations
722
1363
  {
723
1364
  method: "POST",
724
- path: "/products/:id/sync",
725
- handler: "product.sync",
1365
+ path: "/payments/:id/refund",
1366
+ handler: "stripe.createRefund",
1367
+ config: {
1368
+ policies: []
1369
+ }
1370
+ }
1371
+ ]
1372
+ };
1373
+ const routes$1 = {
1374
+ type: "admin",
1375
+ routes: [
1376
+ // Admin Dashboard and Analytics
1377
+ {
1378
+ method: "GET",
1379
+ path: "/admin/dashboard",
1380
+ handler: "stripe.getDashboardData",
726
1381
  config: {
727
- policies: [],
728
- auth: false
1382
+ policies: ["admin::isAuthenticatedAdmin"],
1383
+ middlewares: []
729
1384
  }
730
1385
  },
1386
+ // Payment Reports
731
1387
  {
732
- method: "DELETE",
733
- path: "/products/:id",
734
- handler: "product.delete",
1388
+ method: "GET",
1389
+ path: "/admin/reports",
1390
+ handler: "stripe.getPaymentReports",
735
1391
  config: {
736
- policies: [],
737
- auth: false
1392
+ policies: ["admin::isAuthenticatedAdmin"],
1393
+ middlewares: []
738
1394
  }
739
1395
  },
1396
+ // Configuration Management
740
1397
  {
741
- method: "POST",
742
- path: "/subscriptions/:id/sync",
743
- handler: "subscription.sync",
1398
+ method: "GET",
1399
+ path: "/admin/config",
1400
+ handler: "stripe.getPluginConfig",
744
1401
  config: {
745
- policies: [],
746
- auth: false
1402
+ policies: ["admin::isAuthenticatedAdmin"],
1403
+ middlewares: []
747
1404
  }
748
1405
  },
749
1406
  {
750
- method: "POST",
751
- path: "/products/import",
752
- handler: "product.importFromStripe",
1407
+ method: "PUT",
1408
+ path: "/admin/config",
1409
+ handler: "stripe.updatePluginConfig",
1410
+ config: {
1411
+ policies: ["admin::isAuthenticatedAdmin"],
1412
+ middlewares: []
1413
+ }
1414
+ },
1415
+ // Admin Analytics
1416
+ {
1417
+ method: "GET",
1418
+ path: "/admin/analytics",
1419
+ handler: "stripe.getAnalytics",
753
1420
  config: {
754
- policies: [],
755
- auth: false
1421
+ policies: ["admin::isAuthenticatedAdmin"],
1422
+ middlewares: []
756
1423
  }
757
1424
  },
1425
+ // Admin Payment Management
758
1426
  {
759
1427
  method: "POST",
760
- path: "/subscriptions/:id/sync",
761
- handler: "subscription.sync",
1428
+ path: "/admin/payments/:id/refund",
1429
+ handler: "stripe.adminCreateRefund",
762
1430
  config: {
763
- policies: [],
764
- auth: false
1431
+ policies: ["admin::isAuthenticatedAdmin"],
1432
+ middlewares: []
765
1433
  }
766
1434
  },
767
1435
  {
768
1436
  method: "GET",
769
- path: "/products/stripeProducts",
770
- handler: "product.getStripeProducts",
1437
+ path: "/admin/payments",
1438
+ handler: "stripe.adminListPayments",
771
1439
  config: {
772
- policies: [],
773
- auth: false
1440
+ policies: ["admin::isAuthenticatedAdmin"],
1441
+ middlewares: []
774
1442
  }
775
1443
  },
1444
+ // Admin Customer Management
776
1445
  {
777
- method: "POST",
778
- path: "/test-payment",
779
- handler: "controller.testPayment",
1446
+ method: "GET",
1447
+ path: "/admin/customers",
1448
+ handler: "stripe.adminListCustomers",
780
1449
  config: {
781
- policies: [],
782
- auth: false
1450
+ policies: ["admin::isAuthenticatedAdmin"],
1451
+ middlewares: []
783
1452
  }
784
1453
  },
1454
+ // Admin Order Management
785
1455
  {
786
- method: "POST",
787
- path: "/test-payment-with-redirect",
788
- handler: "controller.testPaymentWithRedirect",
1456
+ method: "GET",
1457
+ path: "/admin/orders",
1458
+ handler: "stripe.adminListOrders",
789
1459
  config: {
790
- policies: [],
791
- auth: false
1460
+ policies: ["admin::isAuthenticatedAdmin"],
1461
+ middlewares: []
792
1462
  }
793
- }
794
- ]
795
- };
796
- const refundRoutes = {
797
- type: "admin",
798
- routes: [
1463
+ },
1464
+ // Test Webhook
799
1465
  {
800
1466
  method: "POST",
801
- path: "/refund",
802
- handler: "controller.refundPayment",
1467
+ path: "/admin/test-webhook",
1468
+ handler: "stripe.testWebhook",
803
1469
  config: {
804
- policies: [],
805
- auth: false
1470
+ policies: ["admin::isAuthenticatedAdmin"],
1471
+ middlewares: []
806
1472
  }
807
- }
808
- ]
809
- };
810
- const contentApi = {
811
- type: "content-api",
812
- routes: [
1473
+ },
1474
+ // Functional Flow Initializer (Admin/Manual)
813
1475
  {
814
1476
  method: "POST",
815
- path: "/initiate-payment",
816
- handler: "controller.initiatePayment",
1477
+ path: "/admin/initialize-payment",
1478
+ handler: "stripe.initializePayment",
817
1479
  config: {
818
- policies: []
1480
+ policies: ["admin::isAuthenticatedAdmin"],
1481
+ middlewares: []
819
1482
  }
820
1483
  },
1484
+ // Sample Payment (for Dashboard)
821
1485
  {
822
1486
  method: "POST",
823
- path: "/webhook/:gateway",
824
- handler: "controller.processWebhook",
1487
+ path: "/admin/create-sample-payment",
1488
+ handler: "stripe.createSamplePayment",
825
1489
  config: {
826
- policies: []
1490
+ policies: ["admin::isAuthenticatedAdmin"],
1491
+ middlewares: []
827
1492
  }
828
1493
  }
829
1494
  ]
830
1495
  };
831
- const productRoutes = strapi.factories.createCoreRouter("plugin::strapi-payment-plugin.product", {
832
- config: {}
833
- });
834
- const subscriptionRoutes = strapi.factories.createCoreRouter("plugin::strapi-payment-plugin.subscription");
835
1496
  const routes = {
836
- adminRoutes,
837
- refundRoutes,
838
- contentApi,
839
- productRoutes,
840
- subscriptionRoutes
841
- };
842
- const paypalDriver = {
843
- async initializePayment(amount, currency, orderId, options) {
844
- console.warn("PayPal is disabled");
845
- return {
846
- status: "disabled",
847
- message: "PayPal functionality is currently disabled"
848
- };
849
- },
850
- async processWebhook(payload, signature) {
851
- console.warn("PayPal is disabled");
852
- return {
853
- status: "disabled",
854
- message: "PayPal functionality is currently disabled"
855
- };
856
- },
857
- async refundPayment(transactionId, amount, options) {
858
- console.warn("PayPal is disabled");
859
- return {
860
- status: "disabled",
861
- message: "PayPal functionality is currently disabled"
862
- };
863
- },
864
- async getPaymentDetails(transactionId) {
865
- console.warn("PayPal is disabled");
866
- return {
867
- status: "disabled",
868
- message: "PayPal functionality is currently disabled"
869
- };
870
- },
871
- async createProduct(productData) {
872
- console.warn("PayPal is disabled");
873
- return {
874
- status: "disabled",
875
- message: "PayPal functionality is currently disabled"
876
- };
877
- },
878
- async updateProduct(productId, productData) {
879
- console.warn("PayPal is disabled");
880
- return {
881
- status: "disabled",
882
- message: "PayPal functionality is currently disabled"
883
- };
884
- },
885
- async createSubscription(customerId, planId, options) {
886
- console.warn("PayPal is disabled");
887
- return {
888
- status: "disabled",
889
- message: "PayPal functionality is currently disabled"
890
- };
891
- },
892
- async updateSubscription(subscriptionId, options) {
893
- console.warn("PayPal is disabled");
894
- return {
895
- status: "disabled",
896
- message: "PayPal functionality is currently disabled"
897
- };
898
- },
899
- async cancelSubscription(subscriptionId, reason) {
900
- console.warn("PayPal is disabled");
901
- return {
902
- status: "disabled",
903
- message: "PayPal functionality is currently disabled"
904
- };
905
- },
906
- async getSubscription(subscriptionId) {
907
- console.warn("PayPal is disabled");
908
- return {
909
- status: "disabled",
910
- message: "PayPal functionality is currently disabled"
911
- };
912
- },
913
- async getTransactions(limit, page) {
914
- console.warn("PayPal is disabled");
915
- return {
916
- status: "disabled",
917
- message: "PayPal functionality is currently disabled"
918
- };
919
- }
920
- };
921
- const drivers = {
922
- stripe: stripeDriver,
923
- paypal: paypalDriver
1497
+ "content-api": routes$2,
1498
+ admin: routes$1
924
1499
  };
925
- const service = ({ strapi: strapi2 }) => ({
926
- async initializePayment(gateway, amount, currency, orderId, options) {
927
- const driver = drivers[gateway];
928
- if (!driver) throw new Error("Invalid payment gateway");
929
- return driver.initializePayment(amount, currency, orderId, options);
930
- },
931
- async processWebhook(gateway, payload, signature) {
932
- const driver = drivers[gateway];
933
- if (!driver) throw new Error("Invalid payment gateway");
934
- return driver.processWebhook(payload, signature);
935
- },
936
- async refundPayment(gateway, transactionId, amount, options) {
937
- const driver = drivers[gateway];
938
- if (!driver) throw new Error("Invalid payment gateway");
939
- return driver.refundPayment(transactionId, amount, options);
940
- },
941
- async getPaymentDetails(gateway, transactionId) {
942
- const driver = drivers[gateway];
943
- if (!driver) throw new Error("Invalid payment gateway");
944
- return driver.getPaymentDetails(transactionId);
945
- },
946
- async getTransactions(gateway, status, limit = 10, offset = 0) {
947
- if (gateway == "all") {
948
- const allTransactions = [];
949
- for (const key of Object.keys(drivers)) {
950
- const transactions = await this.getTransactions(key, status, limit, offset);
951
- allTransactions.push(...transactions.data);
952
- }
953
- return {
954
- data: allTransactions,
955
- meta: {
956
- total: allTransactions.length,
957
- limit,
958
- offset
959
- }
1500
+ const stripeService = ({ strapi: strapi2 }) => {
1501
+ let stripe = null;
1502
+ const getConfig = () => {
1503
+ return strapi2.config.get("plugin::payment-plugin") || {};
1504
+ };
1505
+ const getLogger = () => {
1506
+ return strapi2.log;
1507
+ };
1508
+ const getCombinedConfig = async () => {
1509
+ const staticConfig = strapi2.config.get("plugin::payment-plugin") || {};
1510
+ try {
1511
+ const store = strapi2.store({ type: "plugin", name: "payment-plugin" });
1512
+ const dbConfig = await store.get({ key: "settings" });
1513
+ const safeDbConfig = dbConfig && typeof dbConfig === "object" ? dbConfig : {};
1514
+ return { ...staticConfig, ...safeDbConfig };
1515
+ } catch (error) {
1516
+ return staticConfig;
1517
+ }
1518
+ };
1519
+ const initializeStripe = async () => {
1520
+ if (stripe) {
1521
+ return stripe;
1522
+ }
1523
+ const config2 = await getCombinedConfig();
1524
+ const stripeConfig = config2.stripe || {};
1525
+ const secretKey = stripeConfig.secretKey || process.env.STRIPE_SECRET_KEY || (process.env.STRIPE_WEBHOOK_SECRET?.startsWith("sk_") ? process.env.STRIPE_WEBHOOK_SECRET : null) || (process.env.STRIPE_API_KEY?.startsWith("sk_") ? process.env.STRIPE_API_KEY : null);
1526
+ if (!secretKey) {
1527
+ throw new Error("Stripe Secret Key not found. Please configure it in the plugin settings or set the STRIPE_SECRET_KEY environment variable.");
1528
+ }
1529
+ const apiVersion = stripeConfig.apiVersion || "2024-06-20";
1530
+ stripe = new Stripe__default.default(secretKey, {
1531
+ apiVersion
1532
+ });
1533
+ getLogger().info("Stripe service initialized successfully");
1534
+ return stripe;
1535
+ };
1536
+ const getStripe = () => {
1537
+ return stripe;
1538
+ };
1539
+ const createPaymentIntent = async (params) => {
1540
+ try {
1541
+ const stripe2 = await initializeStripe();
1542
+ const config2 = await getCombinedConfig();
1543
+ const logger = getLogger();
1544
+ const { amount, currency, metadata = {}, customer: customer2, orderId, customerId, payment_method, confirm } = params;
1545
+ const enhancedMetadata = {
1546
+ ...metadata,
1547
+ ...orderId && { strapi_order_id: orderId.toString() },
1548
+ ...customerId && { strapi_customer_id: customerId.toString() },
1549
+ source: "strapi_payment_plugin"
960
1550
  };
961
- } else if (drivers[gateway]) {
962
- const driver = drivers[gateway];
963
- let transactions = [];
964
- if (gateway === "stripe") {
965
- let startingAfter = null;
966
- if (offset > 0) {
967
- startingAfter = offset.toString();
1551
+ const paymentIntentData = {
1552
+ amount,
1553
+ currency,
1554
+ metadata: enhancedMetadata,
1555
+ automatic_payment_methods: {
1556
+ enabled: true,
1557
+ ...confirm && { allow_redirects: "never" }
968
1558
  }
969
- transactions = await driver.getTransactions(limit, startingAfter);
970
- } else if (gateway === "paypal") {
971
- console.warn("Paypal is disabled for now");
1559
+ };
1560
+ if (customer2) {
1561
+ paymentIntentData.customer = customer2;
972
1562
  }
973
- if (!transactions) {
974
- return;
1563
+ if (payment_method) {
1564
+ paymentIntentData.payment_method = payment_method;
975
1565
  }
976
- const formattedTransactions = transactions.map((txn) => {
977
- let amount, currency, customer, paymentMethod, txnStatus, date;
978
- if (gateway === "stripe") {
979
- amount = (txn.amount_received || txn.amount) / 100;
980
- currency = txn.currency.toUpperCase();
981
- customer = txn.metadata?.customer || txn.customer || "unknown";
982
- paymentMethod = txn.payment_method_types?.[0] || "unknown";
983
- txnStatus = txn.status;
984
- date = new Date(txn.created * 1e3).toISOString();
985
- } else if (gateway === "paypal") {
986
- amount = parseFloat(txn.amount_with_breakdown?.gross_amount?.value || "0");
987
- currency = txn.amount_with_breakdown?.gross_amount?.currency_code || "USD";
988
- customer = txn.payer_info?.email_address || "unknown";
989
- paymentMethod = txn.payment_source?.paypal?.name || "paypal";
990
- txnStatus = txn.status;
991
- date = new Date(txn.create_time).toISOString();
992
- }
993
- return {
994
- id: txn.id,
995
- amount,
996
- currency,
997
- status: txnStatus,
998
- date,
999
- customer,
1000
- paymentMethod,
1001
- gateway
1002
- };
1566
+ if (confirm) {
1567
+ paymentIntentData.confirm = confirm;
1568
+ }
1569
+ const paymentIntent = await stripe2.paymentIntents.create(paymentIntentData);
1570
+ logger.info("Payment intent created", {
1571
+ paymentIntentId: paymentIntent.id,
1572
+ amount,
1573
+ currency,
1574
+ orderId,
1575
+ customerId
1003
1576
  });
1004
- return {
1005
- data: formattedTransactions,
1006
- meta: {
1007
- total: formattedTransactions.length,
1008
- limit,
1009
- offset
1577
+ return paymentIntent;
1578
+ } catch (error) {
1579
+ const logger = getLogger();
1580
+ logger.error("Failed to create payment intent", { error, params });
1581
+ throw error;
1582
+ }
1583
+ };
1584
+ const confirmPayment = async (paymentIntentId) => {
1585
+ try {
1586
+ const stripe2 = await initializeStripe();
1587
+ const logger = getLogger();
1588
+ const paymentIntent = await stripe2.paymentIntents.confirm(paymentIntentId);
1589
+ logger.info("Payment confirmed", { paymentIntentId, status: paymentIntent.status });
1590
+ return paymentIntent;
1591
+ } catch (error) {
1592
+ const logger = getLogger();
1593
+ logger.error("Failed to confirm payment", { error, paymentIntentId });
1594
+ throw error;
1595
+ }
1596
+ };
1597
+ const createCustomer = async (params) => {
1598
+ try {
1599
+ const stripe2 = await initializeStripe();
1600
+ const logger = getLogger();
1601
+ const { email, firstName, lastName, phone, address, metadata = {}, strapiCustomerId } = params;
1602
+ const customerData = {
1603
+ email,
1604
+ name: `${firstName} ${lastName}`,
1605
+ phone,
1606
+ address,
1607
+ metadata: {
1608
+ ...metadata,
1609
+ ...strapiCustomerId && { strapi_customer_id: strapiCustomerId.toString() },
1610
+ first_name: firstName,
1611
+ last_name: lastName,
1612
+ source: "strapi_payment_plugin"
1010
1613
  }
1011
1614
  };
1012
- } else {
1013
- throw new Error("Invalid payment gateway");
1615
+ const customer2 = await stripe2.customers.create(customerData);
1616
+ logger.info("Customer created in Stripe", {
1617
+ customerId: customer2.id,
1618
+ email,
1619
+ strapiCustomerId
1620
+ });
1621
+ return customer2;
1622
+ } catch (error) {
1623
+ const logger = getLogger();
1624
+ logger.error("Failed to create customer", { error, params });
1625
+ throw error;
1014
1626
  }
1015
- },
1016
- async saveConfiguration(configData) {
1017
- return {
1018
- success: true,
1019
- message: "Configuration saved successfully",
1020
- data: configData
1021
- };
1022
- },
1023
- async getConfigStatus() {
1024
- const stripeConfigured = !!(process.env.STRIPE_SECRET_KEY && process.env.STRIPE_PUBLISHABLE_KEY && process.env.STRIPE_WEBHOOK_SECRET);
1025
- const paypalConfigured = !!(process.env.PAYPAL_CLIENT_ID && process.env.PAYPAL_CLIENT_SECRET && process.env.PAYPAL_WEBHOOK_ID);
1026
- return {
1027
- stripe: stripeConfigured,
1028
- paypal: paypalConfigured
1029
- };
1030
- }
1031
- });
1032
- const product = strapi.factories.createCoreService("plugin::strapi-payment-plugin.product", ({ strapi: strapi2 }) => ({
1033
- async createProduct(productData) {
1034
- const strapiProduct = await strapi2.db.query("plugin::strapi-payment-plugin.product").create({
1035
- data: productData
1036
- });
1037
- if (config.stripe.secretKey) {
1038
- try {
1039
- const stripeProduct = await stripeDriver.createProduct(productData);
1040
- await strapi2.db.query("plugin::strapi-payment-plugin.product").update({
1041
- where: { id: strapiProduct.id },
1042
- data: {
1043
- stripeProductId: stripeProduct.id
1044
- }
1045
- });
1046
- } catch (error) {
1047
- console.error("Error creating product in Stripe:", error);
1048
- }
1627
+ };
1628
+ const retrieveCustomer = async (customerId) => {
1629
+ try {
1630
+ const stripe2 = await initializeStripe();
1631
+ const logger = getLogger();
1632
+ const customer2 = await stripe2.customers.retrieve(customerId);
1633
+ logger.info("Customer retrieved from Stripe", { customerId });
1634
+ return customer2;
1635
+ } catch (error) {
1636
+ const logger = getLogger();
1637
+ logger.error("Failed to retrieve customer", { error, customerId });
1638
+ throw error;
1049
1639
  }
1050
- if (config.paypal.clientId && config.paypal.clientSecret) {
1051
- try {
1052
- console.warn("PayPal is disabled");
1053
- } catch (error) {
1054
- console.error("Error creating product in PayPal:", error);
1640
+ };
1641
+ const createRefund = async (params) => {
1642
+ try {
1643
+ const stripe2 = await initializeStripe();
1644
+ const logger = getLogger();
1645
+ const { paymentIntentId, amount, reason, metadata = {} } = params;
1646
+ const refundData = {
1647
+ payment_intent: paymentIntentId,
1648
+ metadata: {
1649
+ ...metadata,
1650
+ source: "strapi_payment_plugin",
1651
+ refunded_at: (/* @__PURE__ */ new Date()).toISOString()
1652
+ }
1653
+ };
1654
+ if (amount) {
1655
+ refundData.amount = amount;
1055
1656
  }
1056
- }
1057
- return strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
1058
- where: { id: strapiProduct.id }
1059
- });
1060
- },
1061
- async updateProduct(id, productData) {
1062
- const updatedProduct = await strapi2.db.query("plugin::strapi-payment-plugin.product").update({
1063
- where: { id },
1064
- data: productData
1065
- });
1066
- if (updatedProduct.stripeProductId && config.stripe.secretKey) {
1067
- try {
1068
- await stripeDriver.updateProduct(updatedProduct.stripeProductId, productData);
1069
- } catch (error) {
1070
- console.error("Error updating product in Stripe:", error);
1657
+ if (reason) {
1658
+ refundData.reason = reason;
1071
1659
  }
1660
+ const refund = await stripe2.refunds.create(refundData);
1661
+ logger.info("Refund processed", {
1662
+ refundId: refund.id,
1663
+ paymentIntentId,
1664
+ amount: refund.amount
1665
+ });
1666
+ return refund;
1667
+ } catch (error) {
1668
+ const logger = getLogger();
1669
+ logger.error("Failed to create refund", { error, params });
1670
+ throw error;
1072
1671
  }
1073
- if (updatedProduct.paypalProductId && config.paypal.clientId && config.paypal.clientSecret) {
1074
- try {
1075
- await paypalDriver.updateProduct(updatedProduct.paypalProductId, productData);
1076
- } catch (error) {
1077
- console.error("Error updating product in PayPal:", error);
1078
- }
1672
+ };
1673
+ const constructEvent = async (payload, signature) => {
1674
+ const stripe2 = await initializeStripe();
1675
+ const config2 = await getCombinedConfig();
1676
+ const stripeConfig = config2.stripe || {};
1677
+ const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET || stripeConfig.webhookSecret;
1678
+ if (!webhookSecret) {
1679
+ throw new Error("Stripe webhook secret not configured. Please set STRIPE_WEBHOOK_SECRET environment variable.");
1079
1680
  }
1080
- return updatedProduct;
1081
- }
1082
- }));
1083
- const subscription = strapi.factories.createCoreService("plugin::strapi-payment-plugin.subscription", ({ strapi: strapi2 }) => ({
1084
- async createSubscription(subscriptionData) {
1085
- const strapiSubscription = await strapi2.db.query("plugin::strapi-payment-plugin.subscription").create({
1086
- data: subscriptionData
1087
- });
1088
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
1089
- where: { id: subscriptionData.product }
1090
- });
1091
- if (product2.stripeProductId && config.stripe.secretKey) {
1092
- try {
1093
- let customerId = subscriptionData.stripeCustomerId;
1094
- if (!customerId) {
1095
- const customer = await stripeDriver.customers.create({
1096
- email: subscriptionData.email || "customer@example.com",
1097
- name: subscriptionData.customerName || "Customer"
1098
- });
1099
- customerId = customer.id;
1100
- await strapi2.db.query("plugin::strapi-payment-plugin.subscription").update({
1101
- where: { id: strapiSubscription.id },
1102
- data: {
1103
- stripeCustomerId: customerId
1104
- }
1105
- });
1681
+ try {
1682
+ return stripe2.webhooks.constructEvent(payload, signature, webhookSecret);
1683
+ } catch (error) {
1684
+ const logger = getLogger();
1685
+ logger.error("Failed to construct webhook event", { error });
1686
+ throw error;
1687
+ }
1688
+ };
1689
+ const updateStrapiPayment = async (stripePaymentIntentId, data) => {
1690
+ try {
1691
+ const existingPayments = await strapi2.documents("plugin::payment-plugin.payment").findMany({
1692
+ filters: {
1693
+ stripe_payment_intent_id: stripePaymentIntentId
1106
1694
  }
1107
- const stripeSubscription = await stripeDriver.createSubscription(
1108
- customerId,
1109
- product2.stripeProductId,
1110
- {
1111
- metadata: { strapiSubscriptionId: strapiSubscription.id }
1112
- }
1113
- );
1114
- await strapi2.db.query("plugin::strapi-payment-plugin.subscription").update({
1115
- where: { id: strapiSubscription.id },
1116
- data: {
1117
- stripeSubscriptionId: stripeSubscription.id
1118
- }
1695
+ });
1696
+ if (existingPayments.length > 0) {
1697
+ await strapi2.documents("plugin::payment-plugin.payment").update({
1698
+ documentId: existingPayments[0].documentId,
1699
+ data
1119
1700
  });
1120
- } catch (error) {
1121
- console.error("Error creating subscription in Stripe:", error);
1122
- }
1123
- }
1124
- if (product2.paypalProductId && config.paypal.clientId && config.paypal.clientSecret) {
1125
- try {
1126
- console.warn("PayPal is disabled");
1127
- } catch (error) {
1128
- console.error("Error creating subscription in PayPal:", error);
1129
1701
  }
1702
+ } catch (error) {
1703
+ const logger = getLogger();
1704
+ logger.error("Failed to update Strapi payment record", {
1705
+ error,
1706
+ stripePaymentIntentId,
1707
+ data
1708
+ });
1130
1709
  }
1131
- return strapi2.db.query("plugin::strapi-payment-plugin.subscription").findOne({
1132
- where: { id: strapiSubscription.id }
1133
- });
1134
- },
1135
- async updateSubscription(id, subscriptionData) {
1136
- const updatedSubscription = await strapi2.db.query("plugin::strapi-payment-plugin.subscription").update({
1137
- where: { id },
1138
- data: subscriptionData
1139
- });
1140
- if (updatedSubscription.stripeSubscriptionId && config.stripe.secretKey) {
1141
- try {
1142
- await stripeDriver.updateSubscription(updatedSubscription.stripeSubscriptionId, {
1143
- metadata: { strapiSubscriptionId: updatedSubscription.id }
1144
- });
1145
- } catch (error) {
1146
- console.error("Error updating subscription in Stripe:", error);
1147
- }
1710
+ };
1711
+ const updateStrapiOrder = async (orderId, data) => {
1712
+ try {
1713
+ await strapi2.documents("plugin::payment-plugin.order").update({
1714
+ documentId: orderId,
1715
+ data
1716
+ });
1717
+ } catch (error) {
1718
+ const logger = getLogger();
1719
+ logger.error("Failed to update Strapi order record", {
1720
+ error,
1721
+ orderId,
1722
+ data
1723
+ });
1148
1724
  }
1149
- if (updatedSubscription.paypalSubscriptionId && config.paypal.clientId && config.paypal.clientSecret) {
1150
- try {
1151
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
1152
- where: { id: updatedSubscription.product }
1153
- });
1154
- await paypalDriver.updateSubscription(updatedSubscription.paypalSubscriptionId, {
1155
- plan_id: product2.paypalProductId
1156
- });
1157
- } catch (error) {
1158
- console.error("Error updating subscription in PayPal:", error);
1159
- }
1725
+ };
1726
+ const updateStrapiCustomer = async (customerId, data) => {
1727
+ try {
1728
+ await strapi2.documents("plugin::payment-plugin.customer").update({
1729
+ documentId: customerId,
1730
+ data
1731
+ });
1732
+ } catch (error) {
1733
+ const logger = getLogger();
1734
+ logger.error("Failed to update Strapi customer record", {
1735
+ error,
1736
+ customerId,
1737
+ data
1738
+ });
1160
1739
  }
1161
- return updatedSubscription;
1162
- },
1163
- async cancelSubscription(id) {
1164
- const subscription2 = await strapi2.db.query("plugin::strapi-payment-plugin.subscription").findOne({
1165
- where: { id }
1740
+ };
1741
+ const createCustomerRecord = async (data) => {
1742
+ return strapi2.documents("plugin::payment-plugin.customer").create({
1743
+ data
1166
1744
  });
1167
- if (!subscription2) {
1168
- throw new Error("Subscription not found");
1169
- }
1170
- if (subscription2.stripeSubscriptionId && config.stripe.secretKey) {
1171
- try {
1172
- await stripeDriver.cancelSubscription(subscription2.stripeSubscriptionId);
1173
- } catch (error) {
1174
- console.error("Error canceling subscription in Stripe:", error);
1175
- }
1176
- }
1177
- if (subscription2.paypalSubscriptionId && config.paypal.clientId && config.paypal.clientSecret) {
1178
- try {
1179
- console.warn("PayPal is disabled");
1180
- } catch (error) {
1181
- console.error("Error canceling subscription in PayPal:", error);
1182
- }
1183
- }
1184
- return strapi2.db.query("plugin::strapi-payment-plugin.subscription").update({
1185
- where: { id },
1745
+ };
1746
+ const createOrderRecord = async (data) => {
1747
+ return strapi2.documents("plugin::payment-plugin.order").create({
1186
1748
  data: {
1187
- status: "canceled",
1188
- endDate: /* @__PURE__ */ new Date()
1749
+ ...data,
1750
+ status: data.status || "pending"
1189
1751
  }
1190
1752
  });
1191
- },
1192
- async syncSubscriptionStatus(id) {
1193
- const subscription2 = await strapi2.db.query("plugin::strapi-payment-plugin.subscription").findOne({
1194
- where: { id }
1753
+ };
1754
+ const createStrapiPaymentRecord = async (paymentIntent) => {
1755
+ try {
1756
+ const metadata = paymentIntent.metadata || {};
1757
+ const customerId = metadata.strapi_customer_id;
1758
+ const orderId = metadata.strapi_order_id;
1759
+ const paymentData = {
1760
+ stripe_payment_intent_id: paymentIntent.id,
1761
+ amount: paymentIntent.amount / 100,
1762
+ currency: paymentIntent.currency,
1763
+ status: paymentIntent.status,
1764
+ payment_method: paymentIntent.payment_method || "card",
1765
+ metadata: paymentIntent.metadata,
1766
+ customer: customerId,
1767
+ order: orderId
1768
+ };
1769
+ const record = await strapi2.documents("plugin::payment-plugin.payment").create({
1770
+ data: paymentData
1771
+ });
1772
+ getLogger().info("Created Strapi payment record", {
1773
+ paymentIntentId: paymentIntent.id,
1774
+ strapiOrderId: orderId,
1775
+ strapiCustomerId: customerId
1776
+ });
1777
+ return record;
1778
+ } catch (error) {
1779
+ const logger = getLogger();
1780
+ logger.error("Failed to create Strapi payment record", {
1781
+ error,
1782
+ paymentIntentId: paymentIntent.id
1783
+ });
1784
+ throw error;
1785
+ }
1786
+ };
1787
+ const initializePaymentFlow = async (params) => {
1788
+ const {
1789
+ amount,
1790
+ currency,
1791
+ email,
1792
+ firstName,
1793
+ lastName,
1794
+ items = [{ name: "Default Item", amount, quantity: 1 }],
1795
+ metadata = {},
1796
+ confirm = false,
1797
+ payment_method,
1798
+ orderNumber = `ORD-${Date.now()}`
1799
+ } = params;
1800
+ const stripeCustomer = await createCustomer({
1801
+ email,
1802
+ firstName,
1803
+ lastName
1195
1804
  });
1196
- if (!subscription2) {
1197
- throw new Error("Subscription not found");
1805
+ const customers = await strapi2.documents("plugin::payment-plugin.customer").findMany({
1806
+ filters: { email: { $eq: email } }
1807
+ });
1808
+ let strapiCustomer;
1809
+ if (customers.length > 0) {
1810
+ strapiCustomer = customers[0];
1811
+ await updateStrapiCustomer(strapiCustomer.documentId, {
1812
+ stripe_customer_id: stripeCustomer.id
1813
+ });
1814
+ } else {
1815
+ strapiCustomer = await createCustomerRecord({
1816
+ email,
1817
+ first_name: firstName,
1818
+ last_name: lastName,
1819
+ stripe_customer_id: stripeCustomer.id
1820
+ });
1198
1821
  }
1199
- if (subscription2.stripeSubscriptionId && config.stripe.secretKey) {
1200
- try {
1201
- const stripeSubscription = await stripeDriver.getSubscription(
1202
- subscription2.stripeSubscriptionId
1203
- );
1204
- await strapi2.db.query("plugin::strapi-payment-plugin.subscription").update({
1205
- where: { id },
1206
- data: {
1207
- status: stripeSubscription.status
1208
- }
1822
+ const strapiOrder = await createOrderRecord({
1823
+ order_number: orderNumber,
1824
+ total_amount: amount / 100,
1825
+ currency,
1826
+ customer: strapiCustomer.documentId,
1827
+ items,
1828
+ metadata
1829
+ });
1830
+ const paymentIntent = await createPaymentIntent({
1831
+ amount,
1832
+ currency,
1833
+ customerId: strapiCustomer.documentId,
1834
+ orderId: strapiOrder.documentId,
1835
+ customer: stripeCustomer.id,
1836
+ confirm,
1837
+ payment_method,
1838
+ metadata: { ...metadata, flow: "functional_api" }
1839
+ });
1840
+ const strapiPayment = await createStrapiPaymentRecord(paymentIntent);
1841
+ return {
1842
+ payment: strapiPayment,
1843
+ order: strapiOrder,
1844
+ customer: strapiCustomer,
1845
+ stripePaymentIntent: paymentIntent
1846
+ };
1847
+ };
1848
+ const handleWebhook = async (event) => {
1849
+ const logger = getLogger();
1850
+ const config2 = getConfig();
1851
+ const webhookConfig = config2.logging || {};
1852
+ try {
1853
+ if (webhookConfig.enableWebhookLogging) {
1854
+ logger.info("Processing Stripe webhook", {
1855
+ eventType: event.type,
1856
+ eventId: event.id
1209
1857
  });
1210
- } catch (error) {
1211
- console.error("Error syncing subscription status from Stripe:", error);
1212
1858
  }
1213
- }
1214
- if (subscription2.paypalSubscriptionId && config.paypal.clientId && config.paypal.clientSecret) {
1215
- try {
1216
- const paypalSubscription = await paypalDriver.getSubscription(
1217
- subscription2.paypalSubscriptionId
1218
- );
1219
- await strapi2.db.query("plugin::strapi-payment-plugin.subscription").update({
1220
- where: { id },
1221
- data: {
1222
- status: paypalSubscription.status
1859
+ switch (event.type) {
1860
+ case "payment_intent.succeeded": {
1861
+ const paymentIntent = event.data.object;
1862
+ try {
1863
+ const existingPayments = await strapi2.documents("plugin::payment-plugin.payment").findMany({
1864
+ filters: {
1865
+ stripe_payment_intent_id: paymentIntent.id
1866
+ }
1867
+ });
1868
+ if (existingPayments.length > 0) {
1869
+ await strapi2.documents("plugin::payment-plugin.payment").update({
1870
+ documentId: existingPayments[0].documentId,
1871
+ data: {
1872
+ status: "succeeded",
1873
+ metadata: paymentIntent.metadata
1874
+ }
1875
+ });
1876
+ } else {
1877
+ await createStrapiPaymentRecord(paymentIntent);
1878
+ }
1879
+ } catch (error) {
1880
+ await createStrapiPaymentRecord(paymentIntent);
1223
1881
  }
1224
- });
1225
- } catch (error) {
1226
- console.error("Error syncing subscription status from PayPal:", error);
1882
+ const orderId = paymentIntent.metadata?.strapi_order_id;
1883
+ if (orderId) {
1884
+ await updateStrapiOrder(orderId, {
1885
+ status: "completed"
1886
+ });
1887
+ }
1888
+ logger.info("Payment succeeded", {
1889
+ paymentIntentId: paymentIntent.id,
1890
+ amount: paymentIntent.amount,
1891
+ orderId
1892
+ });
1893
+ break;
1894
+ }
1895
+ case "payment_intent.payment_failed": {
1896
+ const paymentIntent = event.data.object;
1897
+ await updateStrapiPayment(paymentIntent.id, {
1898
+ status: "failed",
1899
+ metadata: paymentIntent.metadata
1900
+ });
1901
+ const orderId = paymentIntent.metadata?.strapi_order_id;
1902
+ if (orderId) {
1903
+ await updateStrapiOrder(orderId, {
1904
+ status: "failed"
1905
+ });
1906
+ }
1907
+ logger.warn("Payment failed", {
1908
+ paymentIntentId: paymentIntent.id,
1909
+ orderId,
1910
+ lastError: paymentIntent.last_payment_error
1911
+ });
1912
+ break;
1913
+ }
1914
+ case "payment_intent.canceled": {
1915
+ const paymentIntent = event.data.object;
1916
+ await updateStrapiPayment(paymentIntent.id, {
1917
+ status: "canceled",
1918
+ metadata: paymentIntent.metadata
1919
+ });
1920
+ const orderId = paymentIntent.metadata?.strapi_order_id;
1921
+ if (orderId) {
1922
+ await updateStrapiOrder(orderId, {
1923
+ status: "failed"
1924
+ });
1925
+ }
1926
+ logger.info("Payment canceled", {
1927
+ paymentIntentId: paymentIntent.id,
1928
+ orderId
1929
+ });
1930
+ break;
1931
+ }
1932
+ case "charge.refunded": {
1933
+ const charge = event.data.object;
1934
+ if (charge.payment_intent) {
1935
+ await updateStrapiPayment(charge.payment_intent, {
1936
+ status: "refunded"
1937
+ });
1938
+ const orderId = charge.metadata?.strapi_order_id;
1939
+ if (orderId) {
1940
+ await updateStrapiOrder(orderId, {
1941
+ status: "refunded"
1942
+ });
1943
+ }
1944
+ }
1945
+ logger.info("Charge refunded", {
1946
+ chargeId: charge.id,
1947
+ paymentIntentId: charge.payment_intent,
1948
+ amount: charge.amount_refunded,
1949
+ orderId: charge.metadata?.strapi_order_id
1950
+ });
1951
+ break;
1952
+ }
1953
+ case "customer.created": {
1954
+ const customer2 = event.data.object;
1955
+ const strapiCustomerId = customer2.metadata?.strapi_customer_id;
1956
+ if (strapiCustomerId) {
1957
+ await updateStrapiCustomer(strapiCustomerId, {
1958
+ stripe_customer_id: customer2.id,
1959
+ metadata: customer2.metadata
1960
+ });
1961
+ }
1962
+ logger.info("Customer created in Stripe", {
1963
+ stripeCustomerId: customer2.id,
1964
+ strapiCustomerId
1965
+ });
1966
+ break;
1967
+ }
1968
+ default:
1969
+ logger.info("Unhandled webhook event type", { eventType: event.type });
1227
1970
  }
1971
+ } catch (error) {
1972
+ logger.error("Error processing webhook", {
1973
+ error,
1974
+ eventType: event.type,
1975
+ eventId: event.id
1976
+ });
1977
+ throw error;
1228
1978
  }
1229
- return strapi2.db.query("plugin::strapi-payment-plugin.subscription").findOne({
1230
- where: { id }
1231
- });
1232
- }
1233
- }));
1979
+ };
1980
+ return {
1981
+ initializeStripe,
1982
+ createPaymentIntent,
1983
+ confirmPayment,
1984
+ createCustomer,
1985
+ retrieveCustomer,
1986
+ createRefund,
1987
+ handleWebhook,
1988
+ constructEvent,
1989
+ updateStrapiOrder,
1990
+ updateStrapiCustomer,
1991
+ createStrapiPaymentRecord,
1992
+ createCustomerRecord,
1993
+ createOrderRecord,
1994
+ initializePaymentFlow,
1995
+ getStripe
1996
+ };
1997
+ };
1234
1998
  const services = {
1235
- service,
1236
- product,
1237
- subscription
1999
+ stripe: stripeService
1238
2000
  };
1239
2001
  const index = {
1240
2002
  register,