@reeboot/strapi-payment-plugin 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/{Analytics-kChERzX4.mjs → Analytics-CPXtqS6s.mjs} +1 -1
- package/dist/_chunks/{Analytics-DjXqUZy1.js → Analytics-ZpHHWBQt.js} +1 -1
- package/dist/_chunks/{App-D5x-l0Sz.mjs → App-CZNZ1JKf.mjs} +13 -7
- package/dist/_chunks/{App-BpwS5-wg.js → App-CsbyykNC.js} +13 -7
- package/dist/_chunks/{Customers-ChrS373z.js → Customers-D4RVSZ_m.js} +1 -1
- package/dist/_chunks/{Customers-BYQZcZeb.mjs → Customers-DMDEBpH9.mjs} +1 -1
- package/dist/_chunks/{Dashboard-DUWxJQ7q.mjs → Dashboard-D-ckyWDO.mjs} +1 -1
- package/dist/_chunks/{Dashboard-DANhyMZK.js → Dashboard-NNqGJmYw.js} +1 -1
- package/dist/_chunks/{Orders-BXyc3CTW.js → Orders-BELfgToU.js} +1 -1
- package/dist/_chunks/{Orders-B3GZULs3.mjs → Orders-BIkRttRZ.mjs} +1 -1
- package/dist/_chunks/{Payments-DmAzroEv.js → Payments-CIEb4f07.js} +1 -1
- package/dist/_chunks/{Payments-DL6hne_y.mjs → Payments-CKlL-PCV.mjs} +1 -1
- package/dist/_chunks/{Settings-Ck1G0iaS.mjs → Settings-BQXkYm-c.mjs} +1 -1
- package/dist/_chunks/{Settings-CMEzIMqt.js → Settings-CcY98Hyw.js} +1 -1
- package/dist/_chunks/Subscriptions--cI3HC2x.js +178 -0
- package/dist/_chunks/Subscriptions-Dq8Uk7sK.mjs +178 -0
- package/dist/_chunks/{index-Bo7VLX9i.mjs → index-BBarHYyt.mjs} +1 -1
- package/dist/_chunks/{index-Cmn7Tfmf.js → index-DBZ4rcUW.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/index.d.ts +22 -1
- package/dist/admin/src/pages/Subscriptions.d.ts +2 -0
- package/dist/admin/src/types/index.d.ts +20 -0
- package/dist/server/index.js +622 -46
- package/dist/server/index.mjs +622 -46
- package/dist/server/src/__tests__/setup.d.ts +316 -0
- package/dist/server/src/content-types/customer/index.d.ts +6 -0
- package/dist/server/src/content-types/index.d.ts +89 -0
- package/dist/server/src/content-types/subscription/index.d.ts +84 -0
- package/dist/server/src/controllers/index.d.ts +7 -0
- package/dist/server/src/controllers/stripe.d.ts +34 -2
- package/dist/server/src/index.d.ts +99 -6
- package/dist/server/src/middlewares/index.d.ts +3 -3
- package/dist/server/src/services/stripe.d.ts +50 -10
- package/dist/server/src/services/types/api.d.ts +24 -5
- package/dist/server/src/types/plugin-config.d.ts +360 -0
- package/package.json +6 -3
package/dist/server/index.js
CHANGED
|
@@ -13,7 +13,7 @@ const config = {
|
|
|
13
13
|
validator() {
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
-
const schema$
|
|
16
|
+
const schema$3 = {
|
|
17
17
|
kind: "collectionType",
|
|
18
18
|
collectionName: "customers",
|
|
19
19
|
info: {
|
|
@@ -76,11 +76,17 @@ const schema$2 = {
|
|
|
76
76
|
relation: "oneToMany",
|
|
77
77
|
target: "plugin::payment-plugin.payment",
|
|
78
78
|
mappedBy: "customer"
|
|
79
|
+
},
|
|
80
|
+
subscriptions: {
|
|
81
|
+
type: "relation",
|
|
82
|
+
relation: "oneToMany",
|
|
83
|
+
target: "plugin::payment-plugin.subscription",
|
|
84
|
+
mappedBy: "customer"
|
|
79
85
|
}
|
|
80
86
|
}
|
|
81
87
|
};
|
|
82
|
-
const customer = { schema: schema$
|
|
83
|
-
const schema$
|
|
88
|
+
const customer = { schema: schema$3 };
|
|
89
|
+
const schema$2 = {
|
|
84
90
|
kind: "collectionType",
|
|
85
91
|
collectionName: "orders",
|
|
86
92
|
info: {
|
|
@@ -165,8 +171,8 @@ const schema$1 = {
|
|
|
165
171
|
}
|
|
166
172
|
}
|
|
167
173
|
};
|
|
168
|
-
const order = { schema: schema$
|
|
169
|
-
const schema = {
|
|
174
|
+
const order = { schema: schema$2 };
|
|
175
|
+
const schema$1 = {
|
|
170
176
|
kind: "collectionType",
|
|
171
177
|
collectionName: "payments",
|
|
172
178
|
info: {
|
|
@@ -238,17 +244,115 @@ const schema = {
|
|
|
238
244
|
}
|
|
239
245
|
}
|
|
240
246
|
};
|
|
241
|
-
const payment = { schema };
|
|
247
|
+
const payment = { schema: schema$1 };
|
|
248
|
+
const kind = "collectionType";
|
|
249
|
+
const collectionName = "payment_plugin_subscriptions";
|
|
250
|
+
const info = {
|
|
251
|
+
singularName: "subscription",
|
|
252
|
+
pluralName: "subscriptions",
|
|
253
|
+
displayName: "Subscription",
|
|
254
|
+
description: "Stripe subscription records for recurring billing"
|
|
255
|
+
};
|
|
256
|
+
const options = {
|
|
257
|
+
draftAndPublish: false
|
|
258
|
+
};
|
|
259
|
+
const attributes = {
|
|
260
|
+
stripe_subscription_id: {
|
|
261
|
+
type: "string",
|
|
262
|
+
unique: true,
|
|
263
|
+
required: true,
|
|
264
|
+
configurable: false
|
|
265
|
+
},
|
|
266
|
+
stripe_customer_id: {
|
|
267
|
+
type: "string",
|
|
268
|
+
required: true,
|
|
269
|
+
configurable: false
|
|
270
|
+
},
|
|
271
|
+
stripe_price_id: {
|
|
272
|
+
type: "string",
|
|
273
|
+
required: true,
|
|
274
|
+
configurable: false
|
|
275
|
+
},
|
|
276
|
+
stripe_product_id: {
|
|
277
|
+
type: "string",
|
|
278
|
+
configurable: false
|
|
279
|
+
},
|
|
280
|
+
status: {
|
|
281
|
+
type: "enumeration",
|
|
282
|
+
"enum": [
|
|
283
|
+
"active",
|
|
284
|
+
"canceled",
|
|
285
|
+
"incomplete",
|
|
286
|
+
"incomplete_expired",
|
|
287
|
+
"past_due",
|
|
288
|
+
"paused",
|
|
289
|
+
"trialing",
|
|
290
|
+
"unpaid"
|
|
291
|
+
],
|
|
292
|
+
required: true,
|
|
293
|
+
"default": "incomplete",
|
|
294
|
+
configurable: false
|
|
295
|
+
},
|
|
296
|
+
current_period_start: {
|
|
297
|
+
type: "datetime",
|
|
298
|
+
configurable: false
|
|
299
|
+
},
|
|
300
|
+
current_period_end: {
|
|
301
|
+
type: "datetime",
|
|
302
|
+
configurable: false
|
|
303
|
+
},
|
|
304
|
+
cancel_at_period_end: {
|
|
305
|
+
type: "boolean",
|
|
306
|
+
"default": false,
|
|
307
|
+
configurable: false
|
|
308
|
+
},
|
|
309
|
+
canceled_at: {
|
|
310
|
+
type: "datetime",
|
|
311
|
+
configurable: false
|
|
312
|
+
},
|
|
313
|
+
trial_start: {
|
|
314
|
+
type: "datetime",
|
|
315
|
+
configurable: false
|
|
316
|
+
},
|
|
317
|
+
trial_end: {
|
|
318
|
+
type: "datetime",
|
|
319
|
+
configurable: false
|
|
320
|
+
},
|
|
321
|
+
latest_invoice_id: {
|
|
322
|
+
type: "string",
|
|
323
|
+
configurable: false
|
|
324
|
+
},
|
|
325
|
+
metadata: {
|
|
326
|
+
type: "json",
|
|
327
|
+
configurable: false
|
|
328
|
+
},
|
|
329
|
+
customer: {
|
|
330
|
+
type: "relation",
|
|
331
|
+
relation: "manyToOne",
|
|
332
|
+
target: "plugin::payment-plugin.customer",
|
|
333
|
+
inversedBy: "subscriptions"
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
const schema = {
|
|
337
|
+
kind,
|
|
338
|
+
collectionName,
|
|
339
|
+
info,
|
|
340
|
+
options,
|
|
341
|
+
attributes
|
|
342
|
+
};
|
|
343
|
+
const subscription = { schema };
|
|
242
344
|
const contentTypes = {
|
|
243
345
|
customer,
|
|
244
346
|
order,
|
|
245
|
-
payment
|
|
347
|
+
payment,
|
|
348
|
+
subscription
|
|
246
349
|
};
|
|
247
350
|
const controller = ({ strapi: strapi2 }) => ({
|
|
248
351
|
index(ctx) {
|
|
249
352
|
ctx.body = strapi2.plugin("payment-plugin").service("service").getWelcomeMessage();
|
|
250
353
|
}
|
|
251
354
|
});
|
|
355
|
+
const getErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
252
356
|
const stripeController = {
|
|
253
357
|
/**
|
|
254
358
|
* Create a payment intent
|
|
@@ -419,12 +523,13 @@ const stripeController = {
|
|
|
419
523
|
await stripeService2.handleWebhook(event);
|
|
420
524
|
ctx.body = { received: true };
|
|
421
525
|
} catch (error) {
|
|
422
|
-
|
|
526
|
+
const message = getErrorMessage(error);
|
|
527
|
+
if (message.includes("No signatures found matching") || message.includes("Webhook payload must be provided as a string or a Buffer")) {
|
|
423
528
|
strapi.log.error('Stripe Webhook Error: Payload is not raw. Ensure "includeUnparsed: true" is set in config/middlewares.ts for "strapi::body".');
|
|
424
529
|
return ctx.badRequest("Webhook verification failed: Payload must be raw. Check your Strapi configuration for strapi::body middleware.");
|
|
425
530
|
}
|
|
426
|
-
strapi.log.error("Failed to handle webhook", {
|
|
427
|
-
ctx.badRequest(`Webhook Error: ${
|
|
531
|
+
strapi.log.error("Failed to handle webhook", { message, stack: error instanceof Error ? error.stack : void 0 });
|
|
532
|
+
ctx.badRequest(`Webhook Error: ${message}`);
|
|
428
533
|
}
|
|
429
534
|
},
|
|
430
535
|
/**
|
|
@@ -782,24 +887,28 @@ const stripeController = {
|
|
|
782
887
|
const { startDate, endDate, format = "json" } = ctx.query;
|
|
783
888
|
const filters = {};
|
|
784
889
|
if (startDate || endDate) {
|
|
785
|
-
|
|
786
|
-
if (startDate)
|
|
787
|
-
if (endDate)
|
|
890
|
+
const dateFilter = {};
|
|
891
|
+
if (startDate) dateFilter.$gte = new Date(startDate).toISOString();
|
|
892
|
+
if (endDate) dateFilter.$lte = new Date(endDate).toISOString();
|
|
893
|
+
filters.createdAt = dateFilter;
|
|
788
894
|
}
|
|
789
|
-
const
|
|
895
|
+
const rawPayments = await strapi.documents("plugin::payment-plugin.payment").findMany({
|
|
790
896
|
filters,
|
|
791
897
|
populate: ["customer", "order"],
|
|
792
898
|
sort: { createdAt: "desc" }
|
|
793
899
|
});
|
|
900
|
+
const payments = rawPayments;
|
|
794
901
|
const summary = {
|
|
795
902
|
totalPayments: payments.length,
|
|
796
903
|
totalAmount: payments.reduce((sum, p) => sum + p.amount, 0),
|
|
797
904
|
byStatus: payments.reduce((acc, p) => {
|
|
798
|
-
|
|
905
|
+
const s = p.payment_status || "unknown";
|
|
906
|
+
acc[s] = (acc[s] || 0) + 1;
|
|
799
907
|
return acc;
|
|
800
908
|
}, {}),
|
|
801
909
|
byCurrency: payments.reduce((acc, p) => {
|
|
802
|
-
|
|
910
|
+
const c = p.currency || "unknown";
|
|
911
|
+
acc[c] = (acc[c] || 0) + 1;
|
|
803
912
|
return acc;
|
|
804
913
|
}, {})
|
|
805
914
|
};
|
|
@@ -890,8 +999,9 @@ const stripeController = {
|
|
|
890
999
|
fields: ["amount", "createdAt", "currency"],
|
|
891
1000
|
sort: { createdAt: "asc" }
|
|
892
1001
|
});
|
|
893
|
-
const
|
|
894
|
-
|
|
1002
|
+
const typedPayments = payments;
|
|
1003
|
+
const dailyStats = typedPayments.reduce((acc, payment2) => {
|
|
1004
|
+
const date = new Date(payment2.createdAt || "").toISOString().split("T")[0];
|
|
895
1005
|
if (!acc[date]) {
|
|
896
1006
|
acc[date] = { count: 0, amount: 0, currency: payment2.currency };
|
|
897
1007
|
}
|
|
@@ -899,14 +1009,15 @@ const stripeController = {
|
|
|
899
1009
|
acc[date].amount += payment2.amount;
|
|
900
1010
|
return acc;
|
|
901
1011
|
}, {});
|
|
1012
|
+
const totalVolume = typedPayments.reduce((sum, p) => sum + p.amount, 0);
|
|
902
1013
|
ctx.body = {
|
|
903
1014
|
success: true,
|
|
904
1015
|
data: {
|
|
905
1016
|
period,
|
|
906
1017
|
dailyStats,
|
|
907
|
-
totalVolume
|
|
908
|
-
totalTransactions:
|
|
909
|
-
averageTransaction:
|
|
1018
|
+
totalVolume,
|
|
1019
|
+
totalTransactions: typedPayments.length,
|
|
1020
|
+
averageTransaction: typedPayments.length > 0 ? totalVolume / typedPayments.length : 0
|
|
910
1021
|
}
|
|
911
1022
|
};
|
|
912
1023
|
} catch (error) {
|
|
@@ -968,12 +1079,11 @@ const stripeController = {
|
|
|
968
1079
|
};
|
|
969
1080
|
} catch (error) {
|
|
970
1081
|
strapi.log.error("Failed to create admin refund:", {
|
|
971
|
-
message: error
|
|
972
|
-
stack: error.stack,
|
|
973
|
-
...error
|
|
1082
|
+
message: getErrorMessage(error),
|
|
1083
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
1084
|
+
...error instanceof Error && "raw" in error && { stripeError: error.raw }
|
|
974
1085
|
});
|
|
975
|
-
|
|
976
|
-
ctx.internalServerError(errorMessage);
|
|
1086
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to create admin refund");
|
|
977
1087
|
}
|
|
978
1088
|
},
|
|
979
1089
|
/**
|
|
@@ -1225,9 +1335,207 @@ const stripeController = {
|
|
|
1225
1335
|
}
|
|
1226
1336
|
}
|
|
1227
1337
|
};
|
|
1338
|
+
const subscriptionController = {
|
|
1339
|
+
/**
|
|
1340
|
+
* POST /subscriptions
|
|
1341
|
+
* Body: { stripeCustomerId, priceId, trialPeriodDays?, metadata?, defaultPaymentMethod? }
|
|
1342
|
+
*/
|
|
1343
|
+
async createSubscription(ctx) {
|
|
1344
|
+
try {
|
|
1345
|
+
const { stripeCustomerId, priceId, trialPeriodDays, metadata, defaultPaymentMethod } = ctx.request.body;
|
|
1346
|
+
if (!stripeCustomerId || !priceId) {
|
|
1347
|
+
return ctx.badRequest("stripeCustomerId and priceId are required");
|
|
1348
|
+
}
|
|
1349
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1350
|
+
const customers = await strapi.documents("plugin::payment-plugin.customer").findMany({
|
|
1351
|
+
filters: { stripe_customer_id: { $eq: stripeCustomerId } }
|
|
1352
|
+
});
|
|
1353
|
+
const strapiCustomerId = customers.length > 0 ? customers[0].documentId : void 0;
|
|
1354
|
+
const subscription2 = await stripeService2.createSubscription({
|
|
1355
|
+
strapiCustomerId: strapiCustomerId || "",
|
|
1356
|
+
stripeCustomerId,
|
|
1357
|
+
priceId,
|
|
1358
|
+
trialPeriodDays,
|
|
1359
|
+
metadata,
|
|
1360
|
+
defaultPaymentMethod
|
|
1361
|
+
});
|
|
1362
|
+
const invoice = typeof subscription2.latest_invoice === "object" ? subscription2.latest_invoice : null;
|
|
1363
|
+
const invoiceExt = invoice;
|
|
1364
|
+
const paymentIntent = typeof invoiceExt?.payment_intent === "object" ? invoiceExt.payment_intent : null;
|
|
1365
|
+
const setupIntent = typeof subscription2.pending_setup_intent === "object" ? subscription2.pending_setup_intent : null;
|
|
1366
|
+
ctx.body = {
|
|
1367
|
+
subscriptionId: subscription2.id,
|
|
1368
|
+
status: subscription2.status,
|
|
1369
|
+
clientSecret: paymentIntent?.client_secret || setupIntent?.client_secret || null,
|
|
1370
|
+
currentPeriodEnd: subscription2.current_period_end
|
|
1371
|
+
};
|
|
1372
|
+
} catch (error) {
|
|
1373
|
+
strapi.log.error("Failed to create subscription", { error: getErrorMessage(error) });
|
|
1374
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to create subscription");
|
|
1375
|
+
}
|
|
1376
|
+
},
|
|
1377
|
+
/**
|
|
1378
|
+
* POST /subscriptions/:id/cancel
|
|
1379
|
+
* Body: { cancelAtPeriodEnd? }
|
|
1380
|
+
*/
|
|
1381
|
+
async cancelSubscription(ctx) {
|
|
1382
|
+
try {
|
|
1383
|
+
const { id } = ctx.params;
|
|
1384
|
+
const { cancelAtPeriodEnd = false } = ctx.request.body || {};
|
|
1385
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1386
|
+
const subscription2 = await stripeService2.cancelSubscription(id, cancelAtPeriodEnd);
|
|
1387
|
+
ctx.body = {
|
|
1388
|
+
subscriptionId: subscription2.id,
|
|
1389
|
+
status: subscription2.status,
|
|
1390
|
+
cancelAtPeriodEnd: subscription2.cancel_at_period_end,
|
|
1391
|
+
canceledAt: subscription2.canceled_at
|
|
1392
|
+
};
|
|
1393
|
+
} catch (error) {
|
|
1394
|
+
strapi.log.error("Failed to cancel subscription", { error: getErrorMessage(error) });
|
|
1395
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to cancel subscription");
|
|
1396
|
+
}
|
|
1397
|
+
},
|
|
1398
|
+
/**
|
|
1399
|
+
* PUT /subscriptions/:id
|
|
1400
|
+
* Body: { priceId?, cancelAtPeriodEnd?, trialEnd?, metadata?, defaultPaymentMethod? }
|
|
1401
|
+
*/
|
|
1402
|
+
async updateSubscription(ctx) {
|
|
1403
|
+
try {
|
|
1404
|
+
const { id } = ctx.params;
|
|
1405
|
+
const params = ctx.request.body || {};
|
|
1406
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1407
|
+
const subscription2 = await stripeService2.updateSubscription(id, params);
|
|
1408
|
+
ctx.body = {
|
|
1409
|
+
subscriptionId: subscription2.id,
|
|
1410
|
+
status: subscription2.status,
|
|
1411
|
+
cancelAtPeriodEnd: subscription2.cancel_at_period_end,
|
|
1412
|
+
currentPeriodEnd: subscription2.current_period_end
|
|
1413
|
+
};
|
|
1414
|
+
} catch (error) {
|
|
1415
|
+
strapi.log.error("Failed to update subscription", { error: getErrorMessage(error) });
|
|
1416
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to update subscription");
|
|
1417
|
+
}
|
|
1418
|
+
},
|
|
1419
|
+
/**
|
|
1420
|
+
* GET /subscriptions/:id
|
|
1421
|
+
*/
|
|
1422
|
+
async getSubscriptionDetails(ctx) {
|
|
1423
|
+
try {
|
|
1424
|
+
const { id } = ctx.params;
|
|
1425
|
+
const records = await strapi.documents("plugin::payment-plugin.subscription").findMany({
|
|
1426
|
+
filters: { stripe_subscription_id: { $eq: id } },
|
|
1427
|
+
populate: ["customer"]
|
|
1428
|
+
});
|
|
1429
|
+
if (records.length === 0) {
|
|
1430
|
+
return ctx.notFound("Subscription not found");
|
|
1431
|
+
}
|
|
1432
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1433
|
+
const stripeSubscription = await stripeService2.retrieveSubscription(id);
|
|
1434
|
+
ctx.body = {
|
|
1435
|
+
strapi: records[0],
|
|
1436
|
+
stripe: stripeSubscription
|
|
1437
|
+
};
|
|
1438
|
+
} catch (error) {
|
|
1439
|
+
strapi.log.error("Failed to get subscription", { error: getErrorMessage(error) });
|
|
1440
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to get subscription");
|
|
1441
|
+
}
|
|
1442
|
+
},
|
|
1443
|
+
/**
|
|
1444
|
+
* GET /subscriptions
|
|
1445
|
+
* Query: page, pageSize, status, stripeCustomerId
|
|
1446
|
+
*/
|
|
1447
|
+
async listSubscriptions(ctx) {
|
|
1448
|
+
try {
|
|
1449
|
+
const { page = 1, pageSize = 25, status, stripeCustomerId } = ctx.query;
|
|
1450
|
+
const filters = {};
|
|
1451
|
+
if (status) filters.status = { $eq: status };
|
|
1452
|
+
if (stripeCustomerId) filters.stripe_customer_id = { $eq: stripeCustomerId };
|
|
1453
|
+
const subscriptions = await strapi.documents("plugin::payment-plugin.subscription").findMany({
|
|
1454
|
+
filters,
|
|
1455
|
+
populate: ["customer"],
|
|
1456
|
+
sort: { createdAt: "desc" },
|
|
1457
|
+
start: (Number(page) - 1) * Number(pageSize),
|
|
1458
|
+
limit: Number(pageSize)
|
|
1459
|
+
});
|
|
1460
|
+
const total = await strapi.documents("plugin::payment-plugin.subscription").count({ filters });
|
|
1461
|
+
ctx.body = {
|
|
1462
|
+
data: subscriptions,
|
|
1463
|
+
meta: {
|
|
1464
|
+
pagination: {
|
|
1465
|
+
page: Number(page),
|
|
1466
|
+
pageSize: Number(pageSize),
|
|
1467
|
+
total,
|
|
1468
|
+
pageCount: Math.ceil(total / Number(pageSize))
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
} catch (error) {
|
|
1473
|
+
strapi.log.error("Failed to list subscriptions", { error: getErrorMessage(error) });
|
|
1474
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to list subscriptions");
|
|
1475
|
+
}
|
|
1476
|
+
},
|
|
1477
|
+
/**
|
|
1478
|
+
* GET /admin/subscriptions (admin only)
|
|
1479
|
+
*/
|
|
1480
|
+
async adminListSubscriptions(ctx) {
|
|
1481
|
+
try {
|
|
1482
|
+
const { page = 1, pageSize = 25, status } = ctx.query;
|
|
1483
|
+
const filters = {};
|
|
1484
|
+
if (status) filters.status = { $eq: status };
|
|
1485
|
+
const subscriptions = await strapi.documents("plugin::payment-plugin.subscription").findMany({
|
|
1486
|
+
filters,
|
|
1487
|
+
populate: ["customer"],
|
|
1488
|
+
sort: { createdAt: "desc" },
|
|
1489
|
+
start: (Number(page) - 1) * Number(pageSize),
|
|
1490
|
+
limit: Number(pageSize)
|
|
1491
|
+
});
|
|
1492
|
+
const total = await strapi.documents("plugin::payment-plugin.subscription").count({ filters });
|
|
1493
|
+
const enriched = subscriptions.map((s) => ({
|
|
1494
|
+
...s,
|
|
1495
|
+
customerEmail: s.customer?.email || "",
|
|
1496
|
+
customerName: s.customer ? `${s.customer.first_name} ${s.customer.last_name}` : ""
|
|
1497
|
+
}));
|
|
1498
|
+
ctx.body = {
|
|
1499
|
+
data: enriched,
|
|
1500
|
+
meta: {
|
|
1501
|
+
pagination: {
|
|
1502
|
+
page: Number(page),
|
|
1503
|
+
pageSize: Number(pageSize),
|
|
1504
|
+
total,
|
|
1505
|
+
pageCount: Math.ceil(total / Number(pageSize))
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
strapi.log.error("Failed to list subscriptions (admin)", { error: getErrorMessage(error) });
|
|
1511
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to list subscriptions");
|
|
1512
|
+
}
|
|
1513
|
+
},
|
|
1514
|
+
/**
|
|
1515
|
+
* POST /admin/subscriptions/:id/cancel (admin only)
|
|
1516
|
+
*/
|
|
1517
|
+
async adminCancelSubscription(ctx) {
|
|
1518
|
+
try {
|
|
1519
|
+
const { id } = ctx.params;
|
|
1520
|
+
const { cancelAtPeriodEnd = false } = ctx.request.body || {};
|
|
1521
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1522
|
+
const subscription2 = await stripeService2.cancelSubscription(id, cancelAtPeriodEnd);
|
|
1523
|
+
ctx.body = {
|
|
1524
|
+
success: true,
|
|
1525
|
+
subscriptionId: subscription2.id,
|
|
1526
|
+
status: subscription2.status,
|
|
1527
|
+
cancelAtPeriodEnd: subscription2.cancel_at_period_end
|
|
1528
|
+
};
|
|
1529
|
+
} catch (error) {
|
|
1530
|
+
strapi.log.error("Failed to cancel subscription (admin)", { error: getErrorMessage(error) });
|
|
1531
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to cancel subscription");
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
};
|
|
1535
|
+
const stripeControllerFull = { ...stripeController, ...subscriptionController };
|
|
1228
1536
|
const controllers = {
|
|
1229
1537
|
controller,
|
|
1230
|
-
stripe:
|
|
1538
|
+
stripe: stripeControllerFull
|
|
1231
1539
|
};
|
|
1232
1540
|
const middlewares = {
|
|
1233
1541
|
/**
|
|
@@ -1426,6 +1734,37 @@ const routes$2 = {
|
|
|
1426
1734
|
config: {
|
|
1427
1735
|
policies: []
|
|
1428
1736
|
}
|
|
1737
|
+
},
|
|
1738
|
+
// Subscription Operations
|
|
1739
|
+
{
|
|
1740
|
+
method: "POST",
|
|
1741
|
+
path: "/subscriptions",
|
|
1742
|
+
handler: "stripe.createSubscription",
|
|
1743
|
+
config: { policies: [] }
|
|
1744
|
+
},
|
|
1745
|
+
{
|
|
1746
|
+
method: "GET",
|
|
1747
|
+
path: "/subscriptions",
|
|
1748
|
+
handler: "stripe.listSubscriptions",
|
|
1749
|
+
config: { policies: [] }
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
method: "GET",
|
|
1753
|
+
path: "/subscriptions/:id",
|
|
1754
|
+
handler: "stripe.getSubscriptionDetails",
|
|
1755
|
+
config: { policies: [] }
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
method: "PUT",
|
|
1759
|
+
path: "/subscriptions/:id",
|
|
1760
|
+
handler: "stripe.updateSubscription",
|
|
1761
|
+
config: { policies: [] }
|
|
1762
|
+
},
|
|
1763
|
+
{
|
|
1764
|
+
method: "POST",
|
|
1765
|
+
path: "/subscriptions/:id/cancel",
|
|
1766
|
+
handler: "stripe.cancelSubscription",
|
|
1767
|
+
config: { policies: [] }
|
|
1429
1768
|
}
|
|
1430
1769
|
]
|
|
1431
1770
|
};
|
|
@@ -1549,6 +1888,25 @@ const routes$1 = {
|
|
|
1549
1888
|
policies: ["admin::isAuthenticatedAdmin"],
|
|
1550
1889
|
middlewares: []
|
|
1551
1890
|
}
|
|
1891
|
+
},
|
|
1892
|
+
// Admin Subscription Management
|
|
1893
|
+
{
|
|
1894
|
+
method: "GET",
|
|
1895
|
+
path: "/admin/subscriptions",
|
|
1896
|
+
handler: "stripe.adminListSubscriptions",
|
|
1897
|
+
config: {
|
|
1898
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
1899
|
+
middlewares: []
|
|
1900
|
+
}
|
|
1901
|
+
},
|
|
1902
|
+
{
|
|
1903
|
+
method: "POST",
|
|
1904
|
+
path: "/admin/subscriptions/:id/cancel",
|
|
1905
|
+
handler: "stripe.adminCancelSubscription",
|
|
1906
|
+
config: {
|
|
1907
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
1908
|
+
middlewares: []
|
|
1909
|
+
}
|
|
1552
1910
|
}
|
|
1553
1911
|
]
|
|
1554
1912
|
};
|
|
@@ -1595,6 +1953,9 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1595
1953
|
const getStripe = () => {
|
|
1596
1954
|
return stripe;
|
|
1597
1955
|
};
|
|
1956
|
+
const setStripe = (instance) => {
|
|
1957
|
+
stripe = instance;
|
|
1958
|
+
};
|
|
1598
1959
|
const mapStripeStatusToStrapi = (stripeStatus) => {
|
|
1599
1960
|
switch (stripeStatus) {
|
|
1600
1961
|
case "succeeded":
|
|
@@ -1752,14 +2113,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1752
2113
|
return refund;
|
|
1753
2114
|
} catch (error) {
|
|
1754
2115
|
const logger = getLogger();
|
|
1755
|
-
const errorMessage = error
|
|
2116
|
+
const errorMessage = getErrorMessage(error);
|
|
1756
2117
|
logger.error("Failed to create Stripe refund", {
|
|
1757
2118
|
message: errorMessage,
|
|
1758
2119
|
error: error instanceof Error ? {
|
|
1759
2120
|
message: error.message,
|
|
1760
2121
|
stack: error.stack,
|
|
1761
|
-
...
|
|
1762
|
-
} : error,
|
|
2122
|
+
..."raw" in error && { raw: error.raw }
|
|
2123
|
+
} : String(error),
|
|
1763
2124
|
params
|
|
1764
2125
|
});
|
|
1765
2126
|
throw error;
|
|
@@ -1782,8 +2143,8 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1782
2143
|
return stripe2.webhooks.constructEvent(verifiedPayload, signature, webhookSecret);
|
|
1783
2144
|
} catch (error) {
|
|
1784
2145
|
logger.error("Failed to construct webhook event", {
|
|
1785
|
-
message: error
|
|
1786
|
-
type: error.type
|
|
2146
|
+
message: getErrorMessage(error),
|
|
2147
|
+
...error instanceof Error && "type" in error && { type: error.type }
|
|
1787
2148
|
});
|
|
1788
2149
|
throw error;
|
|
1789
2150
|
}
|
|
@@ -1856,10 +2217,7 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1856
2217
|
};
|
|
1857
2218
|
const createOrderRecord = async (data) => {
|
|
1858
2219
|
return strapi2.documents("plugin::payment-plugin.order").create({
|
|
1859
|
-
data: {
|
|
1860
|
-
...data,
|
|
1861
|
-
order_status: data.order_status || "pending"
|
|
1862
|
-
}
|
|
2220
|
+
data: { ...data, order_status: data.order_status || "pending" }
|
|
1863
2221
|
});
|
|
1864
2222
|
};
|
|
1865
2223
|
const createStrapiPaymentRecord = async (paymentIntent) => {
|
|
@@ -1976,10 +2334,18 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1976
2334
|
switch (event.type) {
|
|
1977
2335
|
case "payment_intent.succeeded":
|
|
1978
2336
|
case "charge.succeeded": {
|
|
1979
|
-
|
|
1980
|
-
|
|
2337
|
+
let paymentIntentId;
|
|
2338
|
+
let metadata;
|
|
2339
|
+
if (event.type === "payment_intent.succeeded") {
|
|
2340
|
+
const piObj = event.data.object;
|
|
2341
|
+
paymentIntentId = piObj.id;
|
|
2342
|
+
metadata = piObj.metadata || {};
|
|
2343
|
+
} else {
|
|
2344
|
+
const chargeObj = event.data.object;
|
|
2345
|
+
paymentIntentId = chargeObj.payment_intent;
|
|
2346
|
+
metadata = chargeObj.metadata || {};
|
|
2347
|
+
}
|
|
1981
2348
|
if (!paymentIntentId) break;
|
|
1982
|
-
const metadata = obj.metadata || {};
|
|
1983
2349
|
await updateStrapiPayment(paymentIntentId, {
|
|
1984
2350
|
payment_status: "succeeded",
|
|
1985
2351
|
metadata
|
|
@@ -1987,12 +2353,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1987
2353
|
const existing = await strapi2.documents("plugin::payment-plugin.payment").findMany({
|
|
1988
2354
|
filters: { stripe_payment_intent_id: paymentIntentId }
|
|
1989
2355
|
});
|
|
1990
|
-
if (existing.length === 0
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2356
|
+
if (existing.length === 0) {
|
|
2357
|
+
if (event.type === "payment_intent.succeeded") {
|
|
2358
|
+
await createStrapiPaymentRecord(event.data.object);
|
|
2359
|
+
} else {
|
|
2360
|
+
const stripeClient = await initializeStripe();
|
|
2361
|
+
const pi = await stripeClient.paymentIntents.retrieve(paymentIntentId);
|
|
2362
|
+
await createStrapiPaymentRecord(pi);
|
|
2363
|
+
}
|
|
1996
2364
|
}
|
|
1997
2365
|
const orderId = metadata.strapi_order_id;
|
|
1998
2366
|
if (orderId) {
|
|
@@ -2099,6 +2467,73 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2099
2467
|
});
|
|
2100
2468
|
break;
|
|
2101
2469
|
}
|
|
2470
|
+
case "customer.subscription.created": {
|
|
2471
|
+
const subscription2 = event.data.object;
|
|
2472
|
+
const strapiCustomerId = subscription2.metadata?.strapi_customer_id;
|
|
2473
|
+
const existing = await strapi2.documents("plugin::payment-plugin.subscription").findMany({
|
|
2474
|
+
filters: { stripe_subscription_id: subscription2.id }
|
|
2475
|
+
});
|
|
2476
|
+
if (existing.length === 0) {
|
|
2477
|
+
await createStrapiSubscriptionRecord(subscription2, strapiCustomerId);
|
|
2478
|
+
}
|
|
2479
|
+
logger.info("Subscription created via webhook", { subscriptionId: subscription2.id });
|
|
2480
|
+
break;
|
|
2481
|
+
}
|
|
2482
|
+
case "customer.subscription.updated": {
|
|
2483
|
+
const subscription2 = event.data.object;
|
|
2484
|
+
const sub = subscription2;
|
|
2485
|
+
const firstItem = subscription2.items.data[0];
|
|
2486
|
+
await updateStrapiSubscription(subscription2.id, {
|
|
2487
|
+
status: mapStripeSubscriptionStatus(subscription2.status),
|
|
2488
|
+
stripe_price_id: firstItem?.price?.id,
|
|
2489
|
+
stripe_product_id: firstItem?.price?.product,
|
|
2490
|
+
current_period_start: toISOOrNull(sub.current_period_start),
|
|
2491
|
+
current_period_end: toISOOrNull(sub.current_period_end),
|
|
2492
|
+
cancel_at_period_end: subscription2.cancel_at_period_end,
|
|
2493
|
+
canceled_at: toISOOrNull(subscription2.canceled_at),
|
|
2494
|
+
trial_start: toISOOrNull(subscription2.trial_start),
|
|
2495
|
+
trial_end: toISOOrNull(subscription2.trial_end),
|
|
2496
|
+
latest_invoice_id: typeof subscription2.latest_invoice === "string" ? subscription2.latest_invoice : subscription2.latest_invoice?.id,
|
|
2497
|
+
metadata: subscription2.metadata
|
|
2498
|
+
});
|
|
2499
|
+
logger.info("Subscription updated via webhook", { subscriptionId: subscription2.id, status: subscription2.status });
|
|
2500
|
+
break;
|
|
2501
|
+
}
|
|
2502
|
+
case "customer.subscription.deleted": {
|
|
2503
|
+
const subscription2 = event.data.object;
|
|
2504
|
+
await updateStrapiSubscription(subscription2.id, {
|
|
2505
|
+
status: "canceled",
|
|
2506
|
+
canceled_at: toISOOrNull(subscription2.canceled_at) || (/* @__PURE__ */ new Date()).toISOString()
|
|
2507
|
+
});
|
|
2508
|
+
logger.info("Subscription deleted via webhook", { subscriptionId: subscription2.id });
|
|
2509
|
+
break;
|
|
2510
|
+
}
|
|
2511
|
+
case "invoice.payment_succeeded": {
|
|
2512
|
+
const invoice = event.data.object;
|
|
2513
|
+
const subDetails = invoice.parent?.subscription_details;
|
|
2514
|
+
const subscriptionId = typeof subDetails?.subscription === "string" ? subDetails.subscription : subDetails?.subscription?.id ?? null;
|
|
2515
|
+
if (subscriptionId) {
|
|
2516
|
+
await updateStrapiSubscription(subscriptionId, {
|
|
2517
|
+
status: "active",
|
|
2518
|
+
latest_invoice_id: invoice.id
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
logger.info("Invoice payment succeeded", { invoiceId: invoice.id, subscriptionId });
|
|
2522
|
+
break;
|
|
2523
|
+
}
|
|
2524
|
+
case "invoice.payment_failed": {
|
|
2525
|
+
const invoice = event.data.object;
|
|
2526
|
+
const subDetails = invoice.parent?.subscription_details;
|
|
2527
|
+
const subscriptionId = typeof subDetails?.subscription === "string" ? subDetails.subscription : subDetails?.subscription?.id ?? null;
|
|
2528
|
+
if (subscriptionId) {
|
|
2529
|
+
await updateStrapiSubscription(subscriptionId, {
|
|
2530
|
+
status: "past_due",
|
|
2531
|
+
latest_invoice_id: invoice.id
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
logger.warn("Invoice payment failed", { invoiceId: invoice.id, subscriptionId });
|
|
2535
|
+
break;
|
|
2536
|
+
}
|
|
2102
2537
|
default:
|
|
2103
2538
|
logger.info("Unhandled webhook event type", { eventType: event.type });
|
|
2104
2539
|
}
|
|
@@ -2111,6 +2546,140 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2111
2546
|
throw error;
|
|
2112
2547
|
}
|
|
2113
2548
|
};
|
|
2549
|
+
const mapStripeSubscriptionStatus = (status) => {
|
|
2550
|
+
const valid = ["active", "canceled", "incomplete", "incomplete_expired", "past_due", "paused", "trialing", "unpaid"];
|
|
2551
|
+
return valid.includes(status) ? status : "incomplete";
|
|
2552
|
+
};
|
|
2553
|
+
const toISOOrNull = (ts) => {
|
|
2554
|
+
if (!ts) return null;
|
|
2555
|
+
return new Date(ts * 1e3).toISOString();
|
|
2556
|
+
};
|
|
2557
|
+
const createStrapiSubscriptionRecord = async (subscription2, strapiCustomerId) => {
|
|
2558
|
+
const logger = getLogger();
|
|
2559
|
+
try {
|
|
2560
|
+
const firstItem = subscription2.items.data[0];
|
|
2561
|
+
const price = firstItem?.price;
|
|
2562
|
+
const data = {
|
|
2563
|
+
stripe_subscription_id: subscription2.id,
|
|
2564
|
+
stripe_customer_id: subscription2.customer,
|
|
2565
|
+
stripe_price_id: price?.id || "",
|
|
2566
|
+
stripe_product_id: price?.product,
|
|
2567
|
+
status: mapStripeSubscriptionStatus(subscription2.status),
|
|
2568
|
+
current_period_start: toISOOrNull(subscription2.current_period_start) || void 0,
|
|
2569
|
+
current_period_end: toISOOrNull(subscription2.current_period_end) || void 0,
|
|
2570
|
+
cancel_at_period_end: subscription2.cancel_at_period_end,
|
|
2571
|
+
canceled_at: toISOOrNull(subscription2.canceled_at) || void 0,
|
|
2572
|
+
trial_start: toISOOrNull(subscription2.trial_start) || void 0,
|
|
2573
|
+
trial_end: toISOOrNull(subscription2.trial_end) || void 0,
|
|
2574
|
+
latest_invoice_id: typeof subscription2.latest_invoice === "string" ? subscription2.latest_invoice : subscription2.latest_invoice?.id,
|
|
2575
|
+
metadata: subscription2.metadata,
|
|
2576
|
+
customer: strapiCustomerId
|
|
2577
|
+
};
|
|
2578
|
+
const record = await strapi2.documents("plugin::payment-plugin.subscription").create({ data });
|
|
2579
|
+
logger.info("Created Strapi subscription record", {
|
|
2580
|
+
stripeSubscriptionId: subscription2.id,
|
|
2581
|
+
strapiCustomerId
|
|
2582
|
+
});
|
|
2583
|
+
return record;
|
|
2584
|
+
} catch (error) {
|
|
2585
|
+
logger.error("Failed to create Strapi subscription record", { error, subscriptionId: subscription2.id });
|
|
2586
|
+
throw error;
|
|
2587
|
+
}
|
|
2588
|
+
};
|
|
2589
|
+
const updateStrapiSubscription = async (stripeSubscriptionId, data) => {
|
|
2590
|
+
const logger = getLogger();
|
|
2591
|
+
try {
|
|
2592
|
+
const existing = await strapi2.documents("plugin::payment-plugin.subscription").findMany({
|
|
2593
|
+
filters: { stripe_subscription_id: stripeSubscriptionId }
|
|
2594
|
+
});
|
|
2595
|
+
if (existing.length > 0) {
|
|
2596
|
+
await strapi2.documents("plugin::payment-plugin.subscription").update({
|
|
2597
|
+
documentId: existing[0].documentId,
|
|
2598
|
+
data
|
|
2599
|
+
});
|
|
2600
|
+
}
|
|
2601
|
+
} catch (error) {
|
|
2602
|
+
logger.error("Failed to update Strapi subscription record", { error, stripeSubscriptionId });
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
const createSubscription = async (params) => {
|
|
2606
|
+
const stripe2 = await initializeStripe();
|
|
2607
|
+
const { strapiCustomerId, stripeCustomerId, priceId, trialPeriodDays, metadata = {}, defaultPaymentMethod } = params;
|
|
2608
|
+
const subscriptionParams = {
|
|
2609
|
+
customer: stripeCustomerId,
|
|
2610
|
+
items: [{ price: priceId }],
|
|
2611
|
+
payment_behavior: "default_incomplete",
|
|
2612
|
+
payment_settings: { save_default_payment_method: "on_subscription" },
|
|
2613
|
+
expand: ["latest_invoice.payment_intent", "pending_setup_intent"],
|
|
2614
|
+
metadata: { ...metadata, strapi_customer_id: strapiCustomerId }
|
|
2615
|
+
};
|
|
2616
|
+
if (trialPeriodDays) {
|
|
2617
|
+
subscriptionParams.trial_period_days = trialPeriodDays;
|
|
2618
|
+
}
|
|
2619
|
+
if (defaultPaymentMethod) {
|
|
2620
|
+
subscriptionParams.default_payment_method = defaultPaymentMethod;
|
|
2621
|
+
}
|
|
2622
|
+
const subscription2 = await stripe2.subscriptions.create(subscriptionParams);
|
|
2623
|
+
await createStrapiSubscriptionRecord(subscription2, strapiCustomerId);
|
|
2624
|
+
getLogger().info("Subscription created", { subscriptionId: subscription2.id, stripeCustomerId });
|
|
2625
|
+
return subscription2;
|
|
2626
|
+
};
|
|
2627
|
+
const cancelSubscription = async (stripeSubscriptionId, cancelAtPeriodEnd = false) => {
|
|
2628
|
+
const stripe2 = await initializeStripe();
|
|
2629
|
+
let subscription2;
|
|
2630
|
+
if (cancelAtPeriodEnd) {
|
|
2631
|
+
subscription2 = await stripe2.subscriptions.update(stripeSubscriptionId, { cancel_at_period_end: true });
|
|
2632
|
+
await updateStrapiSubscription(stripeSubscriptionId, { cancel_at_period_end: true });
|
|
2633
|
+
} else {
|
|
2634
|
+
subscription2 = await stripe2.subscriptions.cancel(stripeSubscriptionId);
|
|
2635
|
+
await updateStrapiSubscription(stripeSubscriptionId, {
|
|
2636
|
+
status: "canceled",
|
|
2637
|
+
canceled_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2638
|
+
cancel_at_period_end: false
|
|
2639
|
+
});
|
|
2640
|
+
}
|
|
2641
|
+
getLogger().info("Subscription canceled", { stripeSubscriptionId, cancelAtPeriodEnd });
|
|
2642
|
+
return subscription2;
|
|
2643
|
+
};
|
|
2644
|
+
const updateSubscription = async (stripeSubscriptionId, params) => {
|
|
2645
|
+
const stripe2 = await initializeStripe();
|
|
2646
|
+
const updateParams = {};
|
|
2647
|
+
if (params.priceId) {
|
|
2648
|
+
const current = await stripe2.subscriptions.retrieve(stripeSubscriptionId);
|
|
2649
|
+
const itemId = current.items.data[0]?.id;
|
|
2650
|
+
updateParams.items = [{ id: itemId, price: params.priceId }];
|
|
2651
|
+
updateParams.proration_behavior = "create_prorations";
|
|
2652
|
+
}
|
|
2653
|
+
if (params.cancelAtPeriodEnd !== void 0) {
|
|
2654
|
+
updateParams.cancel_at_period_end = params.cancelAtPeriodEnd;
|
|
2655
|
+
}
|
|
2656
|
+
if (params.trialEnd !== void 0) {
|
|
2657
|
+
updateParams.trial_end = params.trialEnd;
|
|
2658
|
+
}
|
|
2659
|
+
if (params.metadata) {
|
|
2660
|
+
updateParams.metadata = params.metadata;
|
|
2661
|
+
}
|
|
2662
|
+
if (params.defaultPaymentMethod) {
|
|
2663
|
+
updateParams.default_payment_method = params.defaultPaymentMethod;
|
|
2664
|
+
}
|
|
2665
|
+
const subscription2 = await stripe2.subscriptions.update(stripeSubscriptionId, updateParams);
|
|
2666
|
+
const firstItem = subscription2.items.data[0];
|
|
2667
|
+
await updateStrapiSubscription(stripeSubscriptionId, {
|
|
2668
|
+
stripe_price_id: firstItem?.price?.id,
|
|
2669
|
+
stripe_product_id: firstItem?.price?.product,
|
|
2670
|
+
cancel_at_period_end: subscription2.cancel_at_period_end,
|
|
2671
|
+
status: mapStripeSubscriptionStatus(subscription2.status),
|
|
2672
|
+
metadata: subscription2.metadata
|
|
2673
|
+
});
|
|
2674
|
+
getLogger().info("Subscription updated", { stripeSubscriptionId });
|
|
2675
|
+
return subscription2;
|
|
2676
|
+
};
|
|
2677
|
+
const retrieveSubscription = async (stripeSubscriptionId) => {
|
|
2678
|
+
const stripe2 = await initializeStripe();
|
|
2679
|
+
return stripe2.subscriptions.retrieve(stripeSubscriptionId, {
|
|
2680
|
+
expand: ["latest_invoice", "customer"]
|
|
2681
|
+
});
|
|
2682
|
+
};
|
|
2114
2683
|
return {
|
|
2115
2684
|
initializeStripe,
|
|
2116
2685
|
createPaymentIntent,
|
|
@@ -2126,7 +2695,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2126
2695
|
createCustomerRecord,
|
|
2127
2696
|
createOrderRecord,
|
|
2128
2697
|
initializePaymentFlow,
|
|
2129
|
-
getStripe
|
|
2698
|
+
getStripe,
|
|
2699
|
+
setStripe,
|
|
2700
|
+
createSubscription,
|
|
2701
|
+
cancelSubscription,
|
|
2702
|
+
updateSubscription,
|
|
2703
|
+
retrieveSubscription,
|
|
2704
|
+
createStrapiSubscriptionRecord,
|
|
2705
|
+
updateStrapiSubscription
|
|
2130
2706
|
};
|
|
2131
2707
|
};
|
|
2132
2708
|
const services = {
|