@reeboot/strapi-payment-plugin 0.0.9 → 0.0.10
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 +618 -46
- package/dist/server/index.mjs +618 -46
- 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 +49 -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 +1 -1
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
|
};
|
|
@@ -1752,14 +2110,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1752
2110
|
return refund;
|
|
1753
2111
|
} catch (error) {
|
|
1754
2112
|
const logger = getLogger();
|
|
1755
|
-
const errorMessage = error
|
|
2113
|
+
const errorMessage = getErrorMessage(error);
|
|
1756
2114
|
logger.error("Failed to create Stripe refund", {
|
|
1757
2115
|
message: errorMessage,
|
|
1758
2116
|
error: error instanceof Error ? {
|
|
1759
2117
|
message: error.message,
|
|
1760
2118
|
stack: error.stack,
|
|
1761
|
-
...
|
|
1762
|
-
} : error,
|
|
2119
|
+
..."raw" in error && { raw: error.raw }
|
|
2120
|
+
} : String(error),
|
|
1763
2121
|
params
|
|
1764
2122
|
});
|
|
1765
2123
|
throw error;
|
|
@@ -1782,8 +2140,8 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1782
2140
|
return stripe2.webhooks.constructEvent(verifiedPayload, signature, webhookSecret);
|
|
1783
2141
|
} catch (error) {
|
|
1784
2142
|
logger.error("Failed to construct webhook event", {
|
|
1785
|
-
message: error
|
|
1786
|
-
type: error.type
|
|
2143
|
+
message: getErrorMessage(error),
|
|
2144
|
+
...error instanceof Error && "type" in error && { type: error.type }
|
|
1787
2145
|
});
|
|
1788
2146
|
throw error;
|
|
1789
2147
|
}
|
|
@@ -1856,10 +2214,7 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1856
2214
|
};
|
|
1857
2215
|
const createOrderRecord = async (data) => {
|
|
1858
2216
|
return strapi2.documents("plugin::payment-plugin.order").create({
|
|
1859
|
-
data: {
|
|
1860
|
-
...data,
|
|
1861
|
-
order_status: data.order_status || "pending"
|
|
1862
|
-
}
|
|
2217
|
+
data: { ...data, order_status: data.order_status || "pending" }
|
|
1863
2218
|
});
|
|
1864
2219
|
};
|
|
1865
2220
|
const createStrapiPaymentRecord = async (paymentIntent) => {
|
|
@@ -1976,10 +2331,18 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1976
2331
|
switch (event.type) {
|
|
1977
2332
|
case "payment_intent.succeeded":
|
|
1978
2333
|
case "charge.succeeded": {
|
|
1979
|
-
|
|
1980
|
-
|
|
2334
|
+
let paymentIntentId;
|
|
2335
|
+
let metadata;
|
|
2336
|
+
if (event.type === "payment_intent.succeeded") {
|
|
2337
|
+
const piObj = event.data.object;
|
|
2338
|
+
paymentIntentId = piObj.id;
|
|
2339
|
+
metadata = piObj.metadata || {};
|
|
2340
|
+
} else {
|
|
2341
|
+
const chargeObj = event.data.object;
|
|
2342
|
+
paymentIntentId = chargeObj.payment_intent;
|
|
2343
|
+
metadata = chargeObj.metadata || {};
|
|
2344
|
+
}
|
|
1981
2345
|
if (!paymentIntentId) break;
|
|
1982
|
-
const metadata = obj.metadata || {};
|
|
1983
2346
|
await updateStrapiPayment(paymentIntentId, {
|
|
1984
2347
|
payment_status: "succeeded",
|
|
1985
2348
|
metadata
|
|
@@ -1987,12 +2350,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1987
2350
|
const existing = await strapi2.documents("plugin::payment-plugin.payment").findMany({
|
|
1988
2351
|
filters: { stripe_payment_intent_id: paymentIntentId }
|
|
1989
2352
|
});
|
|
1990
|
-
if (existing.length === 0
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
2353
|
+
if (existing.length === 0) {
|
|
2354
|
+
if (event.type === "payment_intent.succeeded") {
|
|
2355
|
+
await createStrapiPaymentRecord(event.data.object);
|
|
2356
|
+
} else {
|
|
2357
|
+
const stripeClient = await initializeStripe();
|
|
2358
|
+
const pi = await stripeClient.paymentIntents.retrieve(paymentIntentId);
|
|
2359
|
+
await createStrapiPaymentRecord(pi);
|
|
2360
|
+
}
|
|
1996
2361
|
}
|
|
1997
2362
|
const orderId = metadata.strapi_order_id;
|
|
1998
2363
|
if (orderId) {
|
|
@@ -2099,6 +2464,73 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2099
2464
|
});
|
|
2100
2465
|
break;
|
|
2101
2466
|
}
|
|
2467
|
+
case "customer.subscription.created": {
|
|
2468
|
+
const subscription2 = event.data.object;
|
|
2469
|
+
const strapiCustomerId = subscription2.metadata?.strapi_customer_id;
|
|
2470
|
+
const existing = await strapi2.documents("plugin::payment-plugin.subscription").findMany({
|
|
2471
|
+
filters: { stripe_subscription_id: subscription2.id }
|
|
2472
|
+
});
|
|
2473
|
+
if (existing.length === 0) {
|
|
2474
|
+
await createStrapiSubscriptionRecord(subscription2, strapiCustomerId);
|
|
2475
|
+
}
|
|
2476
|
+
logger.info("Subscription created via webhook", { subscriptionId: subscription2.id });
|
|
2477
|
+
break;
|
|
2478
|
+
}
|
|
2479
|
+
case "customer.subscription.updated": {
|
|
2480
|
+
const subscription2 = event.data.object;
|
|
2481
|
+
const sub = subscription2;
|
|
2482
|
+
const firstItem = subscription2.items.data[0];
|
|
2483
|
+
await updateStrapiSubscription(subscription2.id, {
|
|
2484
|
+
status: mapStripeSubscriptionStatus(subscription2.status),
|
|
2485
|
+
stripe_price_id: firstItem?.price?.id,
|
|
2486
|
+
stripe_product_id: firstItem?.price?.product,
|
|
2487
|
+
current_period_start: toISOOrNull(sub.current_period_start),
|
|
2488
|
+
current_period_end: toISOOrNull(sub.current_period_end),
|
|
2489
|
+
cancel_at_period_end: subscription2.cancel_at_period_end,
|
|
2490
|
+
canceled_at: toISOOrNull(subscription2.canceled_at),
|
|
2491
|
+
trial_start: toISOOrNull(subscription2.trial_start),
|
|
2492
|
+
trial_end: toISOOrNull(subscription2.trial_end),
|
|
2493
|
+
latest_invoice_id: typeof subscription2.latest_invoice === "string" ? subscription2.latest_invoice : subscription2.latest_invoice?.id,
|
|
2494
|
+
metadata: subscription2.metadata
|
|
2495
|
+
});
|
|
2496
|
+
logger.info("Subscription updated via webhook", { subscriptionId: subscription2.id, status: subscription2.status });
|
|
2497
|
+
break;
|
|
2498
|
+
}
|
|
2499
|
+
case "customer.subscription.deleted": {
|
|
2500
|
+
const subscription2 = event.data.object;
|
|
2501
|
+
await updateStrapiSubscription(subscription2.id, {
|
|
2502
|
+
status: "canceled",
|
|
2503
|
+
canceled_at: toISOOrNull(subscription2.canceled_at) || (/* @__PURE__ */ new Date()).toISOString()
|
|
2504
|
+
});
|
|
2505
|
+
logger.info("Subscription deleted via webhook", { subscriptionId: subscription2.id });
|
|
2506
|
+
break;
|
|
2507
|
+
}
|
|
2508
|
+
case "invoice.payment_succeeded": {
|
|
2509
|
+
const invoice = event.data.object;
|
|
2510
|
+
const subDetails = invoice.parent?.subscription_details;
|
|
2511
|
+
const subscriptionId = typeof subDetails?.subscription === "string" ? subDetails.subscription : subDetails?.subscription?.id ?? null;
|
|
2512
|
+
if (subscriptionId) {
|
|
2513
|
+
await updateStrapiSubscription(subscriptionId, {
|
|
2514
|
+
status: "active",
|
|
2515
|
+
latest_invoice_id: invoice.id
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
logger.info("Invoice payment succeeded", { invoiceId: invoice.id, subscriptionId });
|
|
2519
|
+
break;
|
|
2520
|
+
}
|
|
2521
|
+
case "invoice.payment_failed": {
|
|
2522
|
+
const invoice = event.data.object;
|
|
2523
|
+
const subDetails = invoice.parent?.subscription_details;
|
|
2524
|
+
const subscriptionId = typeof subDetails?.subscription === "string" ? subDetails.subscription : subDetails?.subscription?.id ?? null;
|
|
2525
|
+
if (subscriptionId) {
|
|
2526
|
+
await updateStrapiSubscription(subscriptionId, {
|
|
2527
|
+
status: "past_due",
|
|
2528
|
+
latest_invoice_id: invoice.id
|
|
2529
|
+
});
|
|
2530
|
+
}
|
|
2531
|
+
logger.warn("Invoice payment failed", { invoiceId: invoice.id, subscriptionId });
|
|
2532
|
+
break;
|
|
2533
|
+
}
|
|
2102
2534
|
default:
|
|
2103
2535
|
logger.info("Unhandled webhook event type", { eventType: event.type });
|
|
2104
2536
|
}
|
|
@@ -2111,6 +2543,140 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2111
2543
|
throw error;
|
|
2112
2544
|
}
|
|
2113
2545
|
};
|
|
2546
|
+
const mapStripeSubscriptionStatus = (status) => {
|
|
2547
|
+
const valid = ["active", "canceled", "incomplete", "incomplete_expired", "past_due", "paused", "trialing", "unpaid"];
|
|
2548
|
+
return valid.includes(status) ? status : "incomplete";
|
|
2549
|
+
};
|
|
2550
|
+
const toISOOrNull = (ts) => {
|
|
2551
|
+
if (!ts) return null;
|
|
2552
|
+
return new Date(ts * 1e3).toISOString();
|
|
2553
|
+
};
|
|
2554
|
+
const createStrapiSubscriptionRecord = async (subscription2, strapiCustomerId) => {
|
|
2555
|
+
const logger = getLogger();
|
|
2556
|
+
try {
|
|
2557
|
+
const firstItem = subscription2.items.data[0];
|
|
2558
|
+
const price = firstItem?.price;
|
|
2559
|
+
const data = {
|
|
2560
|
+
stripe_subscription_id: subscription2.id,
|
|
2561
|
+
stripe_customer_id: subscription2.customer,
|
|
2562
|
+
stripe_price_id: price?.id || "",
|
|
2563
|
+
stripe_product_id: price?.product,
|
|
2564
|
+
status: mapStripeSubscriptionStatus(subscription2.status),
|
|
2565
|
+
current_period_start: toISOOrNull(subscription2.current_period_start) || void 0,
|
|
2566
|
+
current_period_end: toISOOrNull(subscription2.current_period_end) || void 0,
|
|
2567
|
+
cancel_at_period_end: subscription2.cancel_at_period_end,
|
|
2568
|
+
canceled_at: toISOOrNull(subscription2.canceled_at) || void 0,
|
|
2569
|
+
trial_start: toISOOrNull(subscription2.trial_start) || void 0,
|
|
2570
|
+
trial_end: toISOOrNull(subscription2.trial_end) || void 0,
|
|
2571
|
+
latest_invoice_id: typeof subscription2.latest_invoice === "string" ? subscription2.latest_invoice : subscription2.latest_invoice?.id,
|
|
2572
|
+
metadata: subscription2.metadata,
|
|
2573
|
+
customer: strapiCustomerId
|
|
2574
|
+
};
|
|
2575
|
+
const record = await strapi2.documents("plugin::payment-plugin.subscription").create({ data });
|
|
2576
|
+
logger.info("Created Strapi subscription record", {
|
|
2577
|
+
stripeSubscriptionId: subscription2.id,
|
|
2578
|
+
strapiCustomerId
|
|
2579
|
+
});
|
|
2580
|
+
return record;
|
|
2581
|
+
} catch (error) {
|
|
2582
|
+
logger.error("Failed to create Strapi subscription record", { error, subscriptionId: subscription2.id });
|
|
2583
|
+
throw error;
|
|
2584
|
+
}
|
|
2585
|
+
};
|
|
2586
|
+
const updateStrapiSubscription = async (stripeSubscriptionId, data) => {
|
|
2587
|
+
const logger = getLogger();
|
|
2588
|
+
try {
|
|
2589
|
+
const existing = await strapi2.documents("plugin::payment-plugin.subscription").findMany({
|
|
2590
|
+
filters: { stripe_subscription_id: stripeSubscriptionId }
|
|
2591
|
+
});
|
|
2592
|
+
if (existing.length > 0) {
|
|
2593
|
+
await strapi2.documents("plugin::payment-plugin.subscription").update({
|
|
2594
|
+
documentId: existing[0].documentId,
|
|
2595
|
+
data
|
|
2596
|
+
});
|
|
2597
|
+
}
|
|
2598
|
+
} catch (error) {
|
|
2599
|
+
logger.error("Failed to update Strapi subscription record", { error, stripeSubscriptionId });
|
|
2600
|
+
}
|
|
2601
|
+
};
|
|
2602
|
+
const createSubscription = async (params) => {
|
|
2603
|
+
const stripe2 = await initializeStripe();
|
|
2604
|
+
const { strapiCustomerId, stripeCustomerId, priceId, trialPeriodDays, metadata = {}, defaultPaymentMethod } = params;
|
|
2605
|
+
const subscriptionParams = {
|
|
2606
|
+
customer: stripeCustomerId,
|
|
2607
|
+
items: [{ price: priceId }],
|
|
2608
|
+
payment_behavior: "default_incomplete",
|
|
2609
|
+
payment_settings: { save_default_payment_method: "on_subscription" },
|
|
2610
|
+
expand: ["latest_invoice.payment_intent", "pending_setup_intent"],
|
|
2611
|
+
metadata: { ...metadata, strapi_customer_id: strapiCustomerId }
|
|
2612
|
+
};
|
|
2613
|
+
if (trialPeriodDays) {
|
|
2614
|
+
subscriptionParams.trial_period_days = trialPeriodDays;
|
|
2615
|
+
}
|
|
2616
|
+
if (defaultPaymentMethod) {
|
|
2617
|
+
subscriptionParams.default_payment_method = defaultPaymentMethod;
|
|
2618
|
+
}
|
|
2619
|
+
const subscription2 = await stripe2.subscriptions.create(subscriptionParams);
|
|
2620
|
+
await createStrapiSubscriptionRecord(subscription2, strapiCustomerId);
|
|
2621
|
+
getLogger().info("Subscription created", { subscriptionId: subscription2.id, stripeCustomerId });
|
|
2622
|
+
return subscription2;
|
|
2623
|
+
};
|
|
2624
|
+
const cancelSubscription = async (stripeSubscriptionId, cancelAtPeriodEnd = false) => {
|
|
2625
|
+
const stripe2 = await initializeStripe();
|
|
2626
|
+
let subscription2;
|
|
2627
|
+
if (cancelAtPeriodEnd) {
|
|
2628
|
+
subscription2 = await stripe2.subscriptions.update(stripeSubscriptionId, { cancel_at_period_end: true });
|
|
2629
|
+
await updateStrapiSubscription(stripeSubscriptionId, { cancel_at_period_end: true });
|
|
2630
|
+
} else {
|
|
2631
|
+
subscription2 = await stripe2.subscriptions.cancel(stripeSubscriptionId);
|
|
2632
|
+
await updateStrapiSubscription(stripeSubscriptionId, {
|
|
2633
|
+
status: "canceled",
|
|
2634
|
+
canceled_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2635
|
+
cancel_at_period_end: false
|
|
2636
|
+
});
|
|
2637
|
+
}
|
|
2638
|
+
getLogger().info("Subscription canceled", { stripeSubscriptionId, cancelAtPeriodEnd });
|
|
2639
|
+
return subscription2;
|
|
2640
|
+
};
|
|
2641
|
+
const updateSubscription = async (stripeSubscriptionId, params) => {
|
|
2642
|
+
const stripe2 = await initializeStripe();
|
|
2643
|
+
const updateParams = {};
|
|
2644
|
+
if (params.priceId) {
|
|
2645
|
+
const current = await stripe2.subscriptions.retrieve(stripeSubscriptionId);
|
|
2646
|
+
const itemId = current.items.data[0]?.id;
|
|
2647
|
+
updateParams.items = [{ id: itemId, price: params.priceId }];
|
|
2648
|
+
updateParams.proration_behavior = "create_prorations";
|
|
2649
|
+
}
|
|
2650
|
+
if (params.cancelAtPeriodEnd !== void 0) {
|
|
2651
|
+
updateParams.cancel_at_period_end = params.cancelAtPeriodEnd;
|
|
2652
|
+
}
|
|
2653
|
+
if (params.trialEnd !== void 0) {
|
|
2654
|
+
updateParams.trial_end = params.trialEnd;
|
|
2655
|
+
}
|
|
2656
|
+
if (params.metadata) {
|
|
2657
|
+
updateParams.metadata = params.metadata;
|
|
2658
|
+
}
|
|
2659
|
+
if (params.defaultPaymentMethod) {
|
|
2660
|
+
updateParams.default_payment_method = params.defaultPaymentMethod;
|
|
2661
|
+
}
|
|
2662
|
+
const subscription2 = await stripe2.subscriptions.update(stripeSubscriptionId, updateParams);
|
|
2663
|
+
const firstItem = subscription2.items.data[0];
|
|
2664
|
+
await updateStrapiSubscription(stripeSubscriptionId, {
|
|
2665
|
+
stripe_price_id: firstItem?.price?.id,
|
|
2666
|
+
stripe_product_id: firstItem?.price?.product,
|
|
2667
|
+
cancel_at_period_end: subscription2.cancel_at_period_end,
|
|
2668
|
+
status: mapStripeSubscriptionStatus(subscription2.status),
|
|
2669
|
+
metadata: subscription2.metadata
|
|
2670
|
+
});
|
|
2671
|
+
getLogger().info("Subscription updated", { stripeSubscriptionId });
|
|
2672
|
+
return subscription2;
|
|
2673
|
+
};
|
|
2674
|
+
const retrieveSubscription = async (stripeSubscriptionId) => {
|
|
2675
|
+
const stripe2 = await initializeStripe();
|
|
2676
|
+
return stripe2.subscriptions.retrieve(stripeSubscriptionId, {
|
|
2677
|
+
expand: ["latest_invoice", "customer"]
|
|
2678
|
+
});
|
|
2679
|
+
};
|
|
2114
2680
|
return {
|
|
2115
2681
|
initializeStripe,
|
|
2116
2682
|
createPaymentIntent,
|
|
@@ -2126,7 +2692,13 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2126
2692
|
createCustomerRecord,
|
|
2127
2693
|
createOrderRecord,
|
|
2128
2694
|
initializePaymentFlow,
|
|
2129
|
-
getStripe
|
|
2695
|
+
getStripe,
|
|
2696
|
+
createSubscription,
|
|
2697
|
+
cancelSubscription,
|
|
2698
|
+
updateSubscription,
|
|
2699
|
+
retrieveSubscription,
|
|
2700
|
+
createStrapiSubscriptionRecord,
|
|
2701
|
+
updateStrapiSubscription
|
|
2130
2702
|
};
|
|
2131
2703
|
};
|
|
2132
2704
|
const services = {
|