@reeboot/strapi-payment-plugin 0.0.8 → 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-CncK5kn-.mjs → Analytics-CPXtqS6s.mjs} +1 -1
- package/dist/_chunks/{Analytics-c8KBuG3k.js → Analytics-ZpHHWBQt.js} +1 -1
- package/dist/_chunks/{App-B5AB8Omu.mjs → App-CZNZ1JKf.mjs} +13 -7
- package/dist/_chunks/{App-Cih9sWu1.js → App-CsbyykNC.js} +13 -7
- package/dist/_chunks/{Customers-CZWOnN26.js → Customers-D4RVSZ_m.js} +1 -1
- package/dist/_chunks/{Customers-BVk2gx7w.mjs → Customers-DMDEBpH9.mjs} +1 -1
- package/dist/_chunks/{Dashboard-DAjD8Q_6.mjs → Dashboard-D-ckyWDO.mjs} +1 -1
- package/dist/_chunks/{Dashboard-CEif4jQn.js → Dashboard-NNqGJmYw.js} +1 -1
- package/dist/_chunks/{Orders-DZXb54VO.js → Orders-BELfgToU.js} +1 -1
- package/dist/_chunks/{Orders-DdJqI1HB.mjs → Orders-BIkRttRZ.mjs} +1 -1
- package/dist/_chunks/{Payments-VzDGbK4W.js → Payments-CIEb4f07.js} +1 -1
- package/dist/_chunks/{Payments-DFL-Cwgy.mjs → Payments-CKlL-PCV.mjs} +1 -1
- package/dist/_chunks/{Settings-SALxClBu.mjs → Settings-BQXkYm-c.mjs} +6 -5
- package/dist/_chunks/{Settings-B1tR3WOm.js → Settings-CcY98Hyw.js} +6 -5
- package/dist/_chunks/Subscriptions--cI3HC2x.js +178 -0
- package/dist/_chunks/Subscriptions-Dq8Uk7sK.mjs +178 -0
- package/dist/_chunks/{index-CB6TMitx.mjs → index-BBarHYyt.mjs} +1 -1
- package/dist/_chunks/{index-D-fFikb8.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 +619 -47
- package/dist/server/index.mjs +619 -47
- 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 +97 -13
- 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 +2 -2
package/dist/server/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ const config = {
|
|
|
10
10
|
validator() {
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
|
-
const schema$
|
|
13
|
+
const schema$3 = {
|
|
14
14
|
kind: "collectionType",
|
|
15
15
|
collectionName: "customers",
|
|
16
16
|
info: {
|
|
@@ -73,11 +73,17 @@ const schema$2 = {
|
|
|
73
73
|
relation: "oneToMany",
|
|
74
74
|
target: "plugin::payment-plugin.payment",
|
|
75
75
|
mappedBy: "customer"
|
|
76
|
+
},
|
|
77
|
+
subscriptions: {
|
|
78
|
+
type: "relation",
|
|
79
|
+
relation: "oneToMany",
|
|
80
|
+
target: "plugin::payment-plugin.subscription",
|
|
81
|
+
mappedBy: "customer"
|
|
76
82
|
}
|
|
77
83
|
}
|
|
78
84
|
};
|
|
79
|
-
const customer = { schema: schema$
|
|
80
|
-
const schema$
|
|
85
|
+
const customer = { schema: schema$3 };
|
|
86
|
+
const schema$2 = {
|
|
81
87
|
kind: "collectionType",
|
|
82
88
|
collectionName: "orders",
|
|
83
89
|
info: {
|
|
@@ -162,8 +168,8 @@ const schema$1 = {
|
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
};
|
|
165
|
-
const order = { schema: schema$
|
|
166
|
-
const schema = {
|
|
171
|
+
const order = { schema: schema$2 };
|
|
172
|
+
const schema$1 = {
|
|
167
173
|
kind: "collectionType",
|
|
168
174
|
collectionName: "payments",
|
|
169
175
|
info: {
|
|
@@ -235,17 +241,115 @@ const schema = {
|
|
|
235
241
|
}
|
|
236
242
|
}
|
|
237
243
|
};
|
|
238
|
-
const payment = { schema };
|
|
244
|
+
const payment = { schema: schema$1 };
|
|
245
|
+
const kind = "collectionType";
|
|
246
|
+
const collectionName = "payment_plugin_subscriptions";
|
|
247
|
+
const info = {
|
|
248
|
+
singularName: "subscription",
|
|
249
|
+
pluralName: "subscriptions",
|
|
250
|
+
displayName: "Subscription",
|
|
251
|
+
description: "Stripe subscription records for recurring billing"
|
|
252
|
+
};
|
|
253
|
+
const options = {
|
|
254
|
+
draftAndPublish: false
|
|
255
|
+
};
|
|
256
|
+
const attributes = {
|
|
257
|
+
stripe_subscription_id: {
|
|
258
|
+
type: "string",
|
|
259
|
+
unique: true,
|
|
260
|
+
required: true,
|
|
261
|
+
configurable: false
|
|
262
|
+
},
|
|
263
|
+
stripe_customer_id: {
|
|
264
|
+
type: "string",
|
|
265
|
+
required: true,
|
|
266
|
+
configurable: false
|
|
267
|
+
},
|
|
268
|
+
stripe_price_id: {
|
|
269
|
+
type: "string",
|
|
270
|
+
required: true,
|
|
271
|
+
configurable: false
|
|
272
|
+
},
|
|
273
|
+
stripe_product_id: {
|
|
274
|
+
type: "string",
|
|
275
|
+
configurable: false
|
|
276
|
+
},
|
|
277
|
+
status: {
|
|
278
|
+
type: "enumeration",
|
|
279
|
+
"enum": [
|
|
280
|
+
"active",
|
|
281
|
+
"canceled",
|
|
282
|
+
"incomplete",
|
|
283
|
+
"incomplete_expired",
|
|
284
|
+
"past_due",
|
|
285
|
+
"paused",
|
|
286
|
+
"trialing",
|
|
287
|
+
"unpaid"
|
|
288
|
+
],
|
|
289
|
+
required: true,
|
|
290
|
+
"default": "incomplete",
|
|
291
|
+
configurable: false
|
|
292
|
+
},
|
|
293
|
+
current_period_start: {
|
|
294
|
+
type: "datetime",
|
|
295
|
+
configurable: false
|
|
296
|
+
},
|
|
297
|
+
current_period_end: {
|
|
298
|
+
type: "datetime",
|
|
299
|
+
configurable: false
|
|
300
|
+
},
|
|
301
|
+
cancel_at_period_end: {
|
|
302
|
+
type: "boolean",
|
|
303
|
+
"default": false,
|
|
304
|
+
configurable: false
|
|
305
|
+
},
|
|
306
|
+
canceled_at: {
|
|
307
|
+
type: "datetime",
|
|
308
|
+
configurable: false
|
|
309
|
+
},
|
|
310
|
+
trial_start: {
|
|
311
|
+
type: "datetime",
|
|
312
|
+
configurable: false
|
|
313
|
+
},
|
|
314
|
+
trial_end: {
|
|
315
|
+
type: "datetime",
|
|
316
|
+
configurable: false
|
|
317
|
+
},
|
|
318
|
+
latest_invoice_id: {
|
|
319
|
+
type: "string",
|
|
320
|
+
configurable: false
|
|
321
|
+
},
|
|
322
|
+
metadata: {
|
|
323
|
+
type: "json",
|
|
324
|
+
configurable: false
|
|
325
|
+
},
|
|
326
|
+
customer: {
|
|
327
|
+
type: "relation",
|
|
328
|
+
relation: "manyToOne",
|
|
329
|
+
target: "plugin::payment-plugin.customer",
|
|
330
|
+
inversedBy: "subscriptions"
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
const schema = {
|
|
334
|
+
kind,
|
|
335
|
+
collectionName,
|
|
336
|
+
info,
|
|
337
|
+
options,
|
|
338
|
+
attributes
|
|
339
|
+
};
|
|
340
|
+
const subscription = { schema };
|
|
239
341
|
const contentTypes = {
|
|
240
342
|
customer,
|
|
241
343
|
order,
|
|
242
|
-
payment
|
|
344
|
+
payment,
|
|
345
|
+
subscription
|
|
243
346
|
};
|
|
244
347
|
const controller = ({ strapi: strapi2 }) => ({
|
|
245
348
|
index(ctx) {
|
|
246
349
|
ctx.body = strapi2.plugin("payment-plugin").service("service").getWelcomeMessage();
|
|
247
350
|
}
|
|
248
351
|
});
|
|
352
|
+
const getErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
249
353
|
const stripeController = {
|
|
250
354
|
/**
|
|
251
355
|
* Create a payment intent
|
|
@@ -416,12 +520,13 @@ const stripeController = {
|
|
|
416
520
|
await stripeService2.handleWebhook(event);
|
|
417
521
|
ctx.body = { received: true };
|
|
418
522
|
} catch (error) {
|
|
419
|
-
|
|
523
|
+
const message = getErrorMessage(error);
|
|
524
|
+
if (message.includes("No signatures found matching") || message.includes("Webhook payload must be provided as a string or a Buffer")) {
|
|
420
525
|
strapi.log.error('Stripe Webhook Error: Payload is not raw. Ensure "includeUnparsed: true" is set in config/middlewares.ts for "strapi::body".');
|
|
421
526
|
return ctx.badRequest("Webhook verification failed: Payload must be raw. Check your Strapi configuration for strapi::body middleware.");
|
|
422
527
|
}
|
|
423
|
-
strapi.log.error("Failed to handle webhook", {
|
|
424
|
-
ctx.badRequest(`Webhook Error: ${
|
|
528
|
+
strapi.log.error("Failed to handle webhook", { message, stack: error instanceof Error ? error.stack : void 0 });
|
|
529
|
+
ctx.badRequest(`Webhook Error: ${message}`);
|
|
425
530
|
}
|
|
426
531
|
},
|
|
427
532
|
/**
|
|
@@ -779,24 +884,28 @@ const stripeController = {
|
|
|
779
884
|
const { startDate, endDate, format = "json" } = ctx.query;
|
|
780
885
|
const filters = {};
|
|
781
886
|
if (startDate || endDate) {
|
|
782
|
-
|
|
783
|
-
if (startDate)
|
|
784
|
-
if (endDate)
|
|
887
|
+
const dateFilter = {};
|
|
888
|
+
if (startDate) dateFilter.$gte = new Date(startDate).toISOString();
|
|
889
|
+
if (endDate) dateFilter.$lte = new Date(endDate).toISOString();
|
|
890
|
+
filters.createdAt = dateFilter;
|
|
785
891
|
}
|
|
786
|
-
const
|
|
892
|
+
const rawPayments = await strapi.documents("plugin::payment-plugin.payment").findMany({
|
|
787
893
|
filters,
|
|
788
894
|
populate: ["customer", "order"],
|
|
789
895
|
sort: { createdAt: "desc" }
|
|
790
896
|
});
|
|
897
|
+
const payments = rawPayments;
|
|
791
898
|
const summary = {
|
|
792
899
|
totalPayments: payments.length,
|
|
793
900
|
totalAmount: payments.reduce((sum, p) => sum + p.amount, 0),
|
|
794
901
|
byStatus: payments.reduce((acc, p) => {
|
|
795
|
-
|
|
902
|
+
const s = p.payment_status || "unknown";
|
|
903
|
+
acc[s] = (acc[s] || 0) + 1;
|
|
796
904
|
return acc;
|
|
797
905
|
}, {}),
|
|
798
906
|
byCurrency: payments.reduce((acc, p) => {
|
|
799
|
-
|
|
907
|
+
const c = p.currency || "unknown";
|
|
908
|
+
acc[c] = (acc[c] || 0) + 1;
|
|
800
909
|
return acc;
|
|
801
910
|
}, {})
|
|
802
911
|
};
|
|
@@ -887,8 +996,9 @@ const stripeController = {
|
|
|
887
996
|
fields: ["amount", "createdAt", "currency"],
|
|
888
997
|
sort: { createdAt: "asc" }
|
|
889
998
|
});
|
|
890
|
-
const
|
|
891
|
-
|
|
999
|
+
const typedPayments = payments;
|
|
1000
|
+
const dailyStats = typedPayments.reduce((acc, payment2) => {
|
|
1001
|
+
const date = new Date(payment2.createdAt || "").toISOString().split("T")[0];
|
|
892
1002
|
if (!acc[date]) {
|
|
893
1003
|
acc[date] = { count: 0, amount: 0, currency: payment2.currency };
|
|
894
1004
|
}
|
|
@@ -896,14 +1006,15 @@ const stripeController = {
|
|
|
896
1006
|
acc[date].amount += payment2.amount;
|
|
897
1007
|
return acc;
|
|
898
1008
|
}, {});
|
|
1009
|
+
const totalVolume = typedPayments.reduce((sum, p) => sum + p.amount, 0);
|
|
899
1010
|
ctx.body = {
|
|
900
1011
|
success: true,
|
|
901
1012
|
data: {
|
|
902
1013
|
period,
|
|
903
1014
|
dailyStats,
|
|
904
|
-
totalVolume
|
|
905
|
-
totalTransactions:
|
|
906
|
-
averageTransaction:
|
|
1015
|
+
totalVolume,
|
|
1016
|
+
totalTransactions: typedPayments.length,
|
|
1017
|
+
averageTransaction: typedPayments.length > 0 ? totalVolume / typedPayments.length : 0
|
|
907
1018
|
}
|
|
908
1019
|
};
|
|
909
1020
|
} catch (error) {
|
|
@@ -965,12 +1076,11 @@ const stripeController = {
|
|
|
965
1076
|
};
|
|
966
1077
|
} catch (error) {
|
|
967
1078
|
strapi.log.error("Failed to create admin refund:", {
|
|
968
|
-
message: error
|
|
969
|
-
stack: error.stack,
|
|
970
|
-
...error
|
|
1079
|
+
message: getErrorMessage(error),
|
|
1080
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
1081
|
+
...error instanceof Error && "raw" in error && { stripeError: error.raw }
|
|
971
1082
|
});
|
|
972
|
-
|
|
973
|
-
ctx.internalServerError(errorMessage);
|
|
1083
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to create admin refund");
|
|
974
1084
|
}
|
|
975
1085
|
},
|
|
976
1086
|
/**
|
|
@@ -1222,9 +1332,207 @@ const stripeController = {
|
|
|
1222
1332
|
}
|
|
1223
1333
|
}
|
|
1224
1334
|
};
|
|
1335
|
+
const subscriptionController = {
|
|
1336
|
+
/**
|
|
1337
|
+
* POST /subscriptions
|
|
1338
|
+
* Body: { stripeCustomerId, priceId, trialPeriodDays?, metadata?, defaultPaymentMethod? }
|
|
1339
|
+
*/
|
|
1340
|
+
async createSubscription(ctx) {
|
|
1341
|
+
try {
|
|
1342
|
+
const { stripeCustomerId, priceId, trialPeriodDays, metadata, defaultPaymentMethod } = ctx.request.body;
|
|
1343
|
+
if (!stripeCustomerId || !priceId) {
|
|
1344
|
+
return ctx.badRequest("stripeCustomerId and priceId are required");
|
|
1345
|
+
}
|
|
1346
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1347
|
+
const customers = await strapi.documents("plugin::payment-plugin.customer").findMany({
|
|
1348
|
+
filters: { stripe_customer_id: { $eq: stripeCustomerId } }
|
|
1349
|
+
});
|
|
1350
|
+
const strapiCustomerId = customers.length > 0 ? customers[0].documentId : void 0;
|
|
1351
|
+
const subscription2 = await stripeService2.createSubscription({
|
|
1352
|
+
strapiCustomerId: strapiCustomerId || "",
|
|
1353
|
+
stripeCustomerId,
|
|
1354
|
+
priceId,
|
|
1355
|
+
trialPeriodDays,
|
|
1356
|
+
metadata,
|
|
1357
|
+
defaultPaymentMethod
|
|
1358
|
+
});
|
|
1359
|
+
const invoice = typeof subscription2.latest_invoice === "object" ? subscription2.latest_invoice : null;
|
|
1360
|
+
const invoiceExt = invoice;
|
|
1361
|
+
const paymentIntent = typeof invoiceExt?.payment_intent === "object" ? invoiceExt.payment_intent : null;
|
|
1362
|
+
const setupIntent = typeof subscription2.pending_setup_intent === "object" ? subscription2.pending_setup_intent : null;
|
|
1363
|
+
ctx.body = {
|
|
1364
|
+
subscriptionId: subscription2.id,
|
|
1365
|
+
status: subscription2.status,
|
|
1366
|
+
clientSecret: paymentIntent?.client_secret || setupIntent?.client_secret || null,
|
|
1367
|
+
currentPeriodEnd: subscription2.current_period_end
|
|
1368
|
+
};
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
strapi.log.error("Failed to create subscription", { error: getErrorMessage(error) });
|
|
1371
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to create subscription");
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
/**
|
|
1375
|
+
* POST /subscriptions/:id/cancel
|
|
1376
|
+
* Body: { cancelAtPeriodEnd? }
|
|
1377
|
+
*/
|
|
1378
|
+
async cancelSubscription(ctx) {
|
|
1379
|
+
try {
|
|
1380
|
+
const { id } = ctx.params;
|
|
1381
|
+
const { cancelAtPeriodEnd = false } = ctx.request.body || {};
|
|
1382
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1383
|
+
const subscription2 = await stripeService2.cancelSubscription(id, cancelAtPeriodEnd);
|
|
1384
|
+
ctx.body = {
|
|
1385
|
+
subscriptionId: subscription2.id,
|
|
1386
|
+
status: subscription2.status,
|
|
1387
|
+
cancelAtPeriodEnd: subscription2.cancel_at_period_end,
|
|
1388
|
+
canceledAt: subscription2.canceled_at
|
|
1389
|
+
};
|
|
1390
|
+
} catch (error) {
|
|
1391
|
+
strapi.log.error("Failed to cancel subscription", { error: getErrorMessage(error) });
|
|
1392
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to cancel subscription");
|
|
1393
|
+
}
|
|
1394
|
+
},
|
|
1395
|
+
/**
|
|
1396
|
+
* PUT /subscriptions/:id
|
|
1397
|
+
* Body: { priceId?, cancelAtPeriodEnd?, trialEnd?, metadata?, defaultPaymentMethod? }
|
|
1398
|
+
*/
|
|
1399
|
+
async updateSubscription(ctx) {
|
|
1400
|
+
try {
|
|
1401
|
+
const { id } = ctx.params;
|
|
1402
|
+
const params = ctx.request.body || {};
|
|
1403
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1404
|
+
const subscription2 = await stripeService2.updateSubscription(id, params);
|
|
1405
|
+
ctx.body = {
|
|
1406
|
+
subscriptionId: subscription2.id,
|
|
1407
|
+
status: subscription2.status,
|
|
1408
|
+
cancelAtPeriodEnd: subscription2.cancel_at_period_end,
|
|
1409
|
+
currentPeriodEnd: subscription2.current_period_end
|
|
1410
|
+
};
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
strapi.log.error("Failed to update subscription", { error: getErrorMessage(error) });
|
|
1413
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to update subscription");
|
|
1414
|
+
}
|
|
1415
|
+
},
|
|
1416
|
+
/**
|
|
1417
|
+
* GET /subscriptions/:id
|
|
1418
|
+
*/
|
|
1419
|
+
async getSubscriptionDetails(ctx) {
|
|
1420
|
+
try {
|
|
1421
|
+
const { id } = ctx.params;
|
|
1422
|
+
const records = await strapi.documents("plugin::payment-plugin.subscription").findMany({
|
|
1423
|
+
filters: { stripe_subscription_id: { $eq: id } },
|
|
1424
|
+
populate: ["customer"]
|
|
1425
|
+
});
|
|
1426
|
+
if (records.length === 0) {
|
|
1427
|
+
return ctx.notFound("Subscription not found");
|
|
1428
|
+
}
|
|
1429
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1430
|
+
const stripeSubscription = await stripeService2.retrieveSubscription(id);
|
|
1431
|
+
ctx.body = {
|
|
1432
|
+
strapi: records[0],
|
|
1433
|
+
stripe: stripeSubscription
|
|
1434
|
+
};
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
strapi.log.error("Failed to get subscription", { error: getErrorMessage(error) });
|
|
1437
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to get subscription");
|
|
1438
|
+
}
|
|
1439
|
+
},
|
|
1440
|
+
/**
|
|
1441
|
+
* GET /subscriptions
|
|
1442
|
+
* Query: page, pageSize, status, stripeCustomerId
|
|
1443
|
+
*/
|
|
1444
|
+
async listSubscriptions(ctx) {
|
|
1445
|
+
try {
|
|
1446
|
+
const { page = 1, pageSize = 25, status, stripeCustomerId } = ctx.query;
|
|
1447
|
+
const filters = {};
|
|
1448
|
+
if (status) filters.status = { $eq: status };
|
|
1449
|
+
if (stripeCustomerId) filters.stripe_customer_id = { $eq: stripeCustomerId };
|
|
1450
|
+
const subscriptions = await strapi.documents("plugin::payment-plugin.subscription").findMany({
|
|
1451
|
+
filters,
|
|
1452
|
+
populate: ["customer"],
|
|
1453
|
+
sort: { createdAt: "desc" },
|
|
1454
|
+
start: (Number(page) - 1) * Number(pageSize),
|
|
1455
|
+
limit: Number(pageSize)
|
|
1456
|
+
});
|
|
1457
|
+
const total = await strapi.documents("plugin::payment-plugin.subscription").count({ filters });
|
|
1458
|
+
ctx.body = {
|
|
1459
|
+
data: subscriptions,
|
|
1460
|
+
meta: {
|
|
1461
|
+
pagination: {
|
|
1462
|
+
page: Number(page),
|
|
1463
|
+
pageSize: Number(pageSize),
|
|
1464
|
+
total,
|
|
1465
|
+
pageCount: Math.ceil(total / Number(pageSize))
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
};
|
|
1469
|
+
} catch (error) {
|
|
1470
|
+
strapi.log.error("Failed to list subscriptions", { error: getErrorMessage(error) });
|
|
1471
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to list subscriptions");
|
|
1472
|
+
}
|
|
1473
|
+
},
|
|
1474
|
+
/**
|
|
1475
|
+
* GET /admin/subscriptions (admin only)
|
|
1476
|
+
*/
|
|
1477
|
+
async adminListSubscriptions(ctx) {
|
|
1478
|
+
try {
|
|
1479
|
+
const { page = 1, pageSize = 25, status } = ctx.query;
|
|
1480
|
+
const filters = {};
|
|
1481
|
+
if (status) filters.status = { $eq: status };
|
|
1482
|
+
const subscriptions = await strapi.documents("plugin::payment-plugin.subscription").findMany({
|
|
1483
|
+
filters,
|
|
1484
|
+
populate: ["customer"],
|
|
1485
|
+
sort: { createdAt: "desc" },
|
|
1486
|
+
start: (Number(page) - 1) * Number(pageSize),
|
|
1487
|
+
limit: Number(pageSize)
|
|
1488
|
+
});
|
|
1489
|
+
const total = await strapi.documents("plugin::payment-plugin.subscription").count({ filters });
|
|
1490
|
+
const enriched = subscriptions.map((s) => ({
|
|
1491
|
+
...s,
|
|
1492
|
+
customerEmail: s.customer?.email || "",
|
|
1493
|
+
customerName: s.customer ? `${s.customer.first_name} ${s.customer.last_name}` : ""
|
|
1494
|
+
}));
|
|
1495
|
+
ctx.body = {
|
|
1496
|
+
data: enriched,
|
|
1497
|
+
meta: {
|
|
1498
|
+
pagination: {
|
|
1499
|
+
page: Number(page),
|
|
1500
|
+
pageSize: Number(pageSize),
|
|
1501
|
+
total,
|
|
1502
|
+
pageCount: Math.ceil(total / Number(pageSize))
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
};
|
|
1506
|
+
} catch (error) {
|
|
1507
|
+
strapi.log.error("Failed to list subscriptions (admin)", { error: getErrorMessage(error) });
|
|
1508
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to list subscriptions");
|
|
1509
|
+
}
|
|
1510
|
+
},
|
|
1511
|
+
/**
|
|
1512
|
+
* POST /admin/subscriptions/:id/cancel (admin only)
|
|
1513
|
+
*/
|
|
1514
|
+
async adminCancelSubscription(ctx) {
|
|
1515
|
+
try {
|
|
1516
|
+
const { id } = ctx.params;
|
|
1517
|
+
const { cancelAtPeriodEnd = false } = ctx.request.body || {};
|
|
1518
|
+
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
1519
|
+
const subscription2 = await stripeService2.cancelSubscription(id, cancelAtPeriodEnd);
|
|
1520
|
+
ctx.body = {
|
|
1521
|
+
success: true,
|
|
1522
|
+
subscriptionId: subscription2.id,
|
|
1523
|
+
status: subscription2.status,
|
|
1524
|
+
cancelAtPeriodEnd: subscription2.cancel_at_period_end
|
|
1525
|
+
};
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
strapi.log.error("Failed to cancel subscription (admin)", { error: getErrorMessage(error) });
|
|
1528
|
+
ctx.internalServerError(getErrorMessage(error) || "Failed to cancel subscription");
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1532
|
+
const stripeControllerFull = { ...stripeController, ...subscriptionController };
|
|
1225
1533
|
const controllers = {
|
|
1226
1534
|
controller,
|
|
1227
|
-
stripe:
|
|
1535
|
+
stripe: stripeControllerFull
|
|
1228
1536
|
};
|
|
1229
1537
|
const middlewares = {
|
|
1230
1538
|
/**
|
|
@@ -1423,6 +1731,37 @@ const routes$2 = {
|
|
|
1423
1731
|
config: {
|
|
1424
1732
|
policies: []
|
|
1425
1733
|
}
|
|
1734
|
+
},
|
|
1735
|
+
// Subscription Operations
|
|
1736
|
+
{
|
|
1737
|
+
method: "POST",
|
|
1738
|
+
path: "/subscriptions",
|
|
1739
|
+
handler: "stripe.createSubscription",
|
|
1740
|
+
config: { policies: [] }
|
|
1741
|
+
},
|
|
1742
|
+
{
|
|
1743
|
+
method: "GET",
|
|
1744
|
+
path: "/subscriptions",
|
|
1745
|
+
handler: "stripe.listSubscriptions",
|
|
1746
|
+
config: { policies: [] }
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
method: "GET",
|
|
1750
|
+
path: "/subscriptions/:id",
|
|
1751
|
+
handler: "stripe.getSubscriptionDetails",
|
|
1752
|
+
config: { policies: [] }
|
|
1753
|
+
},
|
|
1754
|
+
{
|
|
1755
|
+
method: "PUT",
|
|
1756
|
+
path: "/subscriptions/:id",
|
|
1757
|
+
handler: "stripe.updateSubscription",
|
|
1758
|
+
config: { policies: [] }
|
|
1759
|
+
},
|
|
1760
|
+
{
|
|
1761
|
+
method: "POST",
|
|
1762
|
+
path: "/subscriptions/:id/cancel",
|
|
1763
|
+
handler: "stripe.cancelSubscription",
|
|
1764
|
+
config: { policies: [] }
|
|
1426
1765
|
}
|
|
1427
1766
|
]
|
|
1428
1767
|
};
|
|
@@ -1546,6 +1885,25 @@ const routes$1 = {
|
|
|
1546
1885
|
policies: ["admin::isAuthenticatedAdmin"],
|
|
1547
1886
|
middlewares: []
|
|
1548
1887
|
}
|
|
1888
|
+
},
|
|
1889
|
+
// Admin Subscription Management
|
|
1890
|
+
{
|
|
1891
|
+
method: "GET",
|
|
1892
|
+
path: "/admin/subscriptions",
|
|
1893
|
+
handler: "stripe.adminListSubscriptions",
|
|
1894
|
+
config: {
|
|
1895
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
1896
|
+
middlewares: []
|
|
1897
|
+
}
|
|
1898
|
+
},
|
|
1899
|
+
{
|
|
1900
|
+
method: "POST",
|
|
1901
|
+
path: "/admin/subscriptions/:id/cancel",
|
|
1902
|
+
handler: "stripe.adminCancelSubscription",
|
|
1903
|
+
config: {
|
|
1904
|
+
policies: ["admin::isAuthenticatedAdmin"],
|
|
1905
|
+
middlewares: []
|
|
1906
|
+
}
|
|
1549
1907
|
}
|
|
1550
1908
|
]
|
|
1551
1909
|
};
|
|
@@ -1582,7 +1940,7 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1582
1940
|
if (!secretKey) {
|
|
1583
1941
|
throw new Error("Stripe Secret Key not found. Please configure it in the plugin settings or set the STRIPE_SECRET_KEY environment variable.");
|
|
1584
1942
|
}
|
|
1585
|
-
const apiVersion = stripeConfig.apiVersion || "
|
|
1943
|
+
const apiVersion = stripeConfig.apiVersion || "2026-03-25.dahlia";
|
|
1586
1944
|
stripe = new Stripe(secretKey, {
|
|
1587
1945
|
apiVersion
|
|
1588
1946
|
});
|
|
@@ -1749,14 +2107,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1749
2107
|
return refund;
|
|
1750
2108
|
} catch (error) {
|
|
1751
2109
|
const logger = getLogger();
|
|
1752
|
-
const errorMessage = error
|
|
2110
|
+
const errorMessage = getErrorMessage(error);
|
|
1753
2111
|
logger.error("Failed to create Stripe refund", {
|
|
1754
2112
|
message: errorMessage,
|
|
1755
2113
|
error: error instanceof Error ? {
|
|
1756
2114
|
message: error.message,
|
|
1757
2115
|
stack: error.stack,
|
|
1758
|
-
...
|
|
1759
|
-
} : error,
|
|
2116
|
+
..."raw" in error && { raw: error.raw }
|
|
2117
|
+
} : String(error),
|
|
1760
2118
|
params
|
|
1761
2119
|
});
|
|
1762
2120
|
throw error;
|
|
@@ -1779,8 +2137,8 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1779
2137
|
return stripe2.webhooks.constructEvent(verifiedPayload, signature, webhookSecret);
|
|
1780
2138
|
} catch (error) {
|
|
1781
2139
|
logger.error("Failed to construct webhook event", {
|
|
1782
|
-
message: error
|
|
1783
|
-
type: error.type
|
|
2140
|
+
message: getErrorMessage(error),
|
|
2141
|
+
...error instanceof Error && "type" in error && { type: error.type }
|
|
1784
2142
|
});
|
|
1785
2143
|
throw error;
|
|
1786
2144
|
}
|
|
@@ -1853,10 +2211,7 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1853
2211
|
};
|
|
1854
2212
|
const createOrderRecord = async (data) => {
|
|
1855
2213
|
return strapi2.documents("plugin::payment-plugin.order").create({
|
|
1856
|
-
data: {
|
|
1857
|
-
...data,
|
|
1858
|
-
order_status: data.order_status || "pending"
|
|
1859
|
-
}
|
|
2214
|
+
data: { ...data, order_status: data.order_status || "pending" }
|
|
1860
2215
|
});
|
|
1861
2216
|
};
|
|
1862
2217
|
const createStrapiPaymentRecord = async (paymentIntent) => {
|
|
@@ -1973,10 +2328,18 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1973
2328
|
switch (event.type) {
|
|
1974
2329
|
case "payment_intent.succeeded":
|
|
1975
2330
|
case "charge.succeeded": {
|
|
1976
|
-
|
|
1977
|
-
|
|
2331
|
+
let paymentIntentId;
|
|
2332
|
+
let metadata;
|
|
2333
|
+
if (event.type === "payment_intent.succeeded") {
|
|
2334
|
+
const piObj = event.data.object;
|
|
2335
|
+
paymentIntentId = piObj.id;
|
|
2336
|
+
metadata = piObj.metadata || {};
|
|
2337
|
+
} else {
|
|
2338
|
+
const chargeObj = event.data.object;
|
|
2339
|
+
paymentIntentId = chargeObj.payment_intent;
|
|
2340
|
+
metadata = chargeObj.metadata || {};
|
|
2341
|
+
}
|
|
1978
2342
|
if (!paymentIntentId) break;
|
|
1979
|
-
const metadata = obj.metadata || {};
|
|
1980
2343
|
await updateStrapiPayment(paymentIntentId, {
|
|
1981
2344
|
payment_status: "succeeded",
|
|
1982
2345
|
metadata
|
|
@@ -1984,12 +2347,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1984
2347
|
const existing = await strapi2.documents("plugin::payment-plugin.payment").findMany({
|
|
1985
2348
|
filters: { stripe_payment_intent_id: paymentIntentId }
|
|
1986
2349
|
});
|
|
1987
|
-
if (existing.length === 0
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
2350
|
+
if (existing.length === 0) {
|
|
2351
|
+
if (event.type === "payment_intent.succeeded") {
|
|
2352
|
+
await createStrapiPaymentRecord(event.data.object);
|
|
2353
|
+
} else {
|
|
2354
|
+
const stripeClient = await initializeStripe();
|
|
2355
|
+
const pi = await stripeClient.paymentIntents.retrieve(paymentIntentId);
|
|
2356
|
+
await createStrapiPaymentRecord(pi);
|
|
2357
|
+
}
|
|
1993
2358
|
}
|
|
1994
2359
|
const orderId = metadata.strapi_order_id;
|
|
1995
2360
|
if (orderId) {
|
|
@@ -2096,6 +2461,73 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2096
2461
|
});
|
|
2097
2462
|
break;
|
|
2098
2463
|
}
|
|
2464
|
+
case "customer.subscription.created": {
|
|
2465
|
+
const subscription2 = event.data.object;
|
|
2466
|
+
const strapiCustomerId = subscription2.metadata?.strapi_customer_id;
|
|
2467
|
+
const existing = await strapi2.documents("plugin::payment-plugin.subscription").findMany({
|
|
2468
|
+
filters: { stripe_subscription_id: subscription2.id }
|
|
2469
|
+
});
|
|
2470
|
+
if (existing.length === 0) {
|
|
2471
|
+
await createStrapiSubscriptionRecord(subscription2, strapiCustomerId);
|
|
2472
|
+
}
|
|
2473
|
+
logger.info("Subscription created via webhook", { subscriptionId: subscription2.id });
|
|
2474
|
+
break;
|
|
2475
|
+
}
|
|
2476
|
+
case "customer.subscription.updated": {
|
|
2477
|
+
const subscription2 = event.data.object;
|
|
2478
|
+
const sub = subscription2;
|
|
2479
|
+
const firstItem = subscription2.items.data[0];
|
|
2480
|
+
await updateStrapiSubscription(subscription2.id, {
|
|
2481
|
+
status: mapStripeSubscriptionStatus(subscription2.status),
|
|
2482
|
+
stripe_price_id: firstItem?.price?.id,
|
|
2483
|
+
stripe_product_id: firstItem?.price?.product,
|
|
2484
|
+
current_period_start: toISOOrNull(sub.current_period_start),
|
|
2485
|
+
current_period_end: toISOOrNull(sub.current_period_end),
|
|
2486
|
+
cancel_at_period_end: subscription2.cancel_at_period_end,
|
|
2487
|
+
canceled_at: toISOOrNull(subscription2.canceled_at),
|
|
2488
|
+
trial_start: toISOOrNull(subscription2.trial_start),
|
|
2489
|
+
trial_end: toISOOrNull(subscription2.trial_end),
|
|
2490
|
+
latest_invoice_id: typeof subscription2.latest_invoice === "string" ? subscription2.latest_invoice : subscription2.latest_invoice?.id,
|
|
2491
|
+
metadata: subscription2.metadata
|
|
2492
|
+
});
|
|
2493
|
+
logger.info("Subscription updated via webhook", { subscriptionId: subscription2.id, status: subscription2.status });
|
|
2494
|
+
break;
|
|
2495
|
+
}
|
|
2496
|
+
case "customer.subscription.deleted": {
|
|
2497
|
+
const subscription2 = event.data.object;
|
|
2498
|
+
await updateStrapiSubscription(subscription2.id, {
|
|
2499
|
+
status: "canceled",
|
|
2500
|
+
canceled_at: toISOOrNull(subscription2.canceled_at) || (/* @__PURE__ */ new Date()).toISOString()
|
|
2501
|
+
});
|
|
2502
|
+
logger.info("Subscription deleted via webhook", { subscriptionId: subscription2.id });
|
|
2503
|
+
break;
|
|
2504
|
+
}
|
|
2505
|
+
case "invoice.payment_succeeded": {
|
|
2506
|
+
const invoice = event.data.object;
|
|
2507
|
+
const subDetails = invoice.parent?.subscription_details;
|
|
2508
|
+
const subscriptionId = typeof subDetails?.subscription === "string" ? subDetails.subscription : subDetails?.subscription?.id ?? null;
|
|
2509
|
+
if (subscriptionId) {
|
|
2510
|
+
await updateStrapiSubscription(subscriptionId, {
|
|
2511
|
+
status: "active",
|
|
2512
|
+
latest_invoice_id: invoice.id
|
|
2513
|
+
});
|
|
2514
|
+
}
|
|
2515
|
+
logger.info("Invoice payment succeeded", { invoiceId: invoice.id, subscriptionId });
|
|
2516
|
+
break;
|
|
2517
|
+
}
|
|
2518
|
+
case "invoice.payment_failed": {
|
|
2519
|
+
const invoice = event.data.object;
|
|
2520
|
+
const subDetails = invoice.parent?.subscription_details;
|
|
2521
|
+
const subscriptionId = typeof subDetails?.subscription === "string" ? subDetails.subscription : subDetails?.subscription?.id ?? null;
|
|
2522
|
+
if (subscriptionId) {
|
|
2523
|
+
await updateStrapiSubscription(subscriptionId, {
|
|
2524
|
+
status: "past_due",
|
|
2525
|
+
latest_invoice_id: invoice.id
|
|
2526
|
+
});
|
|
2527
|
+
}
|
|
2528
|
+
logger.warn("Invoice payment failed", { invoiceId: invoice.id, subscriptionId });
|
|
2529
|
+
break;
|
|
2530
|
+
}
|
|
2099
2531
|
default:
|
|
2100
2532
|
logger.info("Unhandled webhook event type", { eventType: event.type });
|
|
2101
2533
|
}
|
|
@@ -2108,6 +2540,140 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2108
2540
|
throw error;
|
|
2109
2541
|
}
|
|
2110
2542
|
};
|
|
2543
|
+
const mapStripeSubscriptionStatus = (status) => {
|
|
2544
|
+
const valid = ["active", "canceled", "incomplete", "incomplete_expired", "past_due", "paused", "trialing", "unpaid"];
|
|
2545
|
+
return valid.includes(status) ? status : "incomplete";
|
|
2546
|
+
};
|
|
2547
|
+
const toISOOrNull = (ts) => {
|
|
2548
|
+
if (!ts) return null;
|
|
2549
|
+
return new Date(ts * 1e3).toISOString();
|
|
2550
|
+
};
|
|
2551
|
+
const createStrapiSubscriptionRecord = async (subscription2, strapiCustomerId) => {
|
|
2552
|
+
const logger = getLogger();
|
|
2553
|
+
try {
|
|
2554
|
+
const firstItem = subscription2.items.data[0];
|
|
2555
|
+
const price = firstItem?.price;
|
|
2556
|
+
const data = {
|
|
2557
|
+
stripe_subscription_id: subscription2.id,
|
|
2558
|
+
stripe_customer_id: subscription2.customer,
|
|
2559
|
+
stripe_price_id: price?.id || "",
|
|
2560
|
+
stripe_product_id: price?.product,
|
|
2561
|
+
status: mapStripeSubscriptionStatus(subscription2.status),
|
|
2562
|
+
current_period_start: toISOOrNull(subscription2.current_period_start) || void 0,
|
|
2563
|
+
current_period_end: toISOOrNull(subscription2.current_period_end) || void 0,
|
|
2564
|
+
cancel_at_period_end: subscription2.cancel_at_period_end,
|
|
2565
|
+
canceled_at: toISOOrNull(subscription2.canceled_at) || void 0,
|
|
2566
|
+
trial_start: toISOOrNull(subscription2.trial_start) || void 0,
|
|
2567
|
+
trial_end: toISOOrNull(subscription2.trial_end) || void 0,
|
|
2568
|
+
latest_invoice_id: typeof subscription2.latest_invoice === "string" ? subscription2.latest_invoice : subscription2.latest_invoice?.id,
|
|
2569
|
+
metadata: subscription2.metadata,
|
|
2570
|
+
customer: strapiCustomerId
|
|
2571
|
+
};
|
|
2572
|
+
const record = await strapi2.documents("plugin::payment-plugin.subscription").create({ data });
|
|
2573
|
+
logger.info("Created Strapi subscription record", {
|
|
2574
|
+
stripeSubscriptionId: subscription2.id,
|
|
2575
|
+
strapiCustomerId
|
|
2576
|
+
});
|
|
2577
|
+
return record;
|
|
2578
|
+
} catch (error) {
|
|
2579
|
+
logger.error("Failed to create Strapi subscription record", { error, subscriptionId: subscription2.id });
|
|
2580
|
+
throw error;
|
|
2581
|
+
}
|
|
2582
|
+
};
|
|
2583
|
+
const updateStrapiSubscription = async (stripeSubscriptionId, data) => {
|
|
2584
|
+
const logger = getLogger();
|
|
2585
|
+
try {
|
|
2586
|
+
const existing = await strapi2.documents("plugin::payment-plugin.subscription").findMany({
|
|
2587
|
+
filters: { stripe_subscription_id: stripeSubscriptionId }
|
|
2588
|
+
});
|
|
2589
|
+
if (existing.length > 0) {
|
|
2590
|
+
await strapi2.documents("plugin::payment-plugin.subscription").update({
|
|
2591
|
+
documentId: existing[0].documentId,
|
|
2592
|
+
data
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
} catch (error) {
|
|
2596
|
+
logger.error("Failed to update Strapi subscription record", { error, stripeSubscriptionId });
|
|
2597
|
+
}
|
|
2598
|
+
};
|
|
2599
|
+
const createSubscription = async (params) => {
|
|
2600
|
+
const stripe2 = await initializeStripe();
|
|
2601
|
+
const { strapiCustomerId, stripeCustomerId, priceId, trialPeriodDays, metadata = {}, defaultPaymentMethod } = params;
|
|
2602
|
+
const subscriptionParams = {
|
|
2603
|
+
customer: stripeCustomerId,
|
|
2604
|
+
items: [{ price: priceId }],
|
|
2605
|
+
payment_behavior: "default_incomplete",
|
|
2606
|
+
payment_settings: { save_default_payment_method: "on_subscription" },
|
|
2607
|
+
expand: ["latest_invoice.payment_intent", "pending_setup_intent"],
|
|
2608
|
+
metadata: { ...metadata, strapi_customer_id: strapiCustomerId }
|
|
2609
|
+
};
|
|
2610
|
+
if (trialPeriodDays) {
|
|
2611
|
+
subscriptionParams.trial_period_days = trialPeriodDays;
|
|
2612
|
+
}
|
|
2613
|
+
if (defaultPaymentMethod) {
|
|
2614
|
+
subscriptionParams.default_payment_method = defaultPaymentMethod;
|
|
2615
|
+
}
|
|
2616
|
+
const subscription2 = await stripe2.subscriptions.create(subscriptionParams);
|
|
2617
|
+
await createStrapiSubscriptionRecord(subscription2, strapiCustomerId);
|
|
2618
|
+
getLogger().info("Subscription created", { subscriptionId: subscription2.id, stripeCustomerId });
|
|
2619
|
+
return subscription2;
|
|
2620
|
+
};
|
|
2621
|
+
const cancelSubscription = async (stripeSubscriptionId, cancelAtPeriodEnd = false) => {
|
|
2622
|
+
const stripe2 = await initializeStripe();
|
|
2623
|
+
let subscription2;
|
|
2624
|
+
if (cancelAtPeriodEnd) {
|
|
2625
|
+
subscription2 = await stripe2.subscriptions.update(stripeSubscriptionId, { cancel_at_period_end: true });
|
|
2626
|
+
await updateStrapiSubscription(stripeSubscriptionId, { cancel_at_period_end: true });
|
|
2627
|
+
} else {
|
|
2628
|
+
subscription2 = await stripe2.subscriptions.cancel(stripeSubscriptionId);
|
|
2629
|
+
await updateStrapiSubscription(stripeSubscriptionId, {
|
|
2630
|
+
status: "canceled",
|
|
2631
|
+
canceled_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2632
|
+
cancel_at_period_end: false
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
getLogger().info("Subscription canceled", { stripeSubscriptionId, cancelAtPeriodEnd });
|
|
2636
|
+
return subscription2;
|
|
2637
|
+
};
|
|
2638
|
+
const updateSubscription = async (stripeSubscriptionId, params) => {
|
|
2639
|
+
const stripe2 = await initializeStripe();
|
|
2640
|
+
const updateParams = {};
|
|
2641
|
+
if (params.priceId) {
|
|
2642
|
+
const current = await stripe2.subscriptions.retrieve(stripeSubscriptionId);
|
|
2643
|
+
const itemId = current.items.data[0]?.id;
|
|
2644
|
+
updateParams.items = [{ id: itemId, price: params.priceId }];
|
|
2645
|
+
updateParams.proration_behavior = "create_prorations";
|
|
2646
|
+
}
|
|
2647
|
+
if (params.cancelAtPeriodEnd !== void 0) {
|
|
2648
|
+
updateParams.cancel_at_period_end = params.cancelAtPeriodEnd;
|
|
2649
|
+
}
|
|
2650
|
+
if (params.trialEnd !== void 0) {
|
|
2651
|
+
updateParams.trial_end = params.trialEnd;
|
|
2652
|
+
}
|
|
2653
|
+
if (params.metadata) {
|
|
2654
|
+
updateParams.metadata = params.metadata;
|
|
2655
|
+
}
|
|
2656
|
+
if (params.defaultPaymentMethod) {
|
|
2657
|
+
updateParams.default_payment_method = params.defaultPaymentMethod;
|
|
2658
|
+
}
|
|
2659
|
+
const subscription2 = await stripe2.subscriptions.update(stripeSubscriptionId, updateParams);
|
|
2660
|
+
const firstItem = subscription2.items.data[0];
|
|
2661
|
+
await updateStrapiSubscription(stripeSubscriptionId, {
|
|
2662
|
+
stripe_price_id: firstItem?.price?.id,
|
|
2663
|
+
stripe_product_id: firstItem?.price?.product,
|
|
2664
|
+
cancel_at_period_end: subscription2.cancel_at_period_end,
|
|
2665
|
+
status: mapStripeSubscriptionStatus(subscription2.status),
|
|
2666
|
+
metadata: subscription2.metadata
|
|
2667
|
+
});
|
|
2668
|
+
getLogger().info("Subscription updated", { stripeSubscriptionId });
|
|
2669
|
+
return subscription2;
|
|
2670
|
+
};
|
|
2671
|
+
const retrieveSubscription = async (stripeSubscriptionId) => {
|
|
2672
|
+
const stripe2 = await initializeStripe();
|
|
2673
|
+
return stripe2.subscriptions.retrieve(stripeSubscriptionId, {
|
|
2674
|
+
expand: ["latest_invoice", "customer"]
|
|
2675
|
+
});
|
|
2676
|
+
};
|
|
2111
2677
|
return {
|
|
2112
2678
|
initializeStripe,
|
|
2113
2679
|
createPaymentIntent,
|
|
@@ -2123,7 +2689,13 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2123
2689
|
createCustomerRecord,
|
|
2124
2690
|
createOrderRecord,
|
|
2125
2691
|
initializePaymentFlow,
|
|
2126
|
-
getStripe
|
|
2692
|
+
getStripe,
|
|
2693
|
+
createSubscription,
|
|
2694
|
+
cancelSubscription,
|
|
2695
|
+
updateSubscription,
|
|
2696
|
+
retrieveSubscription,
|
|
2697
|
+
createStrapiSubscriptionRecord,
|
|
2698
|
+
updateStrapiSubscription
|
|
2127
2699
|
};
|
|
2128
2700
|
};
|
|
2129
2701
|
const services = {
|