@deenruv/payments-plugin 1.0.0

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.
Files changed (102) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +157 -0
  3. package/package/braintree/braintree-common.d.ts +11 -0
  4. package/package/braintree/braintree-common.js +79 -0
  5. package/package/braintree/braintree-common.js.map +1 -0
  6. package/package/braintree/braintree.handler.d.ts +28 -0
  7. package/package/braintree/braintree.handler.js +147 -0
  8. package/package/braintree/braintree.handler.js.map +1 -0
  9. package/package/braintree/braintree.plugin.d.ts +238 -0
  10. package/package/braintree/braintree.plugin.js +294 -0
  11. package/package/braintree/braintree.plugin.js.map +1 -0
  12. package/package/braintree/braintree.resolver.d.ts +14 -0
  13. package/package/braintree/braintree.resolver.js +109 -0
  14. package/package/braintree/braintree.resolver.js.map +1 -0
  15. package/package/braintree/constants.d.ts +2 -0
  16. package/package/braintree/constants.js +6 -0
  17. package/package/braintree/constants.js.map +1 -0
  18. package/package/braintree/index.d.ts +5 -0
  19. package/package/braintree/index.js +22 -0
  20. package/package/braintree/index.js.map +1 -0
  21. package/package/braintree/types.d.ts +94 -0
  22. package/package/braintree/types.js +4 -0
  23. package/package/braintree/types.js.map +1 -0
  24. package/package/index.d.ts +4 -0
  25. package/package/index.js +8 -0
  26. package/package/index.js.map +1 -0
  27. package/package/mollie/api-extensions.d.ts +2 -0
  28. package/package/mollie/api-extensions.js +88 -0
  29. package/package/mollie/api-extensions.js.map +1 -0
  30. package/package/mollie/constants.d.ts +2 -0
  31. package/package/mollie/constants.js +6 -0
  32. package/package/mollie/constants.js.map +1 -0
  33. package/package/mollie/custom-fields.d.ts +7 -0
  34. package/package/mollie/custom-fields.js +12 -0
  35. package/package/mollie/custom-fields.js.map +1 -0
  36. package/package/mollie/extended-mollie-client.d.ts +51 -0
  37. package/package/mollie/extended-mollie-client.js +39 -0
  38. package/package/mollie/extended-mollie-client.js.map +1 -0
  39. package/package/mollie/graphql/generated-shop-types.d.ts +3212 -0
  40. package/package/mollie/graphql/generated-shop-types.js +977 -0
  41. package/package/mollie/graphql/generated-shop-types.js.map +1 -0
  42. package/package/mollie/index.d.ts +3 -0
  43. package/package/mollie/index.js +23 -0
  44. package/package/mollie/index.js.map +1 -0
  45. package/package/mollie/mollie.common-resolver.d.ts +9 -0
  46. package/package/mollie/mollie.common-resolver.js +55 -0
  47. package/package/mollie/mollie.common-resolver.js.map +1 -0
  48. package/package/mollie/mollie.controller.d.ts +10 -0
  49. package/package/mollie/mollie.controller.js +71 -0
  50. package/package/mollie/mollie.controller.js.map +1 -0
  51. package/package/mollie/mollie.handler.d.ts +36 -0
  52. package/package/mollie/mollie.handler.js +141 -0
  53. package/package/mollie/mollie.handler.js.map +1 -0
  54. package/package/mollie/mollie.helpers.d.ts +33 -0
  55. package/package/mollie/mollie.helpers.js +147 -0
  56. package/package/mollie/mollie.helpers.js.map +1 -0
  57. package/package/mollie/mollie.plugin.d.ts +176 -0
  58. package/package/mollie/mollie.plugin.js +167 -0
  59. package/package/mollie/mollie.plugin.js.map +1 -0
  60. package/package/mollie/mollie.service.d.ts +64 -0
  61. package/package/mollie/mollie.service.js +438 -0
  62. package/package/mollie/mollie.service.js.map +1 -0
  63. package/package/mollie/mollie.shop-resolver.d.ts +8 -0
  64. package/package/mollie/mollie.shop-resolver.js +40 -0
  65. package/package/mollie/mollie.shop-resolver.js.map +1 -0
  66. package/package/stripe/constants.d.ts +2 -0
  67. package/package/stripe/constants.js +6 -0
  68. package/package/stripe/constants.js.map +1 -0
  69. package/package/stripe/index.d.ts +1 -0
  70. package/package/stripe/index.js +6 -0
  71. package/package/stripe/index.js.map +1 -0
  72. package/package/stripe/metadata-sanitize.d.ts +13 -0
  73. package/package/stripe/metadata-sanitize.js +33 -0
  74. package/package/stripe/metadata-sanitize.js.map +1 -0
  75. package/package/stripe/raw-body.middleware.d.ts +6 -0
  76. package/package/stripe/raw-body.middleware.js +18 -0
  77. package/package/stripe/raw-body.middleware.js.map +1 -0
  78. package/package/stripe/stripe-client.d.ts +9 -0
  79. package/package/stripe/stripe-client.js +21 -0
  80. package/package/stripe/stripe-client.js.map +1 -0
  81. package/package/stripe/stripe-utils.d.ts +19 -0
  82. package/package/stripe/stripe-utils.js +40 -0
  83. package/package/stripe/stripe-utils.js.map +1 -0
  84. package/package/stripe/stripe.controller.d.ts +15 -0
  85. package/package/stripe/stripe.controller.js +135 -0
  86. package/package/stripe/stripe.controller.js.map +1 -0
  87. package/package/stripe/stripe.handler.d.ts +30 -0
  88. package/package/stripe/stripe.handler.js +103 -0
  89. package/package/stripe/stripe.handler.js.map +1 -0
  90. package/package/stripe/stripe.plugin.d.ts +158 -0
  91. package/package/stripe/stripe.plugin.js +218 -0
  92. package/package/stripe/stripe.plugin.js.map +1 -0
  93. package/package/stripe/stripe.resolver.d.ts +8 -0
  94. package/package/stripe/stripe.resolver.js +48 -0
  95. package/package/stripe/stripe.resolver.js.map +1 -0
  96. package/package/stripe/stripe.service.d.ts +28 -0
  97. package/package/stripe/stripe.service.js +149 -0
  98. package/package/stripe/stripe.service.js.map +1 -0
  99. package/package/stripe/types.d.ts +137 -0
  100. package/package/stripe/types.js +4 -0
  101. package/package/stripe/types.js.map +1 -0
  102. package/package.json +73 -0
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DeenruvStripeClient = void 0;
7
+ const stripe_1 = __importDefault(require("stripe"));
8
+ /**
9
+ * Wrapper around the Stripe client that exposes ApiKey and WebhookSecret
10
+ */
11
+ class DeenruvStripeClient extends stripe_1.default {
12
+ constructor(apiKey, webhookSecret) {
13
+ super(apiKey, {
14
+ apiVersion: null, // Use accounts default version
15
+ });
16
+ this.apiKey = apiKey;
17
+ this.webhookSecret = webhookSecret;
18
+ }
19
+ }
20
+ exports.DeenruvStripeClient = DeenruvStripeClient;
21
+ //# sourceMappingURL=stripe-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe-client.js","sourceRoot":"","sources":["../../src/stripe/stripe-client.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAE5B;;GAEG;AACH,MAAa,mBAAoB,SAAQ,gBAAM;IAC7C,YACU,MAAc,EACf,aAAqB;QAE5B,KAAK,CAAC,MAAM,EAAE;YACZ,UAAU,EAAE,IAA0C,EAAE,+BAA+B;SACxF,CAAC,CAAC;QALK,WAAM,GAAN,MAAM,CAAQ;QACf,kBAAa,GAAb,aAAa,CAAQ;IAK9B,CAAC;CACF;AATD,kDASC"}
@@ -0,0 +1,19 @@
1
+ import { Order } from "@deenruv/core";
2
+ /**
3
+ * @description
4
+ * From the [Stripe docs](https://stripe.com/docs/currencies#zero-decimal):
5
+ * > All API requests expect amounts to be provided in a currency’s smallest unit.
6
+ * > For example, to charge 10 USD, provide an amount value of 1000 (that is, 1000 cents).
7
+ * > For zero-decimal currencies, still provide amounts as an integer but without multiplying by 100.
8
+ * > For example, to charge ¥500, provide an amount value of 500.
9
+ *
10
+ * Therefore, for a fractionless currency like JPY, we need to divide the amount by 100 (since Deenruv always
11
+ * stores money amounts multiplied by 100). See https://github.com/deenruv-ecommerce/deenruv/issues/1630
12
+ */
13
+ export declare function getAmountInStripeMinorUnits(order: Order): number;
14
+ /**
15
+ * @description
16
+ * Performs the reverse of `getAmountInStripeMinorUnits` - converting the Stripe minor units into the format
17
+ * used by Deenruv.
18
+ */
19
+ export declare function getAmountFromStripeMinorUnits(order: Order, stripeAmount: number): number;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAmountFromStripeMinorUnits = exports.getAmountInStripeMinorUnits = void 0;
4
+ /**
5
+ * @description
6
+ * From the [Stripe docs](https://stripe.com/docs/currencies#zero-decimal):
7
+ * > All API requests expect amounts to be provided in a currency’s smallest unit.
8
+ * > For example, to charge 10 USD, provide an amount value of 1000 (that is, 1000 cents).
9
+ * > For zero-decimal currencies, still provide amounts as an integer but without multiplying by 100.
10
+ * > For example, to charge ¥500, provide an amount value of 500.
11
+ *
12
+ * Therefore, for a fractionless currency like JPY, we need to divide the amount by 100 (since Deenruv always
13
+ * stores money amounts multiplied by 100). See https://github.com/deenruv-ecommerce/deenruv/issues/1630
14
+ */
15
+ function getAmountInStripeMinorUnits(order) {
16
+ return currencyHasFractionPart(order.currencyCode)
17
+ ? order.totalWithTax
18
+ : Math.round(order.totalWithTax / 100);
19
+ }
20
+ exports.getAmountInStripeMinorUnits = getAmountInStripeMinorUnits;
21
+ /**
22
+ * @description
23
+ * Performs the reverse of `getAmountInStripeMinorUnits` - converting the Stripe minor units into the format
24
+ * used by Deenruv.
25
+ */
26
+ function getAmountFromStripeMinorUnits(order, stripeAmount) {
27
+ return currencyHasFractionPart(order.currencyCode)
28
+ ? stripeAmount
29
+ : stripeAmount * 100;
30
+ }
31
+ exports.getAmountFromStripeMinorUnits = getAmountFromStripeMinorUnits;
32
+ function currencyHasFractionPart(currencyCode) {
33
+ const parts = new Intl.NumberFormat(undefined, {
34
+ style: "currency",
35
+ currency: currencyCode,
36
+ currencyDisplay: "symbol",
37
+ }).formatToParts(123.45);
38
+ return !!parts.find((p) => p.type === "fraction");
39
+ }
40
+ //# sourceMappingURL=stripe-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe-utils.js","sourceRoot":"","sources":["../../src/stripe/stripe-utils.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;GAUG;AACH,SAAgB,2BAA2B,CAAC,KAAY;IACtD,OAAO,uBAAuB,CAAC,KAAK,CAAC,YAAY,CAAC;QAChD,CAAC,CAAC,KAAK,CAAC,YAAY;QACpB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC;AAC3C,CAAC;AAJD,kEAIC;AAED;;;;GAIG;AACH,SAAgB,6BAA6B,CAC3C,KAAY,EACZ,YAAoB;IAEpB,OAAO,uBAAuB,CAAC,KAAK,CAAC,YAAY,CAAC;QAChD,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,YAAY,GAAG,GAAG,CAAC;AACzB,CAAC;AAPD,sEAOC;AAED,SAAS,uBAAuB,CAAC,YAA0B;IACzD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE;QAC7C,KAAK,EAAE,UAAU;QACjB,QAAQ,EAAE,YAAY;QACtB,eAAe,EAAE,QAAQ;KAC1B,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAEzB,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { OrderService, PaymentMethodService, RequestContextService, TransactionalConnection } from "@deenruv/core";
2
+ import type { Response } from "express";
3
+ import { StripeService } from "./stripe.service";
4
+ import { RequestWithRawBody } from "./types";
5
+ export declare class StripeController {
6
+ private paymentMethodService;
7
+ private orderService;
8
+ private stripeService;
9
+ private requestContextService;
10
+ private connection;
11
+ constructor(paymentMethodService: PaymentMethodService, orderService: OrderService, stripeService: StripeService, requestContextService: RequestContextService, connection: TransactionalConnection);
12
+ webhook(signature: string | undefined, request: RequestWithRawBody, response: Response): Promise<void>;
13
+ private createContext;
14
+ private getPaymentMethod;
15
+ }
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.StripeController = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const core_1 = require("@deenruv/core");
18
+ const generated_graphql_shop_errors_1 = require("@deenruv/core/dist/common/error/generated-graphql-shop-errors");
19
+ const constants_1 = require("./constants");
20
+ const stripe_handler_1 = require("./stripe.handler");
21
+ const stripe_service_1 = require("./stripe.service");
22
+ const missingHeaderErrorMessage = "Missing stripe-signature header";
23
+ const signatureErrorMessage = "Error verifying Stripe webhook signature";
24
+ const noPaymentIntentErrorMessage = "No payment intent in the event payload";
25
+ let StripeController = exports.StripeController = class StripeController {
26
+ constructor(paymentMethodService, orderService, stripeService, requestContextService, connection) {
27
+ this.paymentMethodService = paymentMethodService;
28
+ this.orderService = orderService;
29
+ this.stripeService = stripeService;
30
+ this.requestContextService = requestContextService;
31
+ this.connection = connection;
32
+ }
33
+ async webhook(signature, request, response) {
34
+ if (!signature) {
35
+ core_1.Logger.error(missingHeaderErrorMessage, constants_1.loggerCtx);
36
+ response.status(common_1.HttpStatus.BAD_REQUEST).send(missingHeaderErrorMessage);
37
+ return;
38
+ }
39
+ const event = JSON.parse(request.body.toString());
40
+ const paymentIntent = event.data.object;
41
+ if (!paymentIntent) {
42
+ core_1.Logger.error(noPaymentIntentErrorMessage, constants_1.loggerCtx);
43
+ response.status(common_1.HttpStatus.BAD_REQUEST).send(noPaymentIntentErrorMessage);
44
+ return;
45
+ }
46
+ const { metadata: { channelToken, orderCode, orderId } = {} } = paymentIntent;
47
+ const outerCtx = await this.createContext(channelToken, request);
48
+ await this.connection.withTransaction(outerCtx, async (ctx) => {
49
+ var _a, _b;
50
+ const order = await this.orderService.findOneByCode(ctx, orderCode);
51
+ if (!order) {
52
+ throw new Error(`Unable to find order ${orderCode}, unable to settle payment ${paymentIntent.id}!`);
53
+ }
54
+ try {
55
+ // Throws an error if the signature is invalid
56
+ await this.stripeService.constructEventFromPayload(ctx, order, request.rawBody, signature);
57
+ }
58
+ catch (e) {
59
+ core_1.Logger.error(`${signatureErrorMessage} ${signature}: ${e === null || e === void 0 ? void 0 : e.message}`, constants_1.loggerCtx);
60
+ response.status(common_1.HttpStatus.BAD_REQUEST).send(signatureErrorMessage);
61
+ return;
62
+ }
63
+ if (event.type === "payment_intent.payment_failed") {
64
+ const message = (_b = (_a = paymentIntent.last_payment_error) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : "unknown error";
65
+ core_1.Logger.warn(`Payment for order ${orderCode} failed: ${message}`, constants_1.loggerCtx);
66
+ response.status(common_1.HttpStatus.OK).send("Ok");
67
+ return;
68
+ }
69
+ if (event.type !== "payment_intent.succeeded") {
70
+ // This should never happen as the webhook is configured to receive
71
+ // payment_intent.succeeded and payment_intent.payment_failed events only
72
+ core_1.Logger.info(`Received ${event.type} status update for order ${orderCode}`, constants_1.loggerCtx);
73
+ return;
74
+ }
75
+ if (order.state !== "ArrangingPayment") {
76
+ const transitionToStateResult = await this.orderService.transitionToState(ctx, orderId, "ArrangingPayment");
77
+ if (transitionToStateResult instanceof generated_graphql_shop_errors_1.OrderStateTransitionError) {
78
+ core_1.Logger.error(`Error transitioning order ${orderCode} to ArrangingPayment state: ${transitionToStateResult.message}`, constants_1.loggerCtx);
79
+ return;
80
+ }
81
+ }
82
+ const paymentMethod = await this.getPaymentMethod(ctx);
83
+ const addPaymentToOrderResult = await this.orderService.addPaymentToOrder(ctx, orderId, {
84
+ method: paymentMethod.code,
85
+ metadata: {
86
+ paymentIntentAmountReceived: paymentIntent.amount_received,
87
+ paymentIntentId: paymentIntent.id,
88
+ },
89
+ });
90
+ if (!(addPaymentToOrderResult instanceof core_1.Order)) {
91
+ core_1.Logger.error(`Error adding payment to order ${orderCode}: ${addPaymentToOrderResult.message}`, constants_1.loggerCtx);
92
+ return;
93
+ }
94
+ // The payment intent ID is added to the order only if we can reach this point.
95
+ core_1.Logger.info(`Stripe payment intent id ${paymentIntent.id} added to order ${orderCode}`, constants_1.loggerCtx);
96
+ });
97
+ // Send the response status only if we didn't sent anything yet.
98
+ if (!response.headersSent) {
99
+ response.status(common_1.HttpStatus.OK).send("Ok");
100
+ }
101
+ }
102
+ async createContext(channelToken, req) {
103
+ return this.requestContextService.create({
104
+ apiType: "admin",
105
+ channelOrToken: channelToken,
106
+ req,
107
+ languageCode: core_1.LanguageCode.en,
108
+ });
109
+ }
110
+ async getPaymentMethod(ctx) {
111
+ const method = (await this.paymentMethodService.findAll(ctx)).items.find((m) => m.handler.code === stripe_handler_1.stripePaymentMethodHandler.code);
112
+ if (!method) {
113
+ throw new core_1.InternalServerError(`[${constants_1.loggerCtx}] Could not find Stripe PaymentMethod`);
114
+ }
115
+ return method;
116
+ }
117
+ };
118
+ __decorate([
119
+ (0, common_1.Post)("stripe"),
120
+ __param(0, (0, common_1.Headers)("stripe-signature")),
121
+ __param(1, (0, common_1.Req)()),
122
+ __param(2, (0, common_1.Res)()),
123
+ __metadata("design:type", Function),
124
+ __metadata("design:paramtypes", [Object, Object, Object]),
125
+ __metadata("design:returntype", Promise)
126
+ ], StripeController.prototype, "webhook", null);
127
+ exports.StripeController = StripeController = __decorate([
128
+ (0, common_1.Controller)("payments"),
129
+ __metadata("design:paramtypes", [core_1.PaymentMethodService,
130
+ core_1.OrderService,
131
+ stripe_service_1.StripeService,
132
+ core_1.RequestContextService,
133
+ core_1.TransactionalConnection])
134
+ ], StripeController);
135
+ //# sourceMappingURL=stripe.controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.controller.js","sourceRoot":"","sources":["../../src/stripe/stripe.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAOwB;AAExB,wCASuB;AACvB,iHAA0G;AAI1G,2CAAwC;AACxC,qDAA8D;AAC9D,qDAAiD;AAGjD,MAAM,yBAAyB,GAAG,iCAAiC,CAAC;AACpE,MAAM,qBAAqB,GAAG,0CAA0C,CAAC;AACzE,MAAM,2BAA2B,GAAG,wCAAwC,CAAC;AAGtE,IAAM,gBAAgB,8BAAtB,MAAM,gBAAgB;IAC3B,YACU,oBAA0C,EAC1C,YAA0B,EAC1B,aAA4B,EAC5B,qBAA4C,EAC5C,UAAmC;QAJnC,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,iBAAY,GAAZ,YAAY,CAAc;QAC1B,kBAAa,GAAb,aAAa,CAAe;QAC5B,0BAAqB,GAArB,qBAAqB,CAAuB;QAC5C,eAAU,GAAV,UAAU,CAAyB;IAC1C,CAAC;IAGE,AAAN,KAAK,CAAC,OAAO,CACkB,SAA6B,EACnD,OAA2B,EAC3B,QAAkB;QAEzB,IAAI,CAAC,SAAS,EAAE;YACd,aAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,qBAAS,CAAC,CAAC;YACnD,QAAQ,CAAC,MAAM,CAAC,mBAAU,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;YACxE,OAAO;SACR;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAiB,CAAC;QAClE,MAAM,aAAa,GAAG,KAAK,CAAC,IAAI,CAAC,MAA8B,CAAC;QAEhE,IAAI,CAAC,aAAa,EAAE;YAClB,aAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,qBAAS,CAAC,CAAC;YACrD,QAAQ,CAAC,MAAM,CAAC,mBAAU,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;YAC1E,OAAO;SACR;QAED,MAAM,EAAE,QAAQ,EAAE,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,GAC3D,aAAa,CAAC;QAChB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAEjE,MAAM,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;;YAC5D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAEpE,IAAI,CAAC,KAAK,EAAE;gBACV,MAAM,IAAI,KAAK,CACb,wBAAwB,SAAS,8BAA8B,aAAa,CAAC,EAAE,GAAG,CACnF,CAAC;aACH;YAED,IAAI;gBACF,8CAA8C;gBAC9C,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAChD,GAAG,EACH,KAAK,EACL,OAAO,CAAC,OAAO,EACf,SAAS,CACV,CAAC;aACH;YAAC,OAAO,CAAM,EAAE;gBACf,aAAM,CAAC,KAAK,CACV,GAAG,qBAAqB,IAAI,SAAS,KAAM,CAAW,aAAX,CAAC,uBAAD,CAAC,CAAY,OAAO,EAAE,EACjE,qBAAS,CACV,CAAC;gBACF,QAAQ,CAAC,MAAM,CAAC,mBAAU,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;gBACpE,OAAO;aACR;YAED,IAAI,KAAK,CAAC,IAAI,KAAK,+BAA+B,EAAE;gBAClD,MAAM,OAAO,GACX,MAAA,MAAA,aAAa,CAAC,kBAAkB,0CAAE,OAAO,mCAAI,eAAe,CAAC;gBAC/D,aAAM,CAAC,IAAI,CACT,qBAAqB,SAAS,YAAY,OAAO,EAAE,EACnD,qBAAS,CACV,CAAC;gBACF,QAAQ,CAAC,MAAM,CAAC,mBAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC1C,OAAO;aACR;YAED,IAAI,KAAK,CAAC,IAAI,KAAK,0BAA0B,EAAE;gBAC7C,mEAAmE;gBACnE,yEAAyE;gBACzE,aAAM,CAAC,IAAI,CACT,YAAY,KAAK,CAAC,IAAI,4BAA4B,SAAS,EAAE,EAC7D,qBAAS,CACV,CAAC;gBACF,OAAO;aACR;YAED,IAAI,KAAK,CAAC,KAAK,KAAK,kBAAkB,EAAE;gBACtC,MAAM,uBAAuB,GAC3B,MAAM,IAAI,CAAC,YAAY,CAAC,iBAAiB,CACvC,GAAG,EACH,OAAO,EACP,kBAAkB,CACnB,CAAC;gBAEJ,IAAI,uBAAuB,YAAY,yDAAyB,EAAE;oBAChE,aAAM,CAAC,KAAK,CACV,6BAA6B,SAAS,+BAA+B,uBAAuB,CAAC,OAAO,EAAE,EACtG,qBAAS,CACV,CAAC;oBACF,OAAO;iBACR;aACF;YAED,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAEvD,MAAM,uBAAuB,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,iBAAiB,CACvE,GAAG,EACH,OAAO,EACP;gBACE,MAAM,EAAE,aAAa,CAAC,IAAI;gBAC1B,QAAQ,EAAE;oBACR,2BAA2B,EAAE,aAAa,CAAC,eAAe;oBAC1D,eAAe,EAAE,aAAa,CAAC,EAAE;iBAClC;aACF,CACF,CAAC;YAEF,IAAI,CAAC,CAAC,uBAAuB,YAAY,YAAK,CAAC,EAAE;gBAC/C,aAAM,CAAC,KAAK,CACV,iCAAiC,SAAS,KAAK,uBAAuB,CAAC,OAAO,EAAE,EAChF,qBAAS,CACV,CAAC;gBACF,OAAO;aACR;YAED,+EAA+E;YAC/E,aAAM,CAAC,IAAI,CACT,4BAA4B,aAAa,CAAC,EAAE,mBAAmB,SAAS,EAAE,EAC1E,qBAAS,CACV,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,gEAAgE;QAChE,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE;YACzB,QAAQ,CAAC,MAAM,CAAC,mBAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SAC3C;IACH,CAAC;IAEO,KAAK,CAAC,aAAa,CACzB,YAAoB,EACpB,GAAuB;QAEvB,OAAO,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC;YACvC,OAAO,EAAE,OAAO;YAChB,cAAc,EAAE,YAAY;YAC5B,GAAG;YACH,YAAY,EAAE,mBAAY,CAAC,EAAE;SAC9B,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAAC,GAAmB;QAChD,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CACtE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,2CAA0B,CAAC,IAAI,CAC1D,CAAC;QAEF,IAAI,CAAC,MAAM,EAAE;YACX,MAAM,IAAI,0BAAmB,CAC3B,IAAI,qBAAS,uCAAuC,CACrD,CAAC;SACH;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAA;AApJO;IADL,IAAA,aAAI,EAAC,QAAQ,CAAC;IAEZ,WAAA,IAAA,gBAAO,EAAC,kBAAkB,CAAC,CAAA;IAC3B,WAAA,IAAA,YAAG,GAAE,CAAA;IACL,WAAA,IAAA,YAAG,GAAE,CAAA;;;;+CAsHP;2BAnIU,gBAAgB;IAD5B,IAAA,mBAAU,EAAC,UAAU,CAAC;qCAGW,2BAAoB;QAC5B,mBAAY;QACX,8BAAa;QACL,4BAAqB;QAChC,8BAAuB;GANlC,gBAAgB,CA8J5B"}
@@ -0,0 +1,30 @@
1
+ import { LanguageCode, PaymentMethodHandler } from "@deenruv/core";
2
+ /**
3
+ * The handler for Stripe payments.
4
+ */
5
+ export declare const stripePaymentMethodHandler: PaymentMethodHandler<{
6
+ apiKey: {
7
+ type: "string";
8
+ label: {
9
+ languageCode: LanguageCode.en;
10
+ value: string;
11
+ }[];
12
+ ui: {
13
+ component: string;
14
+ };
15
+ };
16
+ webhookSecret: {
17
+ type: "string";
18
+ label: {
19
+ languageCode: LanguageCode.en;
20
+ value: string;
21
+ }[];
22
+ description: {
23
+ languageCode: LanguageCode.en;
24
+ value: string;
25
+ }[];
26
+ ui: {
27
+ component: string;
28
+ };
29
+ };
30
+ }>;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.stripePaymentMethodHandler = void 0;
7
+ const core_1 = require("@deenruv/core");
8
+ const stripe_1 = __importDefault(require("stripe"));
9
+ const stripe_utils_1 = require("./stripe-utils");
10
+ const stripe_service_1 = require("./stripe.service");
11
+ const { StripeError } = stripe_1.default.errors;
12
+ let stripeService;
13
+ /**
14
+ * The handler for Stripe payments.
15
+ */
16
+ exports.stripePaymentMethodHandler = new core_1.PaymentMethodHandler({
17
+ code: "stripe",
18
+ description: [{ languageCode: core_1.LanguageCode.en, value: "Stripe payments" }],
19
+ args: {
20
+ apiKey: {
21
+ type: "string",
22
+ label: [{ languageCode: core_1.LanguageCode.en, value: "API Key" }],
23
+ ui: { component: "password-form-input" },
24
+ },
25
+ webhookSecret: {
26
+ type: "string",
27
+ label: [
28
+ {
29
+ languageCode: core_1.LanguageCode.en,
30
+ value: "Webhook secret",
31
+ },
32
+ ],
33
+ description: [
34
+ {
35
+ languageCode: core_1.LanguageCode.en,
36
+ value: "Secret to validate incoming webhooks. Get this from your Stripe dashboard",
37
+ },
38
+ ],
39
+ ui: { component: "password-form-input" },
40
+ },
41
+ },
42
+ init(injector) {
43
+ stripeService = injector.get(stripe_service_1.StripeService);
44
+ },
45
+ createPayment(ctx, order, amount, ___, metadata) {
46
+ // Payment is already settled in Stripe by the time the webhook in stripe.controller.ts
47
+ // adds the payment to the order
48
+ if (ctx.apiType !== "admin") {
49
+ throw Error(`CreatePayment is not allowed for apiType '${ctx.apiType}'`);
50
+ }
51
+ const amountInMinorUnits = (0, stripe_utils_1.getAmountFromStripeMinorUnits)(order, metadata.paymentIntentAmountReceived);
52
+ return {
53
+ amount: amountInMinorUnits,
54
+ state: "Settled",
55
+ transactionId: metadata.paymentIntentId,
56
+ };
57
+ },
58
+ settlePayment() {
59
+ return {
60
+ success: true,
61
+ };
62
+ },
63
+ async createRefund(ctx, input, amount, order, payment, args) {
64
+ // TODO: Consider passing the "reason" property once this feature request is addressed:
65
+ // https://github.com/deenruv-ecommerce/deenruv/issues/893
66
+ try {
67
+ const refund = await stripeService.createRefund(ctx, order, payment, amount);
68
+ if (refund.status === "succeeded") {
69
+ return {
70
+ state: "Settled",
71
+ transactionId: payment.transactionId,
72
+ };
73
+ }
74
+ if (refund.status === "pending") {
75
+ return {
76
+ state: "Pending",
77
+ transactionId: payment.transactionId,
78
+ };
79
+ }
80
+ return {
81
+ state: "Failed",
82
+ transactionId: payment.transactionId,
83
+ metadata: {
84
+ message: refund.failure_reason,
85
+ },
86
+ };
87
+ }
88
+ catch (e) {
89
+ if (e instanceof StripeError) {
90
+ return {
91
+ state: "Failed",
92
+ transactionId: payment.transactionId,
93
+ metadata: {
94
+ type: e.type,
95
+ message: e.message,
96
+ },
97
+ };
98
+ }
99
+ throw e;
100
+ }
101
+ },
102
+ });
103
+ //# sourceMappingURL=stripe.handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.handler.js","sourceRoot":"","sources":["../../src/stripe/stripe.handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,158 @@
1
+ import { Type } from "@deenruv/core";
2
+ import { StripePluginOptions } from "./types";
3
+ /**
4
+ * @description
5
+ * Plugin to enable payments through [Stripe](https://stripe.com/docs) via the Payment Intents API.
6
+ *
7
+ * ## Requirements
8
+ *
9
+ * 1. You will need to create a Stripe account and get your secret key in the dashboard.
10
+ * 2. Create a webhook endpoint in the Stripe dashboard (Developers -> Webhooks, "Add an endpoint") which listens to the `payment_intent.succeeded`
11
+ * and `payment_intent.payment_failed` events. The URL should be `https://my-server.com/payments/stripe`, where
12
+ * `my-server.com` is the host of your Deenruv server. *Note:* for local development, you'll need to use
13
+ * the Stripe CLI to test your webhook locally. See the _local development_ section below.
14
+ * 3. Get the signing secret for the newly created webhook.
15
+ * 4. Install the Payments plugin and the Stripe Node library:
16
+ *
17
+ * `yarn add \@deenruv/payments-plugin stripe`
18
+ *
19
+ * or
20
+ *
21
+ * `npm install \@deenruv/payments-plugin stripe`
22
+ *
23
+ * ## Setup
24
+ *
25
+ * 1. Add the plugin to your DeenruvConfig `plugins` array:
26
+ * ```ts
27
+ * import { StripePlugin } from '\@deenruv/payments-plugin/package/stripe';
28
+ *
29
+ * // ...
30
+ *
31
+ * plugins: [
32
+ * StripePlugin.init({
33
+ * // This prevents different customers from using the same PaymentIntent
34
+ * storeCustomersInStripe: true,
35
+ * }),
36
+ * ]
37
+ * ````
38
+ * For all the plugin options, see the {@link StripePluginOptions} type.
39
+ * 2. Create a new PaymentMethod in the Admin UI, and select "Stripe payments" as the handler.
40
+ * 3. Set the webhook secret and API key in the PaymentMethod form.
41
+ *
42
+ * ## Storefront usage
43
+ *
44
+ * The plugin is designed to work with the [Custom payment flow](https://stripe.com/docs/payments/accept-a-payment?platform=web&ui=elements).
45
+ * In this flow, Stripe provides libraries which handle the payment UI and confirmation for you. You can install it in your storefront project
46
+ * with:
47
+ *
48
+ * ```shell
49
+ * yarn add \@stripe/stripe-js
50
+ * # or
51
+ * npm install \@stripe/stripe-js
52
+ * ```
53
+ *
54
+ * If you are using React, you should also consider installing `@stripe/react-stripe-js`, which is a wrapper around Stripe Elements.
55
+ *
56
+ * The high-level workflow is:
57
+ * 1. Create a "payment intent" on the server by executing the `createStripePaymentIntent` mutation which is exposed by this plugin.
58
+ * 2. Use the returned client secret to instantiate the Stripe Payment Element:
59
+ * ```ts
60
+ * import { Elements } from '\@stripe/react-stripe-js';
61
+ * import { loadStripe, Stripe } from '\@stripe/stripe-js';
62
+ * import { CheckoutForm } from './CheckoutForm';
63
+ *
64
+ * const stripePromise = getStripe('pk_test_....wr83u');
65
+ *
66
+ * type StripePaymentsProps = {
67
+ * clientSecret: string;
68
+ * orderCode: string;
69
+ * }
70
+ *
71
+ * export function StripePayments({ clientSecret, orderCode }: StripePaymentsProps) {
72
+ * const options = {
73
+ * // passing the client secret obtained from the server
74
+ * clientSecret,
75
+ * }
76
+ * return (
77
+ * <Elements stripe={stripePromise} options={options}>
78
+ * <CheckoutForm orderCode={orderCode} />
79
+ * </Elements>
80
+ * );
81
+ * }
82
+ * ```
83
+ * ```ts
84
+ * // CheckoutForm.tsx
85
+ * import { useStripe, useElements, PaymentElement } from '\@stripe/react-stripe-js';
86
+ * import { FormEvent } from 'react';
87
+ *
88
+ * export const CheckoutForm = ({ orderCode }: { orderCode: string }) => {
89
+ * const stripe = useStripe();
90
+ * const elements = useElements();
91
+ *
92
+ * const handleSubmit = async (event: FormEvent) => {
93
+ * // We don't want to let default form submission happen here,
94
+ * // which would refresh the page.
95
+ * event.preventDefault();
96
+ *
97
+ * if (!stripe || !elements) {
98
+ * // Stripe.js has not yet loaded.
99
+ * // Make sure to disable form submission until Stripe.js has loaded.
100
+ * return;
101
+ * }
102
+ *
103
+ * const result = await stripe.confirmPayment({
104
+ * //`Elements` instance that was used to create the Payment Element
105
+ * elements,
106
+ * confirmParams: {
107
+ * return_url: location.origin + `/checkout/confirmation/${orderCode}`,
108
+ * },
109
+ * });
110
+ *
111
+ * if (result.error) {
112
+ * // Show error to your customer (for example, payment details incomplete)
113
+ * console.log(result.error.message);
114
+ * } else {
115
+ * // Your customer will be redirected to your `return_url`. For some payment
116
+ * // methods like iDEAL, your customer will be redirected to an intermediate
117
+ * // site first to authorize the payment, then redirected to the `return_url`.
118
+ * }
119
+ * };
120
+ *
121
+ * return (
122
+ * <form onSubmit={handleSubmit}>
123
+ * <PaymentElement />
124
+ * <button disabled={!stripe}>Submit</button>
125
+ * </form>
126
+ * );
127
+ * };
128
+ * ```
129
+ * 3. Once the form is submitted and Stripe processes the payment, the webhook takes care of updating the order without additional action
130
+ * in the storefront. As in the code above, the customer will be redirected to `/checkout/confirmation/${orderCode}`.
131
+ *
132
+ * :::info
133
+ * A full working storefront example of the Stripe integration can be found in the
134
+ * [Remix Starter repo](https://github.com/deenruv-ecommerce/storefront-remix-starter/tree/master/app/components/checkout/stripe)
135
+ * :::
136
+ *
137
+ * ## Local development
138
+ *
139
+ * 1. Download & install the Stripe CLI: https://stripe.com/docs/stripe-cli
140
+ * 2. From your Stripe dashboard, go to Developers -> Webhooks and click "Add an endpoint" and follow the instructions
141
+ * under "Test in a local environment".
142
+ * 3. The Stripe CLI command will look like
143
+ * ```shell
144
+ * stripe listen --forward-to localhost:3000/payments/stripe
145
+ * ```
146
+ * 4. The Stripe CLI will create a webhook signing secret you can then use in your config of the StripePlugin.
147
+ *
148
+ * @docsCategory core plugins/PaymentsPlugin
149
+ * @docsPage StripePlugin
150
+ */
151
+ export declare class StripePlugin {
152
+ static options: StripePluginOptions;
153
+ /**
154
+ * @description
155
+ * Initialize the Stripe payment plugin
156
+ */
157
+ static init(options: StripePluginOptions): Type<StripePlugin>;
158
+ }