@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.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
|
};
|
|
@@ -1592,6 +1950,9 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1592
1950
|
const getStripe = () => {
|
|
1593
1951
|
return stripe;
|
|
1594
1952
|
};
|
|
1953
|
+
const setStripe = (instance) => {
|
|
1954
|
+
stripe = instance;
|
|
1955
|
+
};
|
|
1595
1956
|
const mapStripeStatusToStrapi = (stripeStatus) => {
|
|
1596
1957
|
switch (stripeStatus) {
|
|
1597
1958
|
case "succeeded":
|
|
@@ -1749,14 +2110,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1749
2110
|
return refund;
|
|
1750
2111
|
} catch (error) {
|
|
1751
2112
|
const logger = getLogger();
|
|
1752
|
-
const errorMessage = error
|
|
2113
|
+
const errorMessage = getErrorMessage(error);
|
|
1753
2114
|
logger.error("Failed to create Stripe refund", {
|
|
1754
2115
|
message: errorMessage,
|
|
1755
2116
|
error: error instanceof Error ? {
|
|
1756
2117
|
message: error.message,
|
|
1757
2118
|
stack: error.stack,
|
|
1758
|
-
...
|
|
1759
|
-
} : error,
|
|
2119
|
+
..."raw" in error && { raw: error.raw }
|
|
2120
|
+
} : String(error),
|
|
1760
2121
|
params
|
|
1761
2122
|
});
|
|
1762
2123
|
throw error;
|
|
@@ -1779,8 +2140,8 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1779
2140
|
return stripe2.webhooks.constructEvent(verifiedPayload, signature, webhookSecret);
|
|
1780
2141
|
} catch (error) {
|
|
1781
2142
|
logger.error("Failed to construct webhook event", {
|
|
1782
|
-
message: error
|
|
1783
|
-
type: error.type
|
|
2143
|
+
message: getErrorMessage(error),
|
|
2144
|
+
...error instanceof Error && "type" in error && { type: error.type }
|
|
1784
2145
|
});
|
|
1785
2146
|
throw error;
|
|
1786
2147
|
}
|
|
@@ -1853,10 +2214,7 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1853
2214
|
};
|
|
1854
2215
|
const createOrderRecord = async (data) => {
|
|
1855
2216
|
return strapi2.documents("plugin::payment-plugin.order").create({
|
|
1856
|
-
data: {
|
|
1857
|
-
...data,
|
|
1858
|
-
order_status: data.order_status || "pending"
|
|
1859
|
-
}
|
|
2217
|
+
data: { ...data, order_status: data.order_status || "pending" }
|
|
1860
2218
|
});
|
|
1861
2219
|
};
|
|
1862
2220
|
const createStrapiPaymentRecord = async (paymentIntent) => {
|
|
@@ -1973,10 +2331,18 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1973
2331
|
switch (event.type) {
|
|
1974
2332
|
case "payment_intent.succeeded":
|
|
1975
2333
|
case "charge.succeeded": {
|
|
1976
|
-
|
|
1977
|
-
|
|
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
|
+
}
|
|
1978
2345
|
if (!paymentIntentId) break;
|
|
1979
|
-
const metadata = obj.metadata || {};
|
|
1980
2346
|
await updateStrapiPayment(paymentIntentId, {
|
|
1981
2347
|
payment_status: "succeeded",
|
|
1982
2348
|
metadata
|
|
@@ -1984,12 +2350,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1984
2350
|
const existing = await strapi2.documents("plugin::payment-plugin.payment").findMany({
|
|
1985
2351
|
filters: { stripe_payment_intent_id: paymentIntentId }
|
|
1986
2352
|
});
|
|
1987
|
-
if (existing.length === 0
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
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
|
+
}
|
|
1993
2361
|
}
|
|
1994
2362
|
const orderId = metadata.strapi_order_id;
|
|
1995
2363
|
if (orderId) {
|
|
@@ -2096,6 +2464,73 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2096
2464
|
});
|
|
2097
2465
|
break;
|
|
2098
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
|
+
}
|
|
2099
2534
|
default:
|
|
2100
2535
|
logger.info("Unhandled webhook event type", { eventType: event.type });
|
|
2101
2536
|
}
|
|
@@ -2108,6 +2543,140 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2108
2543
|
throw error;
|
|
2109
2544
|
}
|
|
2110
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
|
+
};
|
|
2111
2680
|
return {
|
|
2112
2681
|
initializeStripe,
|
|
2113
2682
|
createPaymentIntent,
|
|
@@ -2123,7 +2692,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
2123
2692
|
createCustomerRecord,
|
|
2124
2693
|
createOrderRecord,
|
|
2125
2694
|
initializePaymentFlow,
|
|
2126
|
-
getStripe
|
|
2695
|
+
getStripe,
|
|
2696
|
+
setStripe,
|
|
2697
|
+
createSubscription,
|
|
2698
|
+
cancelSubscription,
|
|
2699
|
+
updateSubscription,
|
|
2700
|
+
retrieveSubscription,
|
|
2701
|
+
createStrapiSubscriptionRecord,
|
|
2702
|
+
updateStrapiSubscription
|
|
2127
2703
|
};
|
|
2128
2704
|
};
|
|
2129
2705
|
const services = {
|