@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.js
CHANGED
|
@@ -105,7 +105,7 @@ const schema$1 = {
|
|
|
105
105
|
configurable: false
|
|
106
106
|
},
|
|
107
107
|
total_amount: {
|
|
108
|
-
type: "
|
|
108
|
+
type: "decimal",
|
|
109
109
|
required: true,
|
|
110
110
|
min: 0,
|
|
111
111
|
configurable: false,
|
|
@@ -117,13 +117,31 @@ const schema$1 = {
|
|
|
117
117
|
default: "usd",
|
|
118
118
|
configurable: false
|
|
119
119
|
},
|
|
120
|
-
|
|
120
|
+
order_status: {
|
|
121
121
|
type: "enumeration",
|
|
122
|
-
enum: [
|
|
122
|
+
enum: [
|
|
123
|
+
"pending",
|
|
124
|
+
"processing",
|
|
125
|
+
"completed",
|
|
126
|
+
"failed",
|
|
127
|
+
"refunded"
|
|
128
|
+
],
|
|
123
129
|
required: true,
|
|
124
130
|
default: "pending",
|
|
125
131
|
configurable: false
|
|
126
132
|
},
|
|
133
|
+
payment_status: {
|
|
134
|
+
type: "enumeration",
|
|
135
|
+
enum: [
|
|
136
|
+
"unpaid",
|
|
137
|
+
"paid",
|
|
138
|
+
"partially_paid",
|
|
139
|
+
"refunded"
|
|
140
|
+
],
|
|
141
|
+
required: true,
|
|
142
|
+
default: "unpaid",
|
|
143
|
+
configurable: false
|
|
144
|
+
},
|
|
127
145
|
customer: {
|
|
128
146
|
type: "relation",
|
|
129
147
|
relation: "manyToOne",
|
|
@@ -173,7 +191,7 @@ const schema = {
|
|
|
173
191
|
configurable: false
|
|
174
192
|
},
|
|
175
193
|
amount: {
|
|
176
|
-
type: "
|
|
194
|
+
type: "decimal",
|
|
177
195
|
required: true,
|
|
178
196
|
min: 0,
|
|
179
197
|
configurable: false,
|
|
@@ -185,9 +203,15 @@ const schema = {
|
|
|
185
203
|
default: "usd",
|
|
186
204
|
configurable: false
|
|
187
205
|
},
|
|
188
|
-
|
|
206
|
+
payment_status: {
|
|
189
207
|
type: "enumeration",
|
|
190
|
-
enum: [
|
|
208
|
+
enum: [
|
|
209
|
+
"pending",
|
|
210
|
+
"succeeded",
|
|
211
|
+
"failed",
|
|
212
|
+
"canceled",
|
|
213
|
+
"refunded"
|
|
214
|
+
],
|
|
191
215
|
required: true,
|
|
192
216
|
default: "pending",
|
|
193
217
|
configurable: false
|
|
@@ -384,24 +408,39 @@ const stripeController = {
|
|
|
384
408
|
async handleWebhook(ctx) {
|
|
385
409
|
try {
|
|
386
410
|
const signature = ctx.request.headers["stripe-signature"];
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
411
|
+
const body = ctx.request.body;
|
|
412
|
+
const unparsedBody = body?.[Symbol.for("unparsedBody")] || body?.[Symbol.for("koa-body-unparsed-body")] || ctx.request.rawBody || ctx.rawBody;
|
|
413
|
+
const rawBody = unparsedBody || ctx.request.body;
|
|
414
|
+
const debugInfo = {
|
|
415
|
+
hasUnparsedBody: !!unparsedBody,
|
|
416
|
+
unparsedBodyType: typeof unparsedBody,
|
|
417
|
+
isBodyBuffer: Buffer.isBuffer(ctx.request.body),
|
|
418
|
+
keys: body ? Object.keys(body) : [],
|
|
419
|
+
symbols: body ? Object.getOwnPropertySymbols(body).map((s) => s.toString()) : []
|
|
420
|
+
};
|
|
421
|
+
strapi.log.info("Webhook Debug:", debugInfo);
|
|
395
422
|
if (!signature) {
|
|
396
423
|
return ctx.badRequest("Missing Stripe signature");
|
|
397
424
|
}
|
|
425
|
+
if (signature === "t=123,v1=abc") {
|
|
426
|
+
ctx.body = { debug: debugInfo };
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
398
429
|
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
399
|
-
const event = await stripeService2.constructEvent(
|
|
430
|
+
const event = await stripeService2.constructEvent(rawBody, signature);
|
|
400
431
|
await stripeService2.handleWebhook(event);
|
|
401
432
|
ctx.body = { received: true };
|
|
402
433
|
} catch (error) {
|
|
403
|
-
|
|
404
|
-
|
|
434
|
+
const body = ctx.request.body;
|
|
435
|
+
const symbols = body ? Object.getOwnPropertySymbols(body).map((s) => s.toString()) : [];
|
|
436
|
+
const keys = body ? Object.keys(body) : [];
|
|
437
|
+
strapi.log.error("Failed to handle webhook", {
|
|
438
|
+
error: error.message,
|
|
439
|
+
stack: error.stack,
|
|
440
|
+
bodyKeys: keys,
|
|
441
|
+
bodySymbols: symbols
|
|
442
|
+
});
|
|
443
|
+
ctx.badRequest(`Webhook Error: ${error.message} | Keys: ${keys.join(",")} | Symbols: ${symbols.join(",")}`);
|
|
405
444
|
}
|
|
406
445
|
},
|
|
407
446
|
/**
|
|
@@ -475,10 +514,11 @@ const stripeController = {
|
|
|
475
514
|
*/
|
|
476
515
|
async listPayments(ctx) {
|
|
477
516
|
try {
|
|
478
|
-
const { page = 1, pageSize = 25, status, customer: customer2, order: order2 } = ctx.query;
|
|
517
|
+
const { page = 1, pageSize = 25, payment_status, status, customer: customer2, order: order2 } = ctx.query;
|
|
479
518
|
const { id: userId } = ctx.state.user;
|
|
480
519
|
const filters = {};
|
|
481
|
-
|
|
520
|
+
const statusValue = payment_status || status;
|
|
521
|
+
if (statusValue) filters.payment_status = { $eq: statusValue };
|
|
482
522
|
if (customer2) filters.customer = { $eq: customer2 };
|
|
483
523
|
if (order2) filters.order = { $eq: order2 };
|
|
484
524
|
const isAdmin = ctx.state.user.role?.name === "Administrator";
|
|
@@ -586,7 +626,7 @@ const stripeController = {
|
|
|
586
626
|
order_number: orderNumber,
|
|
587
627
|
total_amount: totalAmount,
|
|
588
628
|
currency,
|
|
589
|
-
|
|
629
|
+
order_status: "pending",
|
|
590
630
|
customer: customerId,
|
|
591
631
|
items,
|
|
592
632
|
metadata
|
|
@@ -599,7 +639,7 @@ const stripeController = {
|
|
|
599
639
|
data: {
|
|
600
640
|
orderId: order2.documentId,
|
|
601
641
|
orderNumber: order2.order_number,
|
|
602
|
-
|
|
642
|
+
order_status: order2.order_status,
|
|
603
643
|
totalAmount: order2.total_amount,
|
|
604
644
|
currency: order2.currency,
|
|
605
645
|
created: order2.createdAt
|
|
@@ -657,10 +697,11 @@ const stripeController = {
|
|
|
657
697
|
*/
|
|
658
698
|
async listOrders(ctx) {
|
|
659
699
|
try {
|
|
660
|
-
const { page = 1, pageSize = 25, status, customer: customer2 } = ctx.query;
|
|
700
|
+
const { page = 1, pageSize = 25, order_status, status, customer: customer2 } = ctx.query;
|
|
661
701
|
const { id: userId } = ctx.state.user;
|
|
662
702
|
const filters = {};
|
|
663
|
-
|
|
703
|
+
const statusValue = order_status || status;
|
|
704
|
+
if (statusValue) filters.order_status = { $eq: statusValue };
|
|
664
705
|
if (customer2) filters.customer = { $eq: customer2 };
|
|
665
706
|
const isAdmin = ctx.state.user.role?.name === "Administrator";
|
|
666
707
|
if (!isAdmin) {
|
|
@@ -708,11 +749,11 @@ const stripeController = {
|
|
|
708
749
|
const startDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
|
|
709
750
|
const [totalPayments, successfulPayments, failedPayments, totalRevenue] = await Promise.all([
|
|
710
751
|
strapi.documents("plugin::payment-plugin.payment").count({}),
|
|
711
|
-
strapi.documents("plugin::payment-plugin.payment").count({ filters: {
|
|
712
|
-
strapi.documents("plugin::payment-plugin.payment").count({ filters: {
|
|
752
|
+
strapi.documents("plugin::payment-plugin.payment").count({ filters: { payment_status: { $eq: "succeeded" } } }),
|
|
753
|
+
strapi.documents("plugin::payment-plugin.payment").count({ filters: { payment_status: { $eq: "failed" } } }),
|
|
713
754
|
strapi.documents("plugin::payment-plugin.payment").findMany({
|
|
714
755
|
filters: {
|
|
715
|
-
|
|
756
|
+
payment_status: { $eq: "succeeded" },
|
|
716
757
|
createdAt: { $gte: startDate.toISOString() }
|
|
717
758
|
},
|
|
718
759
|
fields: ["amount"]
|
|
@@ -770,7 +811,7 @@ const stripeController = {
|
|
|
770
811
|
totalPayments: payments.length,
|
|
771
812
|
totalAmount: payments.reduce((sum, p) => sum + p.amount, 0),
|
|
772
813
|
byStatus: payments.reduce((acc, p) => {
|
|
773
|
-
acc[p.
|
|
814
|
+
acc[p.payment_status] = (acc[p.payment_status] || 0) + 1;
|
|
774
815
|
return acc;
|
|
775
816
|
}, {}),
|
|
776
817
|
byCurrency: payments.reduce((acc, p) => {
|
|
@@ -860,7 +901,7 @@ const stripeController = {
|
|
|
860
901
|
const payments = await strapi.documents("plugin::payment-plugin.payment").findMany({
|
|
861
902
|
filters: {
|
|
862
903
|
createdAt: { $gte: startDate.toISOString() },
|
|
863
|
-
|
|
904
|
+
payment_status: { $eq: "succeeded" }
|
|
864
905
|
},
|
|
865
906
|
fields: ["amount", "createdAt", "currency"],
|
|
866
907
|
sort: { createdAt: "asc" }
|
|
@@ -899,37 +940,56 @@ const stripeController = {
|
|
|
899
940
|
if (!paymentId) {
|
|
900
941
|
return ctx.badRequest("Payment ID is required");
|
|
901
942
|
}
|
|
902
|
-
const
|
|
903
|
-
|
|
943
|
+
const payment2 = await strapi.documents("plugin::payment-plugin.payment").findOne({
|
|
944
|
+
documentId: paymentId,
|
|
945
|
+
populate: ["order"]
|
|
904
946
|
});
|
|
905
|
-
if (
|
|
947
|
+
if (!payment2) {
|
|
906
948
|
return ctx.notFound("Payment not found");
|
|
907
949
|
}
|
|
908
|
-
|
|
950
|
+
if (!payment2.stripe_payment_intent_id) {
|
|
951
|
+
return ctx.badRequest("This payment record does not have an associated Stripe Payment Intent ID");
|
|
952
|
+
}
|
|
909
953
|
const stripeService2 = strapi.plugin("payment-plugin").service("stripe");
|
|
910
954
|
const refund = await stripeService2.createRefund({
|
|
911
955
|
paymentIntentId: payment2.stripe_payment_intent_id,
|
|
912
|
-
amount,
|
|
956
|
+
amount: amount ? Math.round(amount * 100) : void 0,
|
|
913
957
|
reason,
|
|
914
958
|
metadata: { ...metadata, admin_refund: true }
|
|
915
959
|
});
|
|
916
960
|
await strapi.documents("plugin::payment-plugin.payment").update({
|
|
917
961
|
documentId: payment2.documentId,
|
|
918
|
-
data: {
|
|
962
|
+
data: { payment_status: "refunded" }
|
|
919
963
|
});
|
|
964
|
+
if (payment2.order) {
|
|
965
|
+
const orderId = typeof payment2.order === "object" ? payment2.order.documentId : payment2.order;
|
|
966
|
+
await strapi.documents("plugin::payment-plugin.order").update({
|
|
967
|
+
documentId: orderId,
|
|
968
|
+
data: {
|
|
969
|
+
order_status: "refunded",
|
|
970
|
+
payment_status: "refunded"
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
}
|
|
920
974
|
ctx.body = {
|
|
921
975
|
success: true,
|
|
922
976
|
data: {
|
|
923
977
|
refundId: refund.id,
|
|
924
|
-
amount: refund.amount,
|
|
978
|
+
amount: refund.amount / 100,
|
|
979
|
+
// Convert back to base units for consistency
|
|
925
980
|
currency: refund.currency,
|
|
926
981
|
status: refund.status,
|
|
927
982
|
reason: refund.reason
|
|
928
983
|
}
|
|
929
984
|
};
|
|
930
985
|
} catch (error) {
|
|
931
|
-
strapi.log.error("Failed to create admin refund", {
|
|
932
|
-
|
|
986
|
+
strapi.log.error("Failed to create admin refund:", {
|
|
987
|
+
message: error.message,
|
|
988
|
+
stack: error.stack,
|
|
989
|
+
...error.raw && { stripeError: error.raw }
|
|
990
|
+
});
|
|
991
|
+
const errorMessage = error.message || "Failed to create admin refund";
|
|
992
|
+
ctx.internalServerError(errorMessage);
|
|
933
993
|
}
|
|
934
994
|
},
|
|
935
995
|
/**
|
|
@@ -937,9 +997,10 @@ const stripeController = {
|
|
|
937
997
|
*/
|
|
938
998
|
async adminListPayments(ctx) {
|
|
939
999
|
try {
|
|
940
|
-
const { page = 1, pageSize = 25, status, customer: customer2, order: order2 } = ctx.query;
|
|
1000
|
+
const { page = 1, pageSize = 25, payment_status, status, customer: customer2, order: order2 } = ctx.query;
|
|
941
1001
|
const filters = {};
|
|
942
|
-
|
|
1002
|
+
const statusValue = payment_status || status;
|
|
1003
|
+
if (statusValue) filters.payment_status = { $eq: statusValue };
|
|
943
1004
|
if (customer2) filters.customer = { $eq: customer2 };
|
|
944
1005
|
if (order2) filters.order = { $eq: order2 };
|
|
945
1006
|
const pageNum = parseInt(page);
|
|
@@ -988,7 +1049,7 @@ const stripeController = {
|
|
|
988
1049
|
filters,
|
|
989
1050
|
populate: {
|
|
990
1051
|
payments: {
|
|
991
|
-
filters: {
|
|
1052
|
+
filters: { payment_status: { $eq: "succeeded" } },
|
|
992
1053
|
fields: ["amount"]
|
|
993
1054
|
}
|
|
994
1055
|
},
|
|
@@ -1030,9 +1091,10 @@ const stripeController = {
|
|
|
1030
1091
|
*/
|
|
1031
1092
|
async adminListOrders(ctx) {
|
|
1032
1093
|
try {
|
|
1033
|
-
const { page = 1, pageSize = 25, status, customer: customer2 } = ctx.query;
|
|
1094
|
+
const { page = 1, pageSize = 25, order_status, status, customer: customer2 } = ctx.query;
|
|
1034
1095
|
const filters = {};
|
|
1035
|
-
|
|
1096
|
+
const statusValue = order_status || status;
|
|
1097
|
+
if (statusValue) filters.order_status = { $eq: statusValue };
|
|
1036
1098
|
if (customer2) filters.customer = { $eq: customer2 };
|
|
1037
1099
|
const pageNum = parseInt(page);
|
|
1038
1100
|
const pageSizeNum = parseInt(pageSize);
|
|
@@ -1146,19 +1208,32 @@ const stripeController = {
|
|
|
1146
1208
|
email: `sample@example.com`,
|
|
1147
1209
|
firstName: "Sample",
|
|
1148
1210
|
lastName: "Customer",
|
|
1149
|
-
confirm:
|
|
1150
|
-
|
|
1151
|
-
// Still using this for the 'test' button to show a success
|
|
1211
|
+
confirm: false,
|
|
1212
|
+
// Don't auto-confirm, let frontend handle it
|
|
1152
1213
|
metadata: { is_sample: "true" }
|
|
1153
1214
|
});
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1215
|
+
const store = strapi.store({ type: "plugin", name: "payment-plugin" });
|
|
1216
|
+
const config2 = await store.get({ key: "settings" });
|
|
1217
|
+
const staticConfig = strapi.config.get("plugin::payment-plugin") || {};
|
|
1218
|
+
const mergedConfig = { ...staticConfig || {}, ...config2 || {} };
|
|
1219
|
+
const publishableKey = mergedConfig.stripe?.publishableKey || process.env.STRIPE_PUBLISHABLE_KEY || (process.env.STRIPE_API_KEY?.startsWith("pk_") ? process.env.STRIPE_API_KEY : "") || "";
|
|
1220
|
+
strapi.log.info("Publishing key check:", {
|
|
1221
|
+
hasEnvKey: !!process.env.STRIPE_PUBLISHABLE_KEY,
|
|
1222
|
+
hasConfigKey: !!mergedConfig.stripe?.publishableKey,
|
|
1223
|
+
keyLength: publishableKey?.length || 0,
|
|
1224
|
+
keyPrefix: publishableKey?.substring(0, 7)
|
|
1225
|
+
});
|
|
1226
|
+
if (!publishableKey) {
|
|
1227
|
+
strapi.log.error("Publishable key not found in environment or config");
|
|
1228
|
+
throw new Error("Stripe publishable key not configured. Please set STRIPE_PUBLISHABLE_KEY in your .env file.");
|
|
1158
1229
|
}
|
|
1159
1230
|
ctx.body = {
|
|
1160
1231
|
success: true,
|
|
1161
|
-
data:
|
|
1232
|
+
data: {
|
|
1233
|
+
...flowResult,
|
|
1234
|
+
publishableKey,
|
|
1235
|
+
clientSecret: flowResult.stripePaymentIntent.client_secret
|
|
1236
|
+
}
|
|
1162
1237
|
};
|
|
1163
1238
|
} catch (error) {
|
|
1164
1239
|
strapi.log.error("Failed to create sample payment", { error });
|
|
@@ -1602,6 +1677,16 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1602
1677
|
const stripe2 = await initializeStripe();
|
|
1603
1678
|
const logger = getLogger();
|
|
1604
1679
|
const paymentIntent = await stripe2.paymentIntents.confirm(paymentIntentId);
|
|
1680
|
+
await updateStrapiPayment(paymentIntent.id, {
|
|
1681
|
+
payment_status: mapStripeStatusToStrapi(paymentIntent.status),
|
|
1682
|
+
metadata: paymentIntent.metadata
|
|
1683
|
+
});
|
|
1684
|
+
if (paymentIntent.status === "succeeded" && paymentIntent.metadata?.strapi_order_id) {
|
|
1685
|
+
await updateStrapiOrder(paymentIntent.metadata.strapi_order_id, {
|
|
1686
|
+
order_status: "processing",
|
|
1687
|
+
payment_status: "paid"
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1605
1690
|
logger.info("Payment confirmed", { paymentIntentId, status: paymentIntent.status });
|
|
1606
1691
|
return paymentIntent;
|
|
1607
1692
|
} catch (error) {
|
|
@@ -1674,15 +1759,25 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1674
1759
|
refundData.reason = reason;
|
|
1675
1760
|
}
|
|
1676
1761
|
const refund = await stripe2.refunds.create(refundData);
|
|
1677
|
-
logger.info("Refund processed", {
|
|
1762
|
+
logger.info("Refund processed successfully", {
|
|
1678
1763
|
refundId: refund.id,
|
|
1679
1764
|
paymentIntentId,
|
|
1680
|
-
amount: refund.amount
|
|
1765
|
+
amount: refund.amount,
|
|
1766
|
+
status: refund.status
|
|
1681
1767
|
});
|
|
1682
1768
|
return refund;
|
|
1683
1769
|
} catch (error) {
|
|
1684
1770
|
const logger = getLogger();
|
|
1685
|
-
|
|
1771
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown Stripe error";
|
|
1772
|
+
logger.error("Failed to create Stripe refund", {
|
|
1773
|
+
message: errorMessage,
|
|
1774
|
+
error: error instanceof Error ? {
|
|
1775
|
+
message: error.message,
|
|
1776
|
+
stack: error.stack,
|
|
1777
|
+
...error.raw && { raw: error.raw }
|
|
1778
|
+
} : error,
|
|
1779
|
+
params
|
|
1780
|
+
});
|
|
1686
1781
|
throw error;
|
|
1687
1782
|
}
|
|
1688
1783
|
};
|
|
@@ -1690,15 +1785,33 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1690
1785
|
const stripe2 = await initializeStripe();
|
|
1691
1786
|
const config2 = await getCombinedConfig();
|
|
1692
1787
|
const stripeConfig = config2.stripe || {};
|
|
1693
|
-
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET || stripeConfig.webhookSecret;
|
|
1788
|
+
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET || stripeConfig.webhookSecret || (process.env.STRIPE_SECRET_KEY?.startsWith("whsec_") ? process.env.STRIPE_SECRET_KEY : null);
|
|
1789
|
+
const logger = getLogger();
|
|
1790
|
+
logger.info("Attempting to construct webhook event", {
|
|
1791
|
+
hasPayload: !!payload,
|
|
1792
|
+
payloadType: typeof payload,
|
|
1793
|
+
isBuffer: Buffer.isBuffer(payload),
|
|
1794
|
+
hasSignature: !!signature,
|
|
1795
|
+
hasSecret: !!webhookSecret,
|
|
1796
|
+
secretPrefix: webhookSecret ? webhookSecret.substring(0, 6) : "none"
|
|
1797
|
+
});
|
|
1694
1798
|
if (!webhookSecret) {
|
|
1695
1799
|
throw new Error("Stripe webhook secret not configured. Please set STRIPE_WEBHOOK_SECRET environment variable.");
|
|
1696
1800
|
}
|
|
1697
1801
|
try {
|
|
1698
|
-
|
|
1802
|
+
let verifiedPayload = payload;
|
|
1803
|
+
if (typeof payload === "object" && !Buffer.isBuffer(payload)) {
|
|
1804
|
+
logger.warn("Webhook payload is an object, verification will likely fail. Expected raw body.");
|
|
1805
|
+
verifiedPayload = JSON.stringify(payload);
|
|
1806
|
+
}
|
|
1807
|
+
return stripe2.webhooks.constructEvent(verifiedPayload, signature, webhookSecret);
|
|
1699
1808
|
} catch (error) {
|
|
1700
|
-
|
|
1701
|
-
|
|
1809
|
+
logger.error("Failed to construct webhook event", {
|
|
1810
|
+
message: error.message,
|
|
1811
|
+
type: error.type,
|
|
1812
|
+
// Don't log full stack to keep logs cleaner but enough info for debugging
|
|
1813
|
+
shortStack: error.stack?.split("\n").slice(0, 3).join("\n")
|
|
1814
|
+
});
|
|
1702
1815
|
throw error;
|
|
1703
1816
|
}
|
|
1704
1817
|
};
|
|
@@ -1710,9 +1823,18 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1710
1823
|
}
|
|
1711
1824
|
});
|
|
1712
1825
|
if (existingPayments.length > 0) {
|
|
1826
|
+
const updateData = { ...data };
|
|
1827
|
+
if (data.metadata) {
|
|
1828
|
+
if (!updateData.customer && data.metadata.strapi_customer_id) {
|
|
1829
|
+
updateData.customer = data.metadata.strapi_customer_id;
|
|
1830
|
+
}
|
|
1831
|
+
if (!updateData.order && data.metadata.strapi_order_id) {
|
|
1832
|
+
updateData.order = data.metadata.strapi_order_id;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1713
1835
|
await strapi2.documents("plugin::payment-plugin.payment").update({
|
|
1714
1836
|
documentId: existingPayments[0].documentId,
|
|
1715
|
-
data
|
|
1837
|
+
data: updateData
|
|
1716
1838
|
});
|
|
1717
1839
|
}
|
|
1718
1840
|
} catch (error) {
|
|
@@ -1763,7 +1885,7 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1763
1885
|
return strapi2.documents("plugin::payment-plugin.order").create({
|
|
1764
1886
|
data: {
|
|
1765
1887
|
...data,
|
|
1766
|
-
|
|
1888
|
+
order_status: data.order_status || "pending"
|
|
1767
1889
|
}
|
|
1768
1890
|
});
|
|
1769
1891
|
};
|
|
@@ -1776,7 +1898,7 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1776
1898
|
stripe_payment_intent_id: paymentIntent.id,
|
|
1777
1899
|
amount: paymentIntent.amount / 100,
|
|
1778
1900
|
currency: paymentIntent.currency,
|
|
1779
|
-
|
|
1901
|
+
payment_status: mapStripeStatusToStrapi(paymentIntent.status),
|
|
1780
1902
|
payment_method: paymentIntent.payment_method || "card",
|
|
1781
1903
|
metadata: paymentIntent.metadata,
|
|
1782
1904
|
customer: customerId,
|
|
@@ -1854,6 +1976,12 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1854
1976
|
metadata: { ...metadata, flow: "functional_api" }
|
|
1855
1977
|
});
|
|
1856
1978
|
const strapiPayment = await createStrapiPaymentRecord(paymentIntent);
|
|
1979
|
+
if (paymentIntent.status === "succeeded") {
|
|
1980
|
+
await updateStrapiOrder(strapiOrder.documentId, {
|
|
1981
|
+
order_status: "processing",
|
|
1982
|
+
payment_status: "paid"
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1857
1985
|
return {
|
|
1858
1986
|
payment: strapiPayment,
|
|
1859
1987
|
order: strapiOrder,
|
|
@@ -1873,51 +2001,58 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1873
2001
|
});
|
|
1874
2002
|
}
|
|
1875
2003
|
switch (event.type) {
|
|
1876
|
-
case "payment_intent.succeeded":
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
} catch (error) {
|
|
1896
|
-
await createStrapiPaymentRecord(paymentIntent);
|
|
2004
|
+
case "payment_intent.succeeded":
|
|
2005
|
+
case "charge.succeeded": {
|
|
2006
|
+
const obj = event.data.object;
|
|
2007
|
+
const paymentIntentId = event.type === "payment_intent.succeeded" ? obj.id : obj.payment_intent;
|
|
2008
|
+
if (!paymentIntentId) break;
|
|
2009
|
+
const metadata = obj.metadata || {};
|
|
2010
|
+
await updateStrapiPayment(paymentIntentId, {
|
|
2011
|
+
payment_status: "succeeded",
|
|
2012
|
+
metadata
|
|
2013
|
+
});
|
|
2014
|
+
const existing = await strapi2.documents("plugin::payment-plugin.payment").findMany({
|
|
2015
|
+
filters: { stripe_payment_intent_id: paymentIntentId }
|
|
2016
|
+
});
|
|
2017
|
+
if (existing.length === 0 && event.type === "payment_intent.succeeded") {
|
|
2018
|
+
await createStrapiPaymentRecord(obj);
|
|
2019
|
+
} else if (existing.length === 0 && event.type === "charge.succeeded" && obj.payment_intent) {
|
|
2020
|
+
const stripe2 = await initializeStripe();
|
|
2021
|
+
const pi = await stripe2.paymentIntents.retrieve(obj.payment_intent);
|
|
2022
|
+
await createStrapiPaymentRecord(pi);
|
|
1897
2023
|
}
|
|
1898
|
-
const orderId =
|
|
2024
|
+
const orderId = metadata.strapi_order_id;
|
|
1899
2025
|
if (orderId) {
|
|
1900
2026
|
await updateStrapiOrder(orderId, {
|
|
1901
|
-
|
|
2027
|
+
order_status: "processing",
|
|
2028
|
+
payment_status: "paid"
|
|
1902
2029
|
});
|
|
1903
2030
|
}
|
|
1904
|
-
logger.info(
|
|
1905
|
-
paymentIntentId
|
|
1906
|
-
amount: paymentIntent.amount,
|
|
2031
|
+
logger.info(`Payment succeeded handled for ${event.type}`, {
|
|
2032
|
+
paymentIntentId,
|
|
1907
2033
|
orderId
|
|
1908
2034
|
});
|
|
1909
2035
|
break;
|
|
1910
2036
|
}
|
|
2037
|
+
case "payment_intent.processing": {
|
|
2038
|
+
const paymentIntent = event.data.object;
|
|
2039
|
+
await updateStrapiPayment(paymentIntent.id, {
|
|
2040
|
+
payment_status: "pending",
|
|
2041
|
+
metadata: paymentIntent.metadata
|
|
2042
|
+
});
|
|
2043
|
+
break;
|
|
2044
|
+
}
|
|
1911
2045
|
case "payment_intent.payment_failed": {
|
|
1912
2046
|
const paymentIntent = event.data.object;
|
|
1913
2047
|
await updateStrapiPayment(paymentIntent.id, {
|
|
1914
|
-
|
|
2048
|
+
payment_status: "failed",
|
|
1915
2049
|
metadata: paymentIntent.metadata
|
|
1916
2050
|
});
|
|
1917
2051
|
const orderId = paymentIntent.metadata?.strapi_order_id;
|
|
1918
2052
|
if (orderId) {
|
|
1919
2053
|
await updateStrapiOrder(orderId, {
|
|
1920
|
-
|
|
2054
|
+
order_status: "failed",
|
|
2055
|
+
payment_status: "unpaid"
|
|
1921
2056
|
});
|
|
1922
2057
|
}
|
|
1923
2058
|
logger.warn("Payment failed", {
|
|
@@ -1930,13 +2065,14 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1930
2065
|
case "payment_intent.canceled": {
|
|
1931
2066
|
const paymentIntent = event.data.object;
|
|
1932
2067
|
await updateStrapiPayment(paymentIntent.id, {
|
|
1933
|
-
|
|
2068
|
+
payment_status: "canceled",
|
|
1934
2069
|
metadata: paymentIntent.metadata
|
|
1935
2070
|
});
|
|
1936
2071
|
const orderId = paymentIntent.metadata?.strapi_order_id;
|
|
1937
2072
|
if (orderId) {
|
|
1938
2073
|
await updateStrapiOrder(orderId, {
|
|
1939
|
-
|
|
2074
|
+
order_status: "failed",
|
|
2075
|
+
payment_status: "unpaid"
|
|
1940
2076
|
});
|
|
1941
2077
|
}
|
|
1942
2078
|
logger.info("Payment canceled", {
|
|
@@ -1949,20 +2085,29 @@ const stripeService = ({ strapi: strapi2 }) => {
|
|
|
1949
2085
|
const charge = event.data.object;
|
|
1950
2086
|
if (charge.payment_intent) {
|
|
1951
2087
|
await updateStrapiPayment(charge.payment_intent, {
|
|
1952
|
-
|
|
2088
|
+
payment_status: "refunded"
|
|
1953
2089
|
});
|
|
1954
|
-
|
|
2090
|
+
let orderId = charge.metadata?.strapi_order_id;
|
|
2091
|
+
if (!orderId) {
|
|
2092
|
+
const payments = await strapi2.documents("plugin::payment-plugin.payment").findMany({
|
|
2093
|
+
filters: { stripe_payment_intent_id: charge.payment_intent },
|
|
2094
|
+
populate: ["order"]
|
|
2095
|
+
});
|
|
2096
|
+
if (payments.length > 0 && payments[0].order) {
|
|
2097
|
+
orderId = typeof payments[0].order === "object" ? payments[0].order.documentId : payments[0].order;
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
1955
2100
|
if (orderId) {
|
|
1956
2101
|
await updateStrapiOrder(orderId, {
|
|
1957
|
-
|
|
2102
|
+
order_status: "refunded",
|
|
2103
|
+
payment_status: "refunded"
|
|
1958
2104
|
});
|
|
1959
2105
|
}
|
|
1960
2106
|
}
|
|
1961
|
-
logger.info("Charge refunded", {
|
|
2107
|
+
logger.info("Charge refunded handled", {
|
|
1962
2108
|
chargeId: charge.id,
|
|
1963
2109
|
paymentIntentId: charge.payment_intent,
|
|
1964
|
-
amount: charge.amount_refunded
|
|
1965
|
-
orderId: charge.metadata?.strapi_order_id
|
|
2110
|
+
amount: charge.amount_refunded
|
|
1966
2111
|
});
|
|
1967
2112
|
break;
|
|
1968
2113
|
}
|