@reeboot/strapi-payment-plugin 0.0.1 → 0.0.3

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-DSJqY9ng.mjs +355 -0
  3. package/dist/_chunks/Analytics-nBSdLT2v.js +355 -0
  4. package/dist/_chunks/App-B83DZ9NG.js +70 -0
  5. package/dist/_chunks/App-BUSTbkyy.mjs +68 -0
  6. package/dist/_chunks/Customers-BpFzfglV.js +273 -0
  7. package/dist/_chunks/Customers-C6FH7-zG.mjs +273 -0
  8. package/dist/_chunks/Dashboard-CNMTzSyc.js +180 -0
  9. package/dist/_chunks/Dashboard-Dbwl0ZBo.mjs +180 -0
  10. package/dist/_chunks/Orders-CBkT2YfP.mjs +308 -0
  11. package/dist/_chunks/Orders-OG-pwV-B.js +308 -0
  12. package/dist/_chunks/Payments-BLen1P9N.js +489 -0
  13. package/dist/_chunks/Payments-DSDJ-HWm.mjs +489 -0
  14. package/dist/_chunks/Settings-Dq1xy32B.js +357 -0
  15. package/dist/_chunks/Settings-jmGslDsB.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-BqqrpI6D.js +66 -0
  19. package/dist/_chunks/index-DS_PYNkf.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 +1770 -992
  37. package/dist/server/index.mjs +1773 -995
  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,2008 @@ 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"
18
24
  },
19
- stripe: {
20
- secretKey: process.env.STRIPE_SECRET_KEY,
21
- publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
22
- webhookSecret: process.env.STRIPE_WEBHOOK_SECRET
25
+ options: {
26
+ draftAndPublish: false
27
+ },
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"
168
+ attributes: {
169
+ stripe_payment_intent_id: {
170
+ type: "string",
171
+ unique: true,
172
+ required: true,
173
+ configurable: false
118
174
  },
119
- "product": {
120
- "type": "relation",
121
- "relation": "oneToOne",
122
- "target": "plugin::strapi-payment-plugin.product"
175
+ amount: {
176
+ type: "integer",
177
+ required: true,
178
+ min: 0,
179
+ configurable: false,
180
+ default: 0
123
181
  },
124
- "status": {
125
- "type": "enumeration",
126
- "enum": ["active", "canceled", "past_due", "unpaid", "trialing"],
127
- "default": "active"
182
+ currency: {
183
+ type: "string",
184
+ required: true,
185
+ default: "usd",
186
+ configurable: false
128
187
  },
129
- "startDate": {
130
- "type": "date",
131
- "required": true
188
+ status: {
189
+ type: "enumeration",
190
+ enum: ["pending", "succeeded", "failed", "canceled"],
191
+ required: true,
192
+ default: "pending",
193
+ configurable: false
132
194
  },
133
- "endDate": {
134
- "type": "date"
195
+ payment_method: {
196
+ type: "string",
197
+ configurable: false
135
198
  },
136
- "stripeSubscriptionId": {
137
- "type": "string"
199
+ metadata: {
200
+ type: "json",
201
+ configurable: false
138
202
  },
139
- "paypalSubscriptionId": {
140
- "type": "string"
203
+ customer: {
204
+ type: "relation",
205
+ relation: "manyToOne",
206
+ target: "plugin::payment-plugin.customer",
207
+ inversedBy: "payments"
141
208
  },
142
- "createdAt": {
143
- "type": "datetime"
144
- },
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;
347
- },
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;
354
- },
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;
359
- },
360
- 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;
364
- },
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;
370
- },
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;
375
+ };
376
+ } catch (error) {
377
+ strapi.log.error("Failed to create refund", { error });
378
+ ctx.internalServerError("Failed to create refund");
379
+ }
375
380
  },
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;
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
+ }
380
406
  },
381
- async testPayment(ctx) {
382
- const { amount, currency, paymentMethod, returnUrl } = ctx.request.body;
407
+ /**
408
+ * Get Stripe service status/health check
409
+ */
410
+ async getServiceStatus(ctx) {
383
411
  try {
384
- const result = await stripeDriver.createPayment(amount, currency, paymentMethod, returnUrl);
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
+ }
385
419
  ctx.body = {
386
420
  success: true,
387
- paymentId: result.paymentIntent.id,
388
- message: "Payment successful",
389
- redirectUrl: result.redirectUrl
421
+ data: {
422
+ service: "Stripe Payment Service",
423
+ status: stripe ? "initialized" : "not initialized",
424
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
425
+ }
390
426
  };
391
427
  } catch (error) {
392
- console.error("Payment test error:", error);
393
- ctx.body = {
394
- success: false,
395
- message: error.message
396
- };
397
- ctx.status = 400;
428
+ strapi.log.error("Failed to get service status", { error });
429
+ ctx.internalServerError("Failed to get service status");
398
430
  }
399
431
  },
400
- async testPaymentWithRedirect(ctx) {
401
- const { productId } = ctx.request.body;
432
+ /**
433
+ * Get payment details by Stripe payment intent ID
434
+ */
435
+ async getPaymentDetails(ctx) {
402
436
  try {
403
- const result = await stripeDriver.createPaymentLink(productId);
404
- console.log("Payment link created:", result);
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;
405
454
  ctx.body = {
406
455
  success: true,
407
- paymentLink: result.url,
408
- message: "Payment link created successfully"
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
+ }
409
467
  };
410
468
  } catch (error) {
411
- console.error("Payment test error:", error);
412
- ctx.body = {
413
- success: false,
414
- message: error.message
415
- };
416
- ctx.status = 400;
469
+ strapi.log.error("Failed to get payment details", { error });
470
+ ctx.internalServerError("Failed to get payment details");
417
471
  }
418
- }
419
- });
420
- const product$1 = strapi.factories.createCoreController("plugin::strapi-payment-plugin.product", ({ strapi: strapi2 }) => ({
421
- async create(ctx) {
472
+ },
473
+ /**
474
+ * List payments with filtering and pagination
475
+ */
476
+ async listPayments(ctx) {
422
477
  try {
423
- const { data } = ctx.request.body;
424
- const product2 = await strapi2.service("plugin::strapi-payment-plugin.product").createProduct(data);
425
- ctx.body = product2;
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
+ };
426
516
  } catch (error) {
427
- ctx.throw(500, error);
517
+ strapi.log.error("Failed to list payments", { error });
518
+ ctx.internalServerError("Failed to list payments");
428
519
  }
429
520
  },
430
- async update(ctx) {
521
+ /**
522
+ * List customers with filtering and pagination
523
+ */
524
+ async listCustomers(ctx) {
431
525
  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;
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
+ };
436
558
  } catch (error) {
437
- ctx.throw(500, error);
559
+ strapi.log.error("Failed to list customers", { error });
560
+ ctx.internalServerError("Failed to list customers");
438
561
  }
439
562
  },
440
- async find(ctx) {
563
+ /**
564
+ * Create a new order
565
+ */
566
+ async createOrder(ctx) {
441
567
  try {
442
- const products = await strapi2.db.query("plugin::strapi-payment-plugin.product").findMany({
443
- orderBy: { name: "asc" }
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
444
596
  });
445
- ctx.body = products;
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
+ };
446
608
  } catch (error) {
447
- ctx.throw(500, error);
609
+ strapi.log.error("Failed to create order", { error });
610
+ ctx.internalServerError("Failed to create order");
448
611
  }
449
612
  },
450
- async findOne(ctx) {
613
+ /**
614
+ * Get order details by ID
615
+ */
616
+ async getOrderDetails(ctx) {
451
617
  try {
452
618
  const { id } = ctx.params;
453
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
454
- where: { id }
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"]
455
634
  });
456
- if (!product2) {
457
- return ctx.notFound("Product not found");
635
+ if (orders.length === 0) {
636
+ return ctx.notFound("Order not found");
458
637
  }
459
- ctx.body = product2;
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
+ });
643
+ ctx.body = {
644
+ success: true,
645
+ data: {
646
+ order: order2,
647
+ payments
648
+ }
649
+ };
460
650
  } catch (error) {
461
- ctx.throw(500, error);
651
+ strapi.log.error("Failed to get order details", { error });
652
+ ctx.internalServerError("Failed to get order details");
462
653
  }
463
654
  },
464
- async delete(ctx) {
655
+ /**
656
+ * List orders with filtering and pagination
657
+ */
658
+ async listOrders(ctx) {
465
659
  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");
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 };
472
668
  }
473
- if (product2.stripeProductId) {
474
- try {
475
- await stripeDriver.deleteProduct(product2.stripeProductId);
476
- } catch (stripeError) {
477
- console.error("Error deleting product from Stripe:", stripeError);
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
+ ]);
682
+ ctx.body = {
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
+ }
478
692
  }
479
- }
480
- await strapi2.db.query("plugin::strapi-payment-plugin.product").delete({
481
- where: { id }
482
- });
483
- ctx.body = { message: "Product deleted successfully" };
693
+ };
484
694
  } catch (error) {
485
- ctx.throw(500, error);
695
+ strapi.log.error("Failed to list orders", { error });
696
+ ctx.internalServerError("Failed to list orders");
486
697
  }
487
698
  },
488
- async sync(ctx) {
699
+ // Admin Controller Methods
700
+ /**
701
+ * Get dashboard data for admin panel
702
+ */
703
+ async getDashboardData(ctx) {
489
704
  try {
490
- const { id } = ctx.params;
491
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
492
- where: { id }
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
493
727
  });
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 };
728
+ const topCustomers = await strapi.documents("plugin::payment-plugin.customer").findMany({
729
+ sort: { createdAt: "desc" },
730
+ limit: 5
731
+ });
732
+ ctx.body = {
733
+ success: true,
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
+ }
746
+ };
747
+ } catch (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();
510
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
+ };
781
+ ctx.body = {
782
+ success: true,
783
+ data: {
784
+ summary,
785
+ payments,
786
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
787
+ dateRange: { startDate, endDate }
788
+ }
789
+ };
511
790
  } catch (error) {
512
- ctx.throw(500, error);
791
+ strapi.log.error("Failed to get payment reports", { error });
792
+ ctx.internalServerError("Failed to get payment reports");
513
793
  }
514
794
  },
515
- async getStripeProducts(ctx) {
795
+ /**
796
+ * Get plugin configuration
797
+ */
798
+ async getPluginConfig(ctx) {
516
799
  try {
517
- const stripeProducts = await stripeDriver.getStripeProducts();
518
- ctx.body = stripeProducts;
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
+ };
519
817
  } catch (error) {
520
- ctx.throw(500, error);
818
+ strapi.log.error("Failed to get plugin config", { error });
819
+ ctx.internalServerError("Failed to get plugin config");
521
820
  }
522
821
  },
523
- async importFromStripe(ctx) {
822
+ /**
823
+ * Update plugin configuration
824
+ */
825
+ async updatePluginConfig(ctx) {
524
826
  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
535
- });
536
- ctx.body = { message: "Product imported successfully", product: 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
+ };
537
846
  } catch (error) {
538
- ctx.throw(500, error);
847
+ strapi.log.error("Failed to update plugin config", { error });
848
+ ctx.internalServerError("Failed to update plugin config");
539
849
  }
540
850
  },
541
- async createPaymentLink(ctx) {
851
+ /**
852
+ * Get analytics data
853
+ */
854
+ async getAnalytics(ctx) {
542
855
  try {
543
- const { id } = ctx.params;
544
- const product2 = await strapi2.db.query("plugin::strapi-payment-plugin.product").findOne({
545
- where: { id }
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" }
546
867
  });
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");
552
- }
553
- const paymentLink = await stripeDriver.createPaymentLink(product2.stripeProductId, {
554
- metadata: {
555
- product_id: product2.id
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 };
556
872
  }
557
- });
558
- ctx.body = { paymentLink };
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
+ };
559
887
  } catch (error) {
560
- ctx.throw(500, error);
888
+ strapi.log.error("Failed to get analytics", { error });
889
+ ctx.internalServerError("Failed to get analytics");
561
890
  }
562
- }
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);
574
- }
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);
584
- }
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);
891
+ },
892
+ /**
893
+ * Admin create refund
894
+ */
895
+ async adminCreateRefund(ctx) {
896
+ try {
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");
596
901
  }
597
- },
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);
902
+ const payments = await strapi.documents("plugin::payment-plugin.payment").findMany({
903
+ filters: { documentId: paymentId }
904
+ });
905
+ if (payments.length === 0) {
906
+ return ctx.notFound("Payment not found");
611
907
  }
612
- },
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");
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
621
928
  }
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);
929
+ };
930
+ } catch (error) {
931
+ strapi.log.error("Failed to create admin refund", { error });
932
+ ctx.internalServerError("Failed to create admin refund");
933
+ }
934
+ },
935
+ /**
936
+ * Admin list payments (no user restrictions)
937
+ */
938
+ async adminListPayments(ctx) {
939
+ try {
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
628
967
  }
629
968
  }
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);
636
- }
637
- },
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);
645
- }
646
- },
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);
969
+ };
970
+ } catch (error) {
971
+ strapi.log.error("Failed to list admin payments", { error });
972
+ ctx.internalServerError("Failed to list admin payments");
973
+ }
974
+ },
975
+ /**
976
+ * Admin list customers (no user restrictions)
977
+ */
978
+ async adminListCustomers(ctx) {
979
+ try {
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
+ };
1010
+ });
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
+ };
1062
+ } catch (error) {
1063
+ strapi.log.error("Failed to list admin orders", { error });
1064
+ ctx.internalServerError("Failed to list admin orders");
1065
+ }
1066
+ },
1067
+ /**
1068
+ * Test webhook endpoint
1069
+ */
1070
+ async testWebhook(ctx) {
1071
+ try {
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
+ };
1098
+ } catch (error) {
1099
+ strapi.log.error("Failed to test webhook", { error });
1100
+ ctx.internalServerError("Failed to test webhook");
1101
+ }
1102
+ },
1103
+ /**
1104
+ * Initialize a functional payment flow (Customer -> Order -> PI)
1105
+ */
1106
+ async initializePayment(ctx) {
1107
+ try {
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" }
1127
+ });
1128
+ ctx.body = {
1129
+ success: true,
1130
+ data: flowResult
1131
+ };
1132
+ } catch (error) {
1133
+ strapi.log.error("Failed to initialize payment", { error });
1134
+ ctx.internalServerError(error instanceof Error ? error.message : "Failed to initialize payment");
1135
+ }
1136
+ },
1137
+ /**
1138
+ * Create and confirm a sample payment for dashboard demonstration
1139
+ */
1140
+ async createSamplePayment(ctx) {
1141
+ try {
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" }
1153
+ });
1154
+ if (flowResult.stripePaymentIntent.status === "succeeded") {
1155
+ await stripeService2.updateStrapiOrder(flowResult.order.documentId, {
1156
+ status: "completed"
1157
+ });
654
1158
  }
1159
+ ctx.body = {
1160
+ success: true,
1161
+ data: flowResult
1162
+ };
1163
+ } catch (error) {
1164
+ strapi.log.error("Failed to create sample payment", { error });
1165
+ ctx.internalServerError(error instanceof Error ? error.message : "Failed to create sample payment");
655
1166
  }
656
- })
657
- );
1167
+ }
1168
+ };
658
1169
  const controllers = {
659
1170
  controller,
660
- product: product$1,
661
- subscription: subscription$1
1171
+ stripe: stripeController
662
1172
  };
663
- const middlewares = {};
664
- const policies = {};
665
- const adminRoutes = {
666
- type: "admin",
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");
1182
+ }
1183
+ if (!ctx.request.body || typeof ctx.request.body === "object") {
1184
+ strapi.log.warn("Webhook body already parsed - signature verification may fail");
1185
+ }
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",
667
1268
  routes: [
1269
+ // Stripe Webhook Handler
668
1270
  {
669
- method: "GET",
670
- path: "/transactions",
671
- handler: "controller.getTransactions",
1271
+ method: "POST",
1272
+ path: "/webhook",
1273
+ handler: "stripe.handleWebhook",
672
1274
  config: {
673
- policies: [],
674
- auth: false
1275
+ auth: false,
1276
+ policies: []
1277
+ }
1278
+ },
1279
+ // Payment Intent Operations
1280
+ {
1281
+ method: "POST",
1282
+ path: "/payments/create-intent",
1283
+ handler: "stripe.createPaymentIntent",
1284
+ config: {
1285
+ policies: []
1286
+ }
1287
+ },
1288
+ {
1289
+ method: "POST",
1290
+ path: "/payments/confirm/:id",
1291
+ handler: "stripe.confirmPayment",
1292
+ config: {
1293
+ policies: []
675
1294
  }
676
1295
  },
677
1296
  {
678
1297
  method: "GET",
679
- path: "/products",
680
- handler: "product.find",
1298
+ path: "/payments/:id",
1299
+ handler: "stripe.getPaymentDetails",
681
1300
  config: {
682
- policies: [],
683
- auth: false
1301
+ policies: []
684
1302
  }
685
1303
  },
686
1304
  {
687
1305
  method: "GET",
688
- path: "/subscriptions",
689
- handler: "subscription.find",
1306
+ path: "/payments",
1307
+ handler: "stripe.listPayments",
690
1308
  config: {
691
- policies: [],
692
- auth: false
1309
+ policies: []
693
1310
  }
694
1311
  },
1312
+ // Customer Operations
695
1313
  {
696
1314
  method: "POST",
697
- path: "/config",
698
- handler: "controller.saveConfiguration",
1315
+ path: "/customers",
1316
+ handler: "stripe.createCustomer",
699
1317
  config: {
700
- policies: [],
701
- auth: false
1318
+ policies: []
702
1319
  }
703
1320
  },
704
1321
  {
705
1322
  method: "GET",
706
- path: "/config-status",
707
- handler: "controller.getConfigStatus",
1323
+ path: "/customers/:id",
1324
+ handler: "stripe.retrieveCustomer",
708
1325
  config: {
709
- policies: [],
710
- auth: false
1326
+ policies: []
711
1327
  }
712
1328
  },
713
1329
  {
714
- method: "POST",
715
- path: "/products",
716
- handler: "product.create",
1330
+ method: "GET",
1331
+ path: "/customers",
1332
+ handler: "stripe.listCustomers",
717
1333
  config: {
718
- policies: [],
719
- auth: false
1334
+ policies: []
720
1335
  }
721
1336
  },
1337
+ // Order Operations
722
1338
  {
723
1339
  method: "POST",
724
- path: "/products/:id/sync",
725
- handler: "product.sync",
1340
+ path: "/orders",
1341
+ handler: "stripe.createOrder",
726
1342
  config: {
727
- policies: [],
728
- auth: false
1343
+ policies: []
729
1344
  }
730
1345
  },
731
1346
  {
732
- method: "DELETE",
733
- path: "/products/:id",
734
- handler: "product.delete",
1347
+ method: "GET",
1348
+ path: "/orders/:id",
1349
+ handler: "stripe.getOrderDetails",
735
1350
  config: {
736
- policies: [],
737
- auth: false
1351
+ policies: []
738
1352
  }
739
1353
  },
740
1354
  {
741
- method: "POST",
742
- path: "/subscriptions/:id/sync",
743
- handler: "subscription.sync",
1355
+ method: "GET",
1356
+ path: "/orders",
1357
+ handler: "stripe.listOrders",
744
1358
  config: {
745
- policies: [],
746
- auth: false
1359
+ policies: []
747
1360
  }
748
1361
  },
1362
+ // Refund Operations
749
1363
  {
750
1364
  method: "POST",
751
- path: "/products/import",
752
- handler: "product.importFromStripe",
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",
753
1381
  config: {
754
- policies: [],
755
- auth: false
1382
+ policies: ["admin::isAuthenticatedAdmin"],
1383
+ middlewares: []
756
1384
  }
757
1385
  },
1386
+ // Payment Reports
758
1387
  {
759
- method: "POST",
760
- path: "/subscriptions/:id/sync",
761
- handler: "subscription.sync",
1388
+ method: "GET",
1389
+ path: "/admin/reports",
1390
+ handler: "stripe.getPaymentReports",
762
1391
  config: {
763
- policies: [],
764
- auth: false
1392
+ policies: ["admin::isAuthenticatedAdmin"],
1393
+ middlewares: []
765
1394
  }
766
1395
  },
1396
+ // Configuration Management
767
1397
  {
768
1398
  method: "GET",
769
- path: "/products/stripeProducts",
770
- handler: "product.getStripeProducts",
1399
+ path: "/admin/config",
1400
+ handler: "stripe.getPluginConfig",
771
1401
  config: {
772
- policies: [],
773
- auth: false
1402
+ policies: ["admin::isAuthenticatedAdmin"],
1403
+ middlewares: []
774
1404
  }
775
1405
  },
776
1406
  {
777
- method: "POST",
778
- path: "/test-payment",
779
- handler: "controller.testPayment",
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",
780
1420
  config: {
781
- policies: [],
782
- auth: false
1421
+ policies: ["admin::isAuthenticatedAdmin"],
1422
+ middlewares: []
783
1423
  }
784
1424
  },
1425
+ // Admin Payment Management
785
1426
  {
786
1427
  method: "POST",
787
- path: "/test-payment-with-redirect",
788
- handler: "controller.testPaymentWithRedirect",
1428
+ path: "/admin/payments/:id/refund",
1429
+ handler: "stripe.adminCreateRefund",
789
1430
  config: {
790
- policies: [],
791
- auth: false
1431
+ policies: ["admin::isAuthenticatedAdmin"],
1432
+ middlewares: []
792
1433
  }
793
- }
794
- ]
795
- };
796
- const refundRoutes = {
797
- type: "admin",
798
- routes: [
1434
+ },
1435
+ {
1436
+ method: "GET",
1437
+ path: "/admin/payments",
1438
+ handler: "stripe.adminListPayments",
1439
+ config: {
1440
+ policies: ["admin::isAuthenticatedAdmin"],
1441
+ middlewares: []
1442
+ }
1443
+ },
1444
+ // Admin Customer Management
1445
+ {
1446
+ method: "GET",
1447
+ path: "/admin/customers",
1448
+ handler: "stripe.adminListCustomers",
1449
+ config: {
1450
+ policies: ["admin::isAuthenticatedAdmin"],
1451
+ middlewares: []
1452
+ }
1453
+ },
1454
+ // Admin Order Management
1455
+ {
1456
+ method: "GET",
1457
+ path: "/admin/orders",
1458
+ handler: "stripe.adminListOrders",
1459
+ config: {
1460
+ policies: ["admin::isAuthenticatedAdmin"],
1461
+ middlewares: []
1462
+ }
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
1497
+ "content-api": routes$2,
1498
+ admin: routes$1
841
1499
  };
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
924
- };
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 mapStripeStatusToStrapi = (stripeStatus) => {
1540
+ switch (stripeStatus) {
1541
+ case "succeeded":
1542
+ return "succeeded";
1543
+ case "canceled":
1544
+ return "canceled";
1545
+ case "processing":
1546
+ case "requires_action":
1547
+ case "requires_capture":
1548
+ case "requires_confirmation":
1549
+ case "requires_payment_method":
1550
+ return "pending";
1551
+ default:
1552
+ return "pending";
1553
+ }
1554
+ };
1555
+ const createPaymentIntent = async (params) => {
1556
+ try {
1557
+ const stripe2 = await initializeStripe();
1558
+ const config2 = await getCombinedConfig();
1559
+ const logger = getLogger();
1560
+ const { amount, currency, metadata = {}, customer: customer2, orderId, customerId, payment_method, confirm } = params;
1561
+ const enhancedMetadata = {
1562
+ ...metadata,
1563
+ ...orderId && { strapi_order_id: orderId.toString() },
1564
+ ...customerId && { strapi_customer_id: customerId.toString() },
1565
+ source: "strapi_payment_plugin"
960
1566
  };
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();
1567
+ const paymentIntentData = {
1568
+ amount,
1569
+ currency,
1570
+ metadata: enhancedMetadata,
1571
+ automatic_payment_methods: {
1572
+ enabled: true,
1573
+ ...confirm && { allow_redirects: "never" }
968
1574
  }
969
- transactions = await driver.getTransactions(limit, startingAfter);
970
- } else if (gateway === "paypal") {
971
- console.warn("Paypal is disabled for now");
1575
+ };
1576
+ if (customer2) {
1577
+ paymentIntentData.customer = customer2;
972
1578
  }
973
- if (!transactions) {
974
- return;
1579
+ if (payment_method) {
1580
+ paymentIntentData.payment_method = payment_method;
975
1581
  }
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
- };
1582
+ if (confirm) {
1583
+ paymentIntentData.confirm = confirm;
1584
+ }
1585
+ const paymentIntent = await stripe2.paymentIntents.create(paymentIntentData);
1586
+ logger.info("Payment intent created", {
1587
+ paymentIntentId: paymentIntent.id,
1588
+ amount,
1589
+ currency,
1590
+ orderId,
1591
+ customerId
1003
1592
  });
1004
- return {
1005
- data: formattedTransactions,
1006
- meta: {
1007
- total: formattedTransactions.length,
1008
- limit,
1009
- offset
1593
+ return paymentIntent;
1594
+ } catch (error) {
1595
+ const logger = getLogger();
1596
+ logger.error("Failed to create payment intent", { error, params });
1597
+ throw error;
1598
+ }
1599
+ };
1600
+ const confirmPayment = async (paymentIntentId) => {
1601
+ try {
1602
+ const stripe2 = await initializeStripe();
1603
+ const logger = getLogger();
1604
+ const paymentIntent = await stripe2.paymentIntents.confirm(paymentIntentId);
1605
+ logger.info("Payment confirmed", { paymentIntentId, status: paymentIntent.status });
1606
+ return paymentIntent;
1607
+ } catch (error) {
1608
+ const logger = getLogger();
1609
+ logger.error("Failed to confirm payment", { error, paymentIntentId });
1610
+ throw error;
1611
+ }
1612
+ };
1613
+ const createCustomer = async (params) => {
1614
+ try {
1615
+ const stripe2 = await initializeStripe();
1616
+ const logger = getLogger();
1617
+ const { email, firstName, lastName, phone, address, metadata = {}, strapiCustomerId } = params;
1618
+ const customerData = {
1619
+ email,
1620
+ name: `${firstName} ${lastName}`,
1621
+ phone,
1622
+ address,
1623
+ metadata: {
1624
+ ...metadata,
1625
+ ...strapiCustomerId && { strapi_customer_id: strapiCustomerId.toString() },
1626
+ first_name: firstName,
1627
+ last_name: lastName,
1628
+ source: "strapi_payment_plugin"
1010
1629
  }
1011
1630
  };
1012
- } else {
1013
- throw new Error("Invalid payment gateway");
1631
+ const customer2 = await stripe2.customers.create(customerData);
1632
+ logger.info("Customer created in Stripe", {
1633
+ customerId: customer2.id,
1634
+ email,
1635
+ strapiCustomerId
1636
+ });
1637
+ return customer2;
1638
+ } catch (error) {
1639
+ const logger = getLogger();
1640
+ logger.error("Failed to create customer", { error, params });
1641
+ throw error;
1014
1642
  }
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
- }
1643
+ };
1644
+ const retrieveCustomer = async (customerId) => {
1645
+ try {
1646
+ const stripe2 = await initializeStripe();
1647
+ const logger = getLogger();
1648
+ const customer2 = await stripe2.customers.retrieve(customerId);
1649
+ logger.info("Customer retrieved from Stripe", { customerId });
1650
+ return customer2;
1651
+ } catch (error) {
1652
+ const logger = getLogger();
1653
+ logger.error("Failed to retrieve customer", { error, customerId });
1654
+ throw error;
1049
1655
  }
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);
1656
+ };
1657
+ const createRefund = async (params) => {
1658
+ try {
1659
+ const stripe2 = await initializeStripe();
1660
+ const logger = getLogger();
1661
+ const { paymentIntentId, amount, reason, metadata = {} } = params;
1662
+ const refundData = {
1663
+ payment_intent: paymentIntentId,
1664
+ metadata: {
1665
+ ...metadata,
1666
+ source: "strapi_payment_plugin",
1667
+ refunded_at: (/* @__PURE__ */ new Date()).toISOString()
1668
+ }
1669
+ };
1670
+ if (amount) {
1671
+ refundData.amount = amount;
1055
1672
  }
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);
1673
+ if (reason) {
1674
+ refundData.reason = reason;
1071
1675
  }
1676
+ const refund = await stripe2.refunds.create(refundData);
1677
+ logger.info("Refund processed", {
1678
+ refundId: refund.id,
1679
+ paymentIntentId,
1680
+ amount: refund.amount
1681
+ });
1682
+ return refund;
1683
+ } catch (error) {
1684
+ const logger = getLogger();
1685
+ logger.error("Failed to create refund", { error, params });
1686
+ throw error;
1072
1687
  }
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
- }
1688
+ };
1689
+ const constructEvent = async (payload, signature) => {
1690
+ const stripe2 = await initializeStripe();
1691
+ const config2 = await getCombinedConfig();
1692
+ const stripeConfig = config2.stripe || {};
1693
+ const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET || stripeConfig.webhookSecret;
1694
+ if (!webhookSecret) {
1695
+ throw new Error("Stripe webhook secret not configured. Please set STRIPE_WEBHOOK_SECRET environment variable.");
1079
1696
  }
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
- });
1697
+ try {
1698
+ return stripe2.webhooks.constructEvent(payload, signature, webhookSecret);
1699
+ } catch (error) {
1700
+ const logger = getLogger();
1701
+ logger.error("Failed to construct webhook event", { error });
1702
+ throw error;
1703
+ }
1704
+ };
1705
+ const updateStrapiPayment = async (stripePaymentIntentId, data) => {
1706
+ try {
1707
+ const existingPayments = await strapi2.documents("plugin::payment-plugin.payment").findMany({
1708
+ filters: {
1709
+ stripe_payment_intent_id: stripePaymentIntentId
1106
1710
  }
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
- }
1711
+ });
1712
+ if (existingPayments.length > 0) {
1713
+ await strapi2.documents("plugin::payment-plugin.payment").update({
1714
+ documentId: existingPayments[0].documentId,
1715
+ data
1119
1716
  });
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
1717
  }
1718
+ } catch (error) {
1719
+ const logger = getLogger();
1720
+ logger.error("Failed to update Strapi payment record", {
1721
+ error,
1722
+ stripePaymentIntentId,
1723
+ data
1724
+ });
1130
1725
  }
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
- }
1726
+ };
1727
+ const updateStrapiOrder = async (orderId, data) => {
1728
+ try {
1729
+ await strapi2.documents("plugin::payment-plugin.order").update({
1730
+ documentId: orderId,
1731
+ data
1732
+ });
1733
+ } catch (error) {
1734
+ const logger = getLogger();
1735
+ logger.error("Failed to update Strapi order record", {
1736
+ error,
1737
+ orderId,
1738
+ data
1739
+ });
1148
1740
  }
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
- }
1741
+ };
1742
+ const updateStrapiCustomer = async (customerId, data) => {
1743
+ try {
1744
+ await strapi2.documents("plugin::payment-plugin.customer").update({
1745
+ documentId: customerId,
1746
+ data
1747
+ });
1748
+ } catch (error) {
1749
+ const logger = getLogger();
1750
+ logger.error("Failed to update Strapi customer record", {
1751
+ error,
1752
+ customerId,
1753
+ data
1754
+ });
1160
1755
  }
1161
- return updatedSubscription;
1162
- },
1163
- async cancelSubscription(id) {
1164
- const subscription2 = await strapi2.db.query("plugin::strapi-payment-plugin.subscription").findOne({
1165
- where: { id }
1756
+ };
1757
+ const createCustomerRecord = async (data) => {
1758
+ return strapi2.documents("plugin::payment-plugin.customer").create({
1759
+ data
1166
1760
  });
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 },
1761
+ };
1762
+ const createOrderRecord = async (data) => {
1763
+ return strapi2.documents("plugin::payment-plugin.order").create({
1186
1764
  data: {
1187
- status: "canceled",
1188
- endDate: /* @__PURE__ */ new Date()
1765
+ ...data,
1766
+ status: data.status || "pending"
1189
1767
  }
1190
1768
  });
1191
- },
1192
- async syncSubscriptionStatus(id) {
1193
- const subscription2 = await strapi2.db.query("plugin::strapi-payment-plugin.subscription").findOne({
1194
- where: { id }
1769
+ };
1770
+ const createStrapiPaymentRecord = async (paymentIntent) => {
1771
+ try {
1772
+ const metadata = paymentIntent.metadata || {};
1773
+ const customerId = metadata.strapi_customer_id;
1774
+ const orderId = metadata.strapi_order_id;
1775
+ const paymentData = {
1776
+ stripe_payment_intent_id: paymentIntent.id,
1777
+ amount: paymentIntent.amount / 100,
1778
+ currency: paymentIntent.currency,
1779
+ status: mapStripeStatusToStrapi(paymentIntent.status),
1780
+ payment_method: paymentIntent.payment_method || "card",
1781
+ metadata: paymentIntent.metadata,
1782
+ customer: customerId,
1783
+ order: orderId
1784
+ };
1785
+ const record = await strapi2.documents("plugin::payment-plugin.payment").create({
1786
+ data: paymentData
1787
+ });
1788
+ getLogger().info("Created Strapi payment record", {
1789
+ paymentIntentId: paymentIntent.id,
1790
+ strapiOrderId: orderId,
1791
+ strapiCustomerId: customerId
1792
+ });
1793
+ return record;
1794
+ } catch (error) {
1795
+ const logger = getLogger();
1796
+ logger.error("Failed to create Strapi payment record", {
1797
+ error,
1798
+ paymentIntentId: paymentIntent.id
1799
+ });
1800
+ throw error;
1801
+ }
1802
+ };
1803
+ const initializePaymentFlow = async (params) => {
1804
+ const {
1805
+ amount,
1806
+ currency,
1807
+ email,
1808
+ firstName,
1809
+ lastName,
1810
+ items = [{ name: "Default Item", amount, quantity: 1 }],
1811
+ metadata = {},
1812
+ confirm = false,
1813
+ payment_method,
1814
+ orderNumber = `ORD-${Date.now()}`
1815
+ } = params;
1816
+ const stripeCustomer = await createCustomer({
1817
+ email,
1818
+ firstName,
1819
+ lastName
1820
+ });
1821
+ const customers = await strapi2.documents("plugin::payment-plugin.customer").findMany({
1822
+ filters: { email: { $eq: email } }
1195
1823
  });
1196
- if (!subscription2) {
1197
- throw new Error("Subscription not found");
1824
+ let strapiCustomer;
1825
+ if (customers.length > 0) {
1826
+ strapiCustomer = customers[0];
1827
+ await updateStrapiCustomer(strapiCustomer.documentId, {
1828
+ stripe_customer_id: stripeCustomer.id
1829
+ });
1830
+ } else {
1831
+ strapiCustomer = await createCustomerRecord({
1832
+ email,
1833
+ first_name: firstName,
1834
+ last_name: lastName,
1835
+ stripe_customer_id: stripeCustomer.id
1836
+ });
1198
1837
  }
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
- }
1838
+ const strapiOrder = await createOrderRecord({
1839
+ order_number: orderNumber,
1840
+ total_amount: amount / 100,
1841
+ currency,
1842
+ customer: strapiCustomer.documentId,
1843
+ items,
1844
+ metadata
1845
+ });
1846
+ const paymentIntent = await createPaymentIntent({
1847
+ amount,
1848
+ currency,
1849
+ customerId: strapiCustomer.documentId,
1850
+ orderId: strapiOrder.documentId,
1851
+ customer: stripeCustomer.id,
1852
+ confirm,
1853
+ payment_method,
1854
+ metadata: { ...metadata, flow: "functional_api" }
1855
+ });
1856
+ const strapiPayment = await createStrapiPaymentRecord(paymentIntent);
1857
+ return {
1858
+ payment: strapiPayment,
1859
+ order: strapiOrder,
1860
+ customer: strapiCustomer,
1861
+ stripePaymentIntent: paymentIntent
1862
+ };
1863
+ };
1864
+ const handleWebhook = async (event) => {
1865
+ const logger = getLogger();
1866
+ const config2 = getConfig();
1867
+ const webhookConfig = config2.logging || {};
1868
+ try {
1869
+ if (webhookConfig.enableWebhookLogging) {
1870
+ logger.info("Processing Stripe webhook", {
1871
+ eventType: event.type,
1872
+ eventId: event.id
1209
1873
  });
1210
- } catch (error) {
1211
- console.error("Error syncing subscription status from Stripe:", error);
1212
1874
  }
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
1875
+ switch (event.type) {
1876
+ case "payment_intent.succeeded": {
1877
+ const paymentIntent = event.data.object;
1878
+ try {
1879
+ const existingPayments = await strapi2.documents("plugin::payment-plugin.payment").findMany({
1880
+ filters: {
1881
+ stripe_payment_intent_id: paymentIntent.id
1882
+ }
1883
+ });
1884
+ if (existingPayments.length > 0) {
1885
+ await strapi2.documents("plugin::payment-plugin.payment").update({
1886
+ documentId: existingPayments[0].documentId,
1887
+ data: {
1888
+ status: "succeeded",
1889
+ metadata: paymentIntent.metadata
1890
+ }
1891
+ });
1892
+ } else {
1893
+ await createStrapiPaymentRecord(paymentIntent);
1894
+ }
1895
+ } catch (error) {
1896
+ await createStrapiPaymentRecord(paymentIntent);
1223
1897
  }
1224
- });
1225
- } catch (error) {
1226
- console.error("Error syncing subscription status from PayPal:", error);
1898
+ const orderId = paymentIntent.metadata?.strapi_order_id;
1899
+ if (orderId) {
1900
+ await updateStrapiOrder(orderId, {
1901
+ status: "completed"
1902
+ });
1903
+ }
1904
+ logger.info("Payment succeeded", {
1905
+ paymentIntentId: paymentIntent.id,
1906
+ amount: paymentIntent.amount,
1907
+ orderId
1908
+ });
1909
+ break;
1910
+ }
1911
+ case "payment_intent.payment_failed": {
1912
+ const paymentIntent = event.data.object;
1913
+ await updateStrapiPayment(paymentIntent.id, {
1914
+ status: "failed",
1915
+ metadata: paymentIntent.metadata
1916
+ });
1917
+ const orderId = paymentIntent.metadata?.strapi_order_id;
1918
+ if (orderId) {
1919
+ await updateStrapiOrder(orderId, {
1920
+ status: "failed"
1921
+ });
1922
+ }
1923
+ logger.warn("Payment failed", {
1924
+ paymentIntentId: paymentIntent.id,
1925
+ orderId,
1926
+ lastError: paymentIntent.last_payment_error
1927
+ });
1928
+ break;
1929
+ }
1930
+ case "payment_intent.canceled": {
1931
+ const paymentIntent = event.data.object;
1932
+ await updateStrapiPayment(paymentIntent.id, {
1933
+ status: "canceled",
1934
+ metadata: paymentIntent.metadata
1935
+ });
1936
+ const orderId = paymentIntent.metadata?.strapi_order_id;
1937
+ if (orderId) {
1938
+ await updateStrapiOrder(orderId, {
1939
+ status: "failed"
1940
+ });
1941
+ }
1942
+ logger.info("Payment canceled", {
1943
+ paymentIntentId: paymentIntent.id,
1944
+ orderId
1945
+ });
1946
+ break;
1947
+ }
1948
+ case "charge.refunded": {
1949
+ const charge = event.data.object;
1950
+ if (charge.payment_intent) {
1951
+ await updateStrapiPayment(charge.payment_intent, {
1952
+ status: "refunded"
1953
+ });
1954
+ const orderId = charge.metadata?.strapi_order_id;
1955
+ if (orderId) {
1956
+ await updateStrapiOrder(orderId, {
1957
+ status: "refunded"
1958
+ });
1959
+ }
1960
+ }
1961
+ logger.info("Charge refunded", {
1962
+ chargeId: charge.id,
1963
+ paymentIntentId: charge.payment_intent,
1964
+ amount: charge.amount_refunded,
1965
+ orderId: charge.metadata?.strapi_order_id
1966
+ });
1967
+ break;
1968
+ }
1969
+ case "customer.created": {
1970
+ const customer2 = event.data.object;
1971
+ const strapiCustomerId = customer2.metadata?.strapi_customer_id;
1972
+ if (strapiCustomerId) {
1973
+ await updateStrapiCustomer(strapiCustomerId, {
1974
+ stripe_customer_id: customer2.id,
1975
+ metadata: customer2.metadata
1976
+ });
1977
+ }
1978
+ logger.info("Customer created in Stripe", {
1979
+ stripeCustomerId: customer2.id,
1980
+ strapiCustomerId
1981
+ });
1982
+ break;
1983
+ }
1984
+ default:
1985
+ logger.info("Unhandled webhook event type", { eventType: event.type });
1227
1986
  }
1987
+ } catch (error) {
1988
+ logger.error("Error processing webhook", {
1989
+ error,
1990
+ eventType: event.type,
1991
+ eventId: event.id
1992
+ });
1993
+ throw error;
1228
1994
  }
1229
- return strapi2.db.query("plugin::strapi-payment-plugin.subscription").findOne({
1230
- where: { id }
1231
- });
1232
- }
1233
- }));
1995
+ };
1996
+ return {
1997
+ initializeStripe,
1998
+ createPaymentIntent,
1999
+ confirmPayment,
2000
+ createCustomer,
2001
+ retrieveCustomer,
2002
+ createRefund,
2003
+ handleWebhook,
2004
+ constructEvent,
2005
+ updateStrapiOrder,
2006
+ updateStrapiCustomer,
2007
+ createStrapiPaymentRecord,
2008
+ createCustomerRecord,
2009
+ createOrderRecord,
2010
+ initializePaymentFlow,
2011
+ getStripe
2012
+ };
2013
+ };
1234
2014
  const services = {
1235
- service,
1236
- product,
1237
- subscription
2015
+ stripe: stripeService
1238
2016
  };
1239
2017
  const index = {
1240
2018
  register,