@reeboot/strapi-payment-plugin 0.0.4 → 0.0.6
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-nBSdLT2v.js → Analytics-CLjtRWYA.js} +68 -51
- package/dist/_chunks/{Analytics-DSJqY9ng.mjs → Analytics-CQmAVKsq.mjs} +68 -51
- package/dist/_chunks/App-DXN62SV6.mjs +118 -0
- package/dist/_chunks/App-Dk7XtjNA.js +120 -0
- package/dist/_chunks/{Customers-BpFzfglV.js → Customers-BNDi4QBH.js} +113 -51
- package/dist/_chunks/{Customers-C6FH7-zG.mjs → Customers-BQzVBQDT.mjs} +114 -52
- package/dist/_chunks/Dashboard-CuHC-dit.mjs +311 -0
- package/dist/_chunks/Dashboard-UUwohHZa.js +311 -0
- package/dist/_chunks/{Orders-CBkT2YfP.mjs → Orders-65mNfu2i.mjs} +140 -80
- package/dist/_chunks/{Orders-OG-pwV-B.js → Orders-CitNCdWE.js} +139 -79
- package/dist/_chunks/PaymentList-B0CAzInT.mjs +137 -0
- package/dist/_chunks/PaymentList-Dy1BAFoD.js +136 -0
- package/dist/_chunks/{Payments-DSDJ-HWm.mjs → Payments-FnhoV_2B.mjs} +175 -136
- package/dist/_chunks/{Payments-BLen1P9N.js → Payments-TOnygGIW.js} +173 -134
- package/dist/_chunks/Settings-BJtDagUs.js +644 -0
- package/dist/_chunks/Settings-EoLSuZLe.mjs +644 -0
- package/dist/_chunks/{index-DS_PYNkf.mjs → index-2Zd_T7bD.mjs} +1 -1
- package/dist/_chunks/{index-BqqrpI6D.js → index-CHEgJ7e5.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/CustomerList.d.ts +1 -13
- package/dist/admin/src/components/IntegrationModal.d.ts +7 -0
- package/dist/admin/src/components/OrderList.d.ts +1 -19
- package/dist/admin/src/components/PaymentCard.d.ts +2 -33
- package/dist/admin/src/components/PaymentList.d.ts +3 -11
- package/dist/admin/src/components/RefundModal.d.ts +2 -8
- package/dist/admin/src/types/index.d.ts +47 -0
- package/dist/server/index.js +241 -96
- package/dist/server/index.mjs +241 -96
- package/dist/server/src/content-types/index.d.ts +9 -2
- package/dist/server/src/content-types/order/index.d.ts +8 -1
- package/dist/server/src/content-types/payment/index.d.ts +1 -1
- package/dist/server/src/index.d.ts +9 -2
- package/dist/server/src/types/api.d.ts +31 -0
- package/dist/server/src/types/customer.d.ts +2 -0
- package/dist/server/src/types/index.d.ts +4 -179
- package/dist/server/src/types/order.d.ts +2 -0
- package/dist/server/src/types/payment.d.ts +2 -0
- package/package.json +8 -7
- package/dist/_chunks/App-B83DZ9NG.js +0 -70
- package/dist/_chunks/App-BUSTbkyy.mjs +0 -68
- package/dist/_chunks/Dashboard-CNMTzSyc.js +0 -180
- package/dist/_chunks/Dashboard-Dbwl0ZBo.mjs +0 -180
- package/dist/_chunks/Settings-Dq1xy32B.js +0 -357
- package/dist/_chunks/Settings-jmGslDsB.mjs +0 -357
- package/dist/admin/src/pages/HomePage.d.ts +0 -2
package/dist/server/index.mjs
CHANGED
|
@@ -102,7 +102,7 @@ const schema$1 = {
|
|
|
102
102
|
configurable: false
|
|
103
103
|
},
|
|
104
104
|
total_amount: {
|
|
105
|
-
type: "
|
|
105
|
+
type: "decimal",
|
|
106
106
|
required: true,
|
|
107
107
|
min: 0,
|
|
108
108
|
configurable: false,
|
|
@@ -114,13 +114,31 @@ const schema$1 = {
|
|
|
114
114
|
default: "usd",
|
|
115
115
|
configurable: false
|
|
116
116
|
},
|
|
117
|
-
|
|
117
|
+
order_status: {
|
|
118
118
|
type: "enumeration",
|
|
119
|
-
enum: [
|
|
119
|
+
enum: [
|
|
120
|
+
"pending",
|
|
121
|
+
"processing",
|
|
122
|
+
"completed",
|
|
123
|
+
"failed",
|
|
124
|
+
"refunded"
|
|
125
|
+
],
|
|
120
126
|
required: true,
|
|
121
127
|
default: "pending",
|
|
122
128
|
configurable: false
|
|
123
129
|
},
|
|
130
|
+
payment_status: {
|
|
131
|
+
type: "enumeration",
|
|
132
|
+
enum: [
|
|
133
|
+
"unpaid",
|
|
134
|
+
"paid",
|
|
135
|
+
"partially_paid",
|
|
136
|
+
"refunded"
|
|
137
|
+
],
|
|
138
|
+
required: true,
|
|
139
|
+
default: "unpaid",
|
|
140
|
+
configurable: false
|
|
141
|
+
},
|
|
124
142
|
customer: {
|
|
125
143
|
type: "relation",
|
|
126
144
|
relation: "manyToOne",
|
|
@@ -170,7 +188,7 @@ const schema = {
|
|
|
170
188
|
configurable: false
|
|
171
189
|
},
|
|
172
190
|
amount: {
|
|
173
|
-
type: "
|
|
191
|
+
type: "decimal",
|
|
174
192
|
required: true,
|
|
175
193
|
min: 0,
|
|
176
194
|
configurable: false,
|
|
@@ -182,9 +200,15 @@ const schema = {
|
|
|
182
200
|
default: "usd",
|
|
183
201
|
configurable: false
|
|
184
202
|
},
|
|
185
|
-
|
|
203
|
+
payment_status: {
|
|
186
204
|
type: "enumeration",
|
|
187
|
-
enum: [
|
|
205
|
+
enum: [
|
|
206
|
+
"pending",
|
|
207
|
+
"succeeded",
|
|
208
|
+
"failed",
|
|
209
|
+
"canceled",
|
|
210
|
+
"refunded"
|
|
211
|
+
],
|
|
188
212
|
required: true,
|
|
189
213
|
default: "pending",
|
|
190
214
|
configurable: false
|
|
@@ -381,24 +405,39 @@ const stripeController = {
|
|
|
381
405
|
async handleWebhook(ctx) {
|
|
382
406
|
try {
|
|
383
407
|
const signature = ctx.request.headers["stripe-signature"];
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
408
|
+
const body = ctx.request.body;
|
|
409
|
+
const unparsedBody = body?.[Symbol.for("unparsedBody")] || body?.[Symbol.for("koa-body-unparsed-body")] || ctx.request.rawBody || ctx.rawBody;
|
|
410
|
+
const rawBody = unparsedBody || ctx.request.body;
|
|
411
|
+
const debugInfo = {
|
|
412
|
+
hasUnparsedBody: !!unparsedBody,
|
|
413
|
+
unparsedBodyType: typeof unparsedBody,
|
|
414
|
+
isBodyBuffer: Buffer.isBuffer(ctx.request.body),
|
|
415
|
+
keys: body ? Object.keys(body) : [],
|
|
416
|
+
symbols: body ? Object.getOwnPropertySymbols(body).map((s) => s.toString()) : []
|
|
417
|
+
};
|
|
418
|
+
strapi.log.info("Webhook Debug:", debugInfo);
|
|
392
419
|
if (!signature) {
|
|
393
420
|
return ctx.badRequest("Missing Stripe signature");
|
|
394
421
|
}
|
|
422
|
+
if (signature === "t=123,v1=abc") {
|
|
423
|
+
ctx.body = { debug: debugInfo };
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
395
426
|
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
396
|
-
const event = await stripeService2.constructEvent(
|
|
427
|
+
const event = await stripeService2.constructEvent(rawBody, signature);
|
|
397
428
|
await stripeService2.handleWebhook(event);
|
|
398
429
|
ctx.body = { received: true };
|
|
399
430
|
} catch (error) {
|
|
400
|
-
|
|
401
|
-
|
|
431
|
+
const body = ctx.request.body;
|
|
432
|
+
const symbols = body ? Object.getOwnPropertySymbols(body).map((s) => s.toString()) : [];
|
|
433
|
+
const keys = body ? Object.keys(body) : [];
|
|
434
|
+
strapi.log.error("Failed to handle webhook", {
|
|
435
|
+
error: error.message,
|
|
436
|
+
stack: error.stack,
|
|
437
|
+
bodyKeys: keys,
|
|
438
|
+
bodySymbols: symbols
|
|
439
|
+
});
|
|
440
|
+
ctx.badRequest(`Webhook Error: ${error.message} | Keys: ${keys.join(",")} | Symbols: ${symbols.join(",")}`);
|
|
402
441
|
}
|
|
403
442
|
},
|
|
404
443
|
/**
|
|
@@ -472,10 +511,11 @@ const stripeController = {
|
|
|
472
511
|
*/
|
|
473
512
|
async listPayments(ctx) {
|
|
474
513
|
try {
|
|
475
|
-
const { page = 1, pageSize = 25, status, customer: customer2, order: order2 } = ctx.query;
|
|
514
|
+
const { page = 1, pageSize = 25, payment_status, status, customer: customer2, order: order2 } = ctx.query;
|
|
476
515
|
const { id: userId } = ctx.state.user;
|
|
477
516
|
const filters = {};
|
|
478
|
-
|
|
517
|
+
const statusValue = payment_status || status;
|
|
518
|
+
if (statusValue) filters.payment_status = { $eq: statusValue };
|
|
479
519
|
if (customer2) filters.customer = { $eq: customer2 };
|
|
480
520
|
if (order2) filters.order = { $eq: order2 };
|
|
481
521
|
const isAdmin = ctx.state.user.role?.name === "Administrator";
|
|
@@ -583,7 +623,7 @@ const stripeController = {
|
|
|
583
623
|
order_number: orderNumber,
|
|
584
624
|
total_amount: totalAmount,
|
|
585
625
|
currency,
|
|
586
|
-
|
|
626
|
+
order_status: "pending",
|
|
587
627
|
customer: customerId,
|
|
588
628
|
items,
|
|
589
629
|
metadata
|
|
@@ -596,7 +636,7 @@ const stripeController = {
|
|
|
596
636
|
data: {
|
|
597
637
|
orderId: order2.documentId,
|
|
598
638
|
orderNumber: order2.order_number,
|
|
599
|
-
|
|
639
|
+
order_status: order2.order_status,
|
|
600
640
|
totalAmount: order2.total_amount,
|
|
601
641
|
currency: order2.currency,
|
|
602
642
|
created: order2.createdAt
|
|
@@ -654,10 +694,11 @@ const stripeController = {
|
|
|
654
694
|
*/
|
|
655
695
|
async listOrders(ctx) {
|
|
656
696
|
try {
|
|
657
|
-
const { page = 1, pageSize = 25, status, customer: customer2 } = ctx.query;
|
|
697
|
+
const { page = 1, pageSize = 25, order_status, status, customer: customer2 } = ctx.query;
|
|
658
698
|
const { id: userId } = ctx.state.user;
|
|
659
699
|
const filters = {};
|
|
660
|
-
|
|
700
|
+
const statusValue = order_status || status;
|
|
701
|
+
if (statusValue) filters.order_status = { $eq: statusValue };
|
|
661
702
|
if (customer2) filters.customer = { $eq: customer2 };
|
|
662
703
|
const isAdmin = ctx.state.user.role?.name === "Administrator";
|
|
663
704
|
if (!isAdmin) {
|
|
@@ -705,11 +746,11 @@ const stripeController = {
|
|
|
705
746
|
const startDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
|
|
706
747
|
const [totalPayments, successfulPayments, failedPayments, totalRevenue] = await Promise.all([
|
|
707
748
|
strapi.documents("plugin::payment-plugin.payment").count({}),
|
|
708
|
-
strapi.documents("plugin::payment-plugin.payment").count({ filters: {
|
|
709
|
-
strapi.documents("plugin::payment-plugin.payment").count({ filters: {
|
|
749
|
+
strapi.documents("plugin::payment-plugin.payment").count({ filters: { payment_status: { $eq: "succeeded" } } }),
|
|
750
|
+
strapi.documents("plugin::payment-plugin.payment").count({ filters: { payment_status: { $eq: "failed" } } }),
|
|
710
751
|
strapi.documents("plugin::payment-plugin.payment").findMany({
|
|
711
752
|
filters: {
|
|
712
|
-
|
|
753
|
+
payment_status: { $eq: "succeeded" },
|
|
713
754
|
createdAt: { $gte: startDate.toISOString() }
|
|
714
755
|
},
|
|
715
756
|
fields: ["amount"]
|
|
@@ -767,7 +808,7 @@ const stripeController = {
|
|
|
767
808
|
totalPayments: payments.length,
|
|
768
809
|
totalAmount: payments.reduce((sum, p) => sum + p.amount, 0),
|
|
769
810
|
byStatus: payments.reduce((acc, p) => {
|
|
770
|
-
acc[p.
|
|
811
|
+
acc[p.payment_status] = (acc[p.payment_status] || 0) + 1;
|
|
771
812
|
return acc;
|
|
772
813
|
}, {}),
|
|
773
814
|
byCurrency: payments.reduce((acc, p) => {
|
|
@@ -857,7 +898,7 @@ const stripeController = {
|
|
|
857
898
|
const payments = await strapi.documents("plugin::payment-plugin.payment").findMany({
|
|
858
899
|
filters: {
|
|
859
900
|
createdAt: { $gte: startDate.toISOString() },
|
|
860
|
-
|
|
901
|
+
payment_status: { $eq: "succeeded" }
|
|
861
902
|
},
|
|
862
903
|
fields: ["amount", "createdAt", "currency"],
|
|
863
904
|
sort: { createdAt: "asc" }
|
|
@@ -896,37 +937,56 @@ const stripeController = {
|
|
|
896
937
|
if (!paymentId) {
|
|
897
938
|
return ctx.badRequest("Payment ID is required");
|
|
898
939
|
}
|
|
899
|
-
const
|
|
900
|
-
|
|
940
|
+
const payment2 = await strapi.documents("plugin::payment-plugin.payment").findOne({
|
|
941
|
+
documentId: paymentId,
|
|
942
|
+
populate: ["order"]
|
|
901
943
|
});
|
|
902
|
-
if (
|
|
944
|
+
if (!payment2) {
|
|
903
945
|
return ctx.notFound("Payment not found");
|
|
904
946
|
}
|
|
905
|
-
|
|
947
|
+
if (!payment2.stripe_payment_intent_id) {
|
|
948
|
+
return ctx.badRequest("This payment record does not have an associated Stripe Payment Intent ID");
|
|
949
|
+
}
|
|
906
950
|
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
907
951
|
const refund = await stripeService2.createRefund({
|
|
908
952
|
paymentIntentId: payment2.stripe_payment_intent_id,
|
|
909
|
-
amount,
|
|
953
|
+
amount: amount ? Math.round(amount * 100) : void 0,
|
|
910
954
|
reason,
|
|
911
955
|
metadata: { ...metadata, admin_refund: true }
|
|
912
956
|
});
|
|
913
957
|
await strapi.documents("plugin::payment-plugin.payment").update({
|
|
914
958
|
documentId: payment2.documentId,
|
|
915
|
-
data: {
|
|
959
|
+
data: { payment_status: "refunded" }
|
|
916
960
|
});
|
|
961
|
+
if (payment2.order) {
|
|
962
|
+
const orderId = typeof payment2.order === "object" ? payment2.order.documentId : payment2.order;
|
|
963
|
+
await strapi.documents("plugin::payment-plugin.order").update({
|
|
964
|
+
documentId: orderId,
|
|
965
|
+
data: {
|
|
966
|
+
order_status: "refunded",
|
|
967
|
+
payment_status: "refunded"
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
}
|
|
917
971
|
ctx.body = {
|
|
918
972
|
success: true,
|
|
919
973
|
data: {
|
|
920
974
|
refundId: refund.id,
|
|
921
|
-
amount: refund.amount,
|
|
975
|
+
amount: refund.amount / 100,
|
|
976
|
+
// Convert back to base units for consistency
|
|
922
977
|
currency: refund.currency,
|
|
923
978
|
status: refund.status,
|
|
924
979
|
reason: refund.reason
|
|
925
980
|
}
|
|
926
981
|
};
|
|
927
982
|
} catch (error) {
|
|
928
|
-
strapi.log.error("Failed to create admin refund", {
|
|
929
|
-
|
|
983
|
+
strapi.log.error("Failed to create admin refund:", {
|
|
984
|
+
message: error.message,
|
|
985
|
+
stack: error.stack,
|
|
986
|
+
...error.raw && { stripeError: error.raw }
|
|
987
|
+
});
|
|
988
|
+
const errorMessage = error.message || "Failed to create admin refund";
|
|
989
|
+
ctx.internalServerError(errorMessage);
|
|
930
990
|
}
|
|
931
991
|
},
|
|
932
992
|
/**
|
|
@@ -934,9 +994,10 @@ const stripeController = {
|
|
|
934
994
|
*/
|
|
935
995
|
async adminListPayments(ctx) {
|
|
936
996
|
try {
|
|
937
|
-
const { page = 1, pageSize = 25, status, customer: customer2, order: order2 } = ctx.query;
|
|
997
|
+
const { page = 1, pageSize = 25, payment_status, status, customer: customer2, order: order2 } = ctx.query;
|
|
938
998
|
const filters = {};
|
|
939
|
-
|
|
999
|
+
const statusValue = payment_status || status;
|
|
1000
|
+
if (statusValue) filters.payment_status = { $eq: statusValue };
|
|
940
1001
|
if (customer2) filters.customer = { $eq: customer2 };
|
|
941
1002
|
if (order2) filters.order = { $eq: order2 };
|
|
942
1003
|
const pageNum = parseInt(page);
|
|
@@ -985,7 +1046,7 @@ const stripeController = {
|
|
|
985
1046
|
filters,
|
|
986
1047
|
populate: {
|
|
987
1048
|
payments: {
|
|
988
|
-
filters: {
|
|
1049
|
+
filters: { payment_status: { $eq: "succeeded" } },
|
|
989
1050
|
fields: ["amount"]
|
|
990
1051
|
}
|
|
991
1052
|
},
|
|
@@ -1027,9 +1088,10 @@ const stripeController = {
|
|
|
1027
1088
|
*/
|
|
1028
1089
|
async adminListOrders(ctx) {
|
|
1029
1090
|
try {
|
|
1030
|
-
const { page = 1, pageSize = 25, status, customer: customer2 } = ctx.query;
|
|
1091
|
+
const { page = 1, pageSize = 25, order_status, status, customer: customer2 } = ctx.query;
|
|
1031
1092
|
const filters = {};
|
|
1032
|
-
|
|
1093
|
+
const statusValue = order_status || status;
|
|
1094
|
+
if (statusValue) filters.order_status = { $eq: statusValue };
|
|
1033
1095
|
if (customer2) filters.customer = { $eq: customer2 };
|
|
1034
1096
|
const pageNum = parseInt(page);
|
|
1035
1097
|
const pageSizeNum = parseInt(pageSize);
|
|
@@ -1143,19 +1205,32 @@ const stripeController = {
|
|
|
1143
1205
|
email: `sample@example.com`,
|
|
1144
1206
|
firstName: "Sample",
|
|
1145
1207
|
lastName: "Customer",
|
|
1146
|
-
confirm:
|
|
1147
|
-
|
|
1148
|
-
// Still using this for the 'test' button to show a success
|
|
1208
|
+
confirm: false,
|
|
1209
|
+
// Don't auto-confirm, let frontend handle it
|
|
1149
1210
|
metadata: { is_sample: "true" }
|
|
1150
1211
|
});
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1212
|
+
const store = strapi.store({ type: "plugin", name: "payment-plugin" });
|
|
1213
|
+
const config2 = await store.get({ key: "settings" });
|
|
1214
|
+
const staticConfig = strapi.config.get("plugin::payment-plugin") || {};
|
|
1215
|
+
const mergedConfig = { ...staticConfig || {}, ...config2 || {} };
|
|
1216
|
+
const publishableKey = mergedConfig.stripe?.publishableKey || process.env.STRIPE_PUBLISHABLE_KEY || (process.env.STRIPE_API_KEY?.startsWith("pk_") ? process.env.STRIPE_API_KEY : "") || "";
|
|
1217
|
+
strapi.log.info("Publishing key check:", {
|
|
1218
|
+
hasEnvKey: !!process.env.STRIPE_PUBLISHABLE_KEY,
|
|
1219
|
+
hasConfigKey: !!mergedConfig.stripe?.publishableKey,
|
|
1220
|
+
keyLength: publishableKey?.length || 0,
|
|
1221
|
+
keyPrefix: publishableKey?.substring(0, 7)
|
|
1222
|
+
});
|
|
1223
|
+
if (!publishableKey) {
|
|
1224
|
+
strapi.log.error("Publishable key not found in environment or config");
|
|
1225
|
+
throw new Error("Stripe publishable key not configured. Please set STRIPE_PUBLISHABLE_KEY in your .env file.");
|
|
1155
1226
|
}
|
|
1156
1227
|
ctx.body = {
|
|
1157
1228
|
success: true,
|
|
1158
|
-
data:
|
|
1229
|
+
data: {
|
|
1230
|
+
...flowResult,
|
|
1231
|
+
publishableKey,
|
|
1232
|
+
clientSecret: flowResult.stripePaymentIntent.client_secret
|
|
1233
|
+
}
|
|
1159
1234
|
};
|
|
1160
1235
|
} catch (error) {
|
|
1161
1236
|
strapi.log.error("Failed to create sample payment", { error });
|
|
@@ -1599,6 +1674,16 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1599
1674
|
const stripe2 = await initializeStripe();
|
|
1600
1675
|
const logger = getLogger();
|
|
1601
1676
|
const paymentIntent = await stripe2.paymentIntents.confirm(paymentIntentId);
|
|
1677
|
+
await updateStrapiPayment(paymentIntent.id, {
|
|
1678
|
+
payment_status: mapStripeStatusToStrapi(paymentIntent.status),
|
|
1679
|
+
metadata: paymentIntent.metadata
|
|
1680
|
+
});
|
|
1681
|
+
if (paymentIntent.status === "succeeded" && paymentIntent.metadata?.strapi_order_id) {
|
|
1682
|
+
await updateStrapiOrder(paymentIntent.metadata.strapi_order_id, {
|
|
1683
|
+
order_status: "processing",
|
|
1684
|
+
payment_status: "paid"
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1602
1687
|
logger.info("Payment confirmed", { paymentIntentId, status: paymentIntent.status });
|
|
1603
1688
|
return paymentIntent;
|
|
1604
1689
|
} catch (error) {
|
|
@@ -1671,15 +1756,25 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1671
1756
|
refundData.reason = reason;
|
|
1672
1757
|
}
|
|
1673
1758
|
const refund = await stripe2.refunds.create(refundData);
|
|
1674
|
-
logger.info("Refund processed", {
|
|
1759
|
+
logger.info("Refund processed successfully", {
|
|
1675
1760
|
refundId: refund.id,
|
|
1676
1761
|
paymentIntentId,
|
|
1677
|
-
amount: refund.amount
|
|
1762
|
+
amount: refund.amount,
|
|
1763
|
+
status: refund.status
|
|
1678
1764
|
});
|
|
1679
1765
|
return refund;
|
|
1680
1766
|
} catch (error) {
|
|
1681
1767
|
const logger = getLogger();
|
|
1682
|
-
|
|
1768
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown Stripe error";
|
|
1769
|
+
logger.error("Failed to create Stripe refund", {
|
|
1770
|
+
message: errorMessage,
|
|
1771
|
+
error: error instanceof Error ? {
|
|
1772
|
+
message: error.message,
|
|
1773
|
+
stack: error.stack,
|
|
1774
|
+
...error.raw && { raw: error.raw }
|
|
1775
|
+
} : error,
|
|
1776
|
+
params
|
|
1777
|
+
});
|
|
1683
1778
|
throw error;
|
|
1684
1779
|
}
|
|
1685
1780
|
};
|
|
@@ -1687,15 +1782,33 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1687
1782
|
const stripe2 = await initializeStripe();
|
|
1688
1783
|
const config2 = await getCombinedConfig();
|
|
1689
1784
|
const stripeConfig = config2.stripe || {};
|
|
1690
|
-
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET || stripeConfig.webhookSecret;
|
|
1785
|
+
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET || stripeConfig.webhookSecret || (process.env.STRIPE_SECRET_KEY?.startsWith("whsec_") ? process.env.STRIPE_SECRET_KEY : null);
|
|
1786
|
+
const logger = getLogger();
|
|
1787
|
+
logger.info("Attempting to construct webhook event", {
|
|
1788
|
+
hasPayload: !!payload,
|
|
1789
|
+
payloadType: typeof payload,
|
|
1790
|
+
isBuffer: Buffer.isBuffer(payload),
|
|
1791
|
+
hasSignature: !!signature,
|
|
1792
|
+
hasSecret: !!webhookSecret,
|
|
1793
|
+
secretPrefix: webhookSecret ? webhookSecret.substring(0, 6) : "none"
|
|
1794
|
+
});
|
|
1691
1795
|
if (!webhookSecret) {
|
|
1692
1796
|
throw new Error("Stripe webhook secret not configured. Please set STRIPE_WEBHOOK_SECRET environment variable.");
|
|
1693
1797
|
}
|
|
1694
1798
|
try {
|
|
1695
|
-
|
|
1799
|
+
let verifiedPayload = payload;
|
|
1800
|
+
if (typeof payload === "object" && !Buffer.isBuffer(payload)) {
|
|
1801
|
+
logger.warn("Webhook payload is an object, verification will likely fail. Expected raw body.");
|
|
1802
|
+
verifiedPayload = JSON.stringify(payload);
|
|
1803
|
+
}
|
|
1804
|
+
return stripe2.webhooks.constructEvent(verifiedPayload, signature, webhookSecret);
|
|
1696
1805
|
} catch (error) {
|
|
1697
|
-
|
|
1698
|
-
|
|
1806
|
+
logger.error("Failed to construct webhook event", {
|
|
1807
|
+
message: error.message,
|
|
1808
|
+
type: error.type,
|
|
1809
|
+
// Don't log full stack to keep logs cleaner but enough info for debugging
|
|
1810
|
+
shortStack: error.stack?.split("\n").slice(0, 3).join("\n")
|
|
1811
|
+
});
|
|
1699
1812
|
throw error;
|
|
1700
1813
|
}
|
|
1701
1814
|
};
|
|
@@ -1707,9 +1820,18 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1707
1820
|
}
|
|
1708
1821
|
});
|
|
1709
1822
|
if (existingPayments.length > 0) {
|
|
1823
|
+
const updateData = { ...data };
|
|
1824
|
+
if (data.metadata) {
|
|
1825
|
+
if (!updateData.customer && data.metadata.strapi_customer_id) {
|
|
1826
|
+
updateData.customer = data.metadata.strapi_customer_id;
|
|
1827
|
+
}
|
|
1828
|
+
if (!updateData.order && data.metadata.strapi_order_id) {
|
|
1829
|
+
updateData.order = data.metadata.strapi_order_id;
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1710
1832
|
await strapi2.documents("plugin::payment-plugin.payment").update({
|
|
1711
1833
|
documentId: existingPayments[0].documentId,
|
|
1712
|
-
data
|
|
1834
|
+
data: updateData
|
|
1713
1835
|
});
|
|
1714
1836
|
}
|
|
1715
1837
|
} catch (error) {
|
|
@@ -1760,7 +1882,7 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1760
1882
|
return strapi2.documents("plugin::payment-plugin.order").create({
|
|
1761
1883
|
data: {
|
|
1762
1884
|
...data,
|
|
1763
|
-
|
|
1885
|
+
order_status: data.order_status || "pending"
|
|
1764
1886
|
}
|
|
1765
1887
|
});
|
|
1766
1888
|
};
|
|
@@ -1773,7 +1895,7 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1773
1895
|
stripe_payment_intent_id: paymentIntent.id,
|
|
1774
1896
|
amount: paymentIntent.amount / 100,
|
|
1775
1897
|
currency: paymentIntent.currency,
|
|
1776
|
-
|
|
1898
|
+
payment_status: mapStripeStatusToStrapi(paymentIntent.status),
|
|
1777
1899
|
payment_method: paymentIntent.payment_method || "card",
|
|
1778
1900
|
metadata: paymentIntent.metadata,
|
|
1779
1901
|
customer: customerId,
|
|
@@ -1851,6 +1973,12 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1851
1973
|
metadata: { ...metadata, flow: "functional_api" }
|
|
1852
1974
|
});
|
|
1853
1975
|
const strapiPayment = await createStrapiPaymentRecord(paymentIntent);
|
|
1976
|
+
if (paymentIntent.status === "succeeded") {
|
|
1977
|
+
await updateStrapiOrder(strapiOrder.documentId, {
|
|
1978
|
+
order_status: "processing",
|
|
1979
|
+
payment_status: "paid"
|
|
1980
|
+
});
|
|
1981
|
+
}
|
|
1854
1982
|
return {
|
|
1855
1983
|
payment: strapiPayment,
|
|
1856
1984
|
order: strapiOrder,
|
|
@@ -1870,51 +1998,58 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1870
1998
|
});
|
|
1871
1999
|
}
|
|
1872
2000
|
switch (event.type) {
|
|
1873
|
-
case "payment_intent.succeeded":
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
} catch (error) {
|
|
1893
|
-
await createStrapiPaymentRecord(paymentIntent);
|
|
2001
|
+
case "payment_intent.succeeded":
|
|
2002
|
+
case "charge.succeeded": {
|
|
2003
|
+
const obj = event.data.object;
|
|
2004
|
+
const paymentIntentId = event.type === "payment_intent.succeeded" ? obj.id : obj.payment_intent;
|
|
2005
|
+
if (!paymentIntentId) break;
|
|
2006
|
+
const metadata = obj.metadata || {};
|
|
2007
|
+
await updateStrapiPayment(paymentIntentId, {
|
|
2008
|
+
payment_status: "succeeded",
|
|
2009
|
+
metadata
|
|
2010
|
+
});
|
|
2011
|
+
const existing = await strapi2.documents("plugin::payment-plugin.payment").findMany({
|
|
2012
|
+
filters: { stripe_payment_intent_id: paymentIntentId }
|
|
2013
|
+
});
|
|
2014
|
+
if (existing.length === 0 && event.type === "payment_intent.succeeded") {
|
|
2015
|
+
await createStrapiPaymentRecord(obj);
|
|
2016
|
+
} else if (existing.length === 0 && event.type === "charge.succeeded" && obj.payment_intent) {
|
|
2017
|
+
const stripe2 = await initializeStripe();
|
|
2018
|
+
const pi = await stripe2.paymentIntents.retrieve(obj.payment_intent);
|
|
2019
|
+
await createStrapiPaymentRecord(pi);
|
|
1894
2020
|
}
|
|
1895
|
-
const orderId =
|
|
2021
|
+
const orderId = metadata.strapi_order_id;
|
|
1896
2022
|
if (orderId) {
|
|
1897
2023
|
await updateStrapiOrder(orderId, {
|
|
1898
|
-
|
|
2024
|
+
order_status: "processing",
|
|
2025
|
+
payment_status: "paid"
|
|
1899
2026
|
});
|
|
1900
2027
|
}
|
|
1901
|
-
logger.info(
|
|
1902
|
-
paymentIntentId
|
|
1903
|
-
amount: paymentIntent.amount,
|
|
2028
|
+
logger.info(`Payment succeeded handled for ${event.type}`, {
|
|
2029
|
+
paymentIntentId,
|
|
1904
2030
|
orderId
|
|
1905
2031
|
});
|
|
1906
2032
|
break;
|
|
1907
2033
|
}
|
|
2034
|
+
case "payment_intent.processing": {
|
|
2035
|
+
const paymentIntent = event.data.object;
|
|
2036
|
+
await updateStrapiPayment(paymentIntent.id, {
|
|
2037
|
+
payment_status: "pending",
|
|
2038
|
+
metadata: paymentIntent.metadata
|
|
2039
|
+
});
|
|
2040
|
+
break;
|
|
2041
|
+
}
|
|
1908
2042
|
case "payment_intent.payment_failed": {
|
|
1909
2043
|
const paymentIntent = event.data.object;
|
|
1910
2044
|
await updateStrapiPayment(paymentIntent.id, {
|
|
1911
|
-
|
|
2045
|
+
payment_status: "failed",
|
|
1912
2046
|
metadata: paymentIntent.metadata
|
|
1913
2047
|
});
|
|
1914
2048
|
const orderId = paymentIntent.metadata?.strapi_order_id;
|
|
1915
2049
|
if (orderId) {
|
|
1916
2050
|
await updateStrapiOrder(orderId, {
|
|
1917
|
-
|
|
2051
|
+
order_status: "failed",
|
|
2052
|
+
payment_status: "unpaid"
|
|
1918
2053
|
});
|
|
1919
2054
|
}
|
|
1920
2055
|
logger.warn("Payment failed", {
|
|
@@ -1927,13 +2062,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1927
2062
|
case "payment_intent.canceled": {
|
|
1928
2063
|
const paymentIntent = event.data.object;
|
|
1929
2064
|
await updateStrapiPayment(paymentIntent.id, {
|
|
1930
|
-
|
|
2065
|
+
payment_status: "canceled",
|
|
1931
2066
|
metadata: paymentIntent.metadata
|
|
1932
2067
|
});
|
|
1933
2068
|
const orderId = paymentIntent.metadata?.strapi_order_id;
|
|
1934
2069
|
if (orderId) {
|
|
1935
2070
|
await updateStrapiOrder(orderId, {
|
|
1936
|
-
|
|
2071
|
+
order_status: "failed",
|
|
2072
|
+
payment_status: "unpaid"
|
|
1937
2073
|
});
|
|
1938
2074
|
}
|
|
1939
2075
|
logger.info("Payment canceled", {
|
|
@@ -1946,20 +2082,29 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1946
2082
|
const charge = event.data.object;
|
|
1947
2083
|
if (charge.payment_intent) {
|
|
1948
2084
|
await updateStrapiPayment(charge.payment_intent, {
|
|
1949
|
-
|
|
2085
|
+
payment_status: "refunded"
|
|
1950
2086
|
});
|
|
1951
|
-
|
|
2087
|
+
let orderId = charge.metadata?.strapi_order_id;
|
|
2088
|
+
if (!orderId) {
|
|
2089
|
+
const payments = await strapi2.documents("plugin::payment-plugin.payment").findMany({
|
|
2090
|
+
filters: { stripe_payment_intent_id: charge.payment_intent },
|
|
2091
|
+
populate: ["order"]
|
|
2092
|
+
});
|
|
2093
|
+
if (payments.length > 0 && payments[0].order) {
|
|
2094
|
+
orderId = typeof payments[0].order === "object" ? payments[0].order.documentId : payments[0].order;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
1952
2097
|
if (orderId) {
|
|
1953
2098
|
await updateStrapiOrder(orderId, {
|
|
1954
|
-
|
|
2099
|
+
order_status: "refunded",
|
|
2100
|
+
payment_status: "refunded"
|
|
1955
2101
|
});
|
|
1956
2102
|
}
|
|
1957
2103
|
}
|
|
1958
|
-
logger.info("Charge refunded", {
|
|
2104
|
+
logger.info("Charge refunded handled", {
|
|
1959
2105
|
chargeId: charge.id,
|
|
1960
2106
|
paymentIntentId: charge.payment_intent,
|
|
1961
|
-
amount: charge.amount_refunded
|
|
1962
|
-
orderId: charge.metadata?.strapi_order_id
|
|
2107
|
+
amount: charge.amount_refunded
|
|
1963
2108
|
});
|
|
1964
2109
|
break;
|
|
1965
2110
|
}
|
|
@@ -105,7 +105,14 @@ declare const _default: {
|
|
|
105
105
|
default: string;
|
|
106
106
|
configurable: boolean;
|
|
107
107
|
};
|
|
108
|
-
|
|
108
|
+
order_status: {
|
|
109
|
+
type: string;
|
|
110
|
+
enum: string[];
|
|
111
|
+
required: boolean;
|
|
112
|
+
default: string;
|
|
113
|
+
configurable: boolean;
|
|
114
|
+
};
|
|
115
|
+
payment_status: {
|
|
109
116
|
type: string;
|
|
110
117
|
enum: string[];
|
|
111
118
|
required: boolean;
|
|
@@ -174,7 +181,7 @@ declare const _default: {
|
|
|
174
181
|
default: string;
|
|
175
182
|
configurable: boolean;
|
|
176
183
|
};
|
|
177
|
-
|
|
184
|
+
payment_status: {
|
|
178
185
|
type: string;
|
|
179
186
|
enum: string[];
|
|
180
187
|
required: boolean;
|
|
@@ -36,7 +36,14 @@ declare const _default: {
|
|
|
36
36
|
default: string;
|
|
37
37
|
configurable: boolean;
|
|
38
38
|
};
|
|
39
|
-
|
|
39
|
+
order_status: {
|
|
40
|
+
type: string;
|
|
41
|
+
enum: string[];
|
|
42
|
+
required: boolean;
|
|
43
|
+
default: string;
|
|
44
|
+
configurable: boolean;
|
|
45
|
+
};
|
|
46
|
+
payment_status: {
|
|
40
47
|
type: string;
|
|
41
48
|
enum: string[];
|
|
42
49
|
required: boolean;
|