@unchainedshop/plugins 2.5.5 → 2.5.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.
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from '@unchainedshop/logger';
2
2
  import generateSignature, { Security } from './generateSignature.js';
3
3
  const { DATATRANS_SIGN_KEY, DATATRANS_SIGN2_KEY, DATATRANS_SECURITY = Security.DYNAMIC_SIGN, } = process.env;
4
- const logger = createLogger('unchained:core-payment:datatrans');
4
+ const logger = createLogger('unchained:core-payment:datatrans:webhook');
5
5
  export const datatransHandler = async (req, res) => {
6
6
  const resolvedContext = req.unchainedContext;
7
7
  const { modules } = resolvedContext;
@@ -15,12 +15,15 @@ export const datatransHandler = async (req, res) => {
15
15
  signKey: DATATRANS_SIGN2_KEY || DATATRANS_SIGN_KEY,
16
16
  })(timestamp, req.body);
17
17
  if (hash !== comparableSignature) {
18
- logger.error(`Datatrans Plugin: Hash mismatch: ${signature} / ${comparableSignature}`, req.body);
18
+ logger.error(`hash mismatch: ${signature} / ${comparableSignature}`, req.body);
19
19
  res.writeHead(403);
20
20
  res.end('Hash mismatch');
21
21
  return;
22
22
  }
23
23
  const transaction = JSON.parse(req.body);
24
+ logger.verbose(`received request`, {
25
+ type: transaction.type,
26
+ });
24
27
  if (transaction.status === 'authorized') {
25
28
  const userId = transaction.refno2;
26
29
  const referenceId = Buffer.from(transaction.refno, 'base64').toString('hex');
@@ -28,7 +31,7 @@ export const datatransHandler = async (req, res) => {
28
31
  if (transaction.type === 'card_check') {
29
32
  const paymentProviderId = referenceId;
30
33
  const paymentCredentials = await modules.payment.registerCredentials(paymentProviderId, { userId, transactionContext: { transactionId: transaction.transactionId } }, { ...resolvedContext });
31
- logger.info(`Datatrans Webhook: Unchained registered payment credentials for ${userId}`, {
34
+ logger.info(`registered payment credentials for ${userId}`, {
32
35
  userId,
33
36
  });
34
37
  res.writeHead(200);
@@ -44,7 +47,7 @@ export const datatransHandler = async (req, res) => {
44
47
  throw new Error(`Order Payment with id ${orderPaymentId} not found`);
45
48
  const order = await modules.orders.checkout(orderPayment.orderId, { paymentContext: { userId, transactionId: transaction.transactionId } }, resolvedContext);
46
49
  res.writeHead(200);
47
- logger.info(`Datatrans Webhook: Unchained confirmed checkout for order ${order.orderNumber}`, {
50
+ logger.info(`confirmed checkout for order ${order.orderNumber}`, {
48
51
  orderId: order._id,
49
52
  });
50
53
  res.end(JSON.stringify(order));
@@ -52,7 +55,7 @@ export const datatransHandler = async (req, res) => {
52
55
  }
53
56
  }
54
57
  catch (e) {
55
- logger.error(`Datatrans Webhook: Unchained rejected to checkout with message`, e);
58
+ logger.error(`rejected to checkout with message`, e);
56
59
  res.writeHead(500);
57
60
  res.end(JSON.stringify({ name: e.name, code: e.code, message: e.message }));
58
61
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/payment/datatrans-v2/middleware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/payment/datatrans-v2/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export * from './middleware.js';
@@ -1,137 +1,8 @@
1
1
  import { PaymentAdapter, PaymentDirector, PaymentError } from '@unchainedshop/core-payment';
2
2
  import { createLogger } from '@unchainedshop/logger';
3
- /*
4
- Test Webhooks:
5
-
6
- brew install stripe/stripe-cli/stripe
7
- stripe login --api-key sk_....
8
- stripe listen --forward-to http://localhost:4010/payment/stripe
9
- stripe trigger payment_intent.succeeded
10
- */
11
- import createStripeClient from 'stripe';
3
+ import stripe, { createOrderPaymentIntent, createRegistrationIntent } from './stripe.js';
4
+ export * from './middleware.js';
12
5
  const logger = createLogger('unchained:core-payment:stripe');
13
- const { STRIPE_SECRET, STRIPE_ENDPOINT_SECRET, EMAIL_WEBSITE_NAME } = process.env;
14
- // eslint-disable-next-line
15
- // @ts-ignore
16
- const stripe = createStripeClient(STRIPE_SECRET);
17
- export const stripeHandler = async (request, response) => {
18
- const sig = request.headers['stripe-signature'];
19
- const resolvedContext = request.unchainedContext;
20
- const { modules } = resolvedContext;
21
- let event;
22
- try {
23
- event = stripe.webhooks.constructEvent(request.body, sig, STRIPE_ENDPOINT_SECRET);
24
- logger.verbose(`Webhook received`, {
25
- type: event.type,
26
- });
27
- }
28
- catch (err) {
29
- logger.error(`Webhook failed: ${err.message}`);
30
- response.writeHead(400);
31
- response.end(err.message);
32
- return;
33
- }
34
- try {
35
- if (event.type === 'payment_intent.succeeded') {
36
- const paymentIntent = event.data.object;
37
- const orderPaymentId = paymentIntent.metadata?.orderPaymentId;
38
- logger.verbose(`Webhook tries to checkout with orderPaymentId: ${orderPaymentId}`, {
39
- type: event.type,
40
- });
41
- await modules.orders.payments.logEvent(orderPaymentId, event);
42
- const orderPayment = await modules.orders.payments.findOrderPayment({
43
- orderPaymentId,
44
- });
45
- if (!orderPayment) {
46
- throw new Error(`Order payment object not found with orderPaymentId: ${orderPaymentId}`);
47
- }
48
- const order = await modules.orders.checkout(orderPayment.orderId, {
49
- transactionContext: {
50
- paymentIntentId: paymentIntent.id,
51
- },
52
- paymentContext: {
53
- paymentIntentId: paymentIntent.id,
54
- },
55
- }, resolvedContext);
56
- logger.info(`Webhook confirmed checkout for order: ${order._id}`, {
57
- orderId: order._id,
58
- type: event.type,
59
- });
60
- }
61
- else if (event.type === 'setup_intent.succeeded') {
62
- const setupIntent = event.data.object;
63
- const { paymentProviderId, userId } = setupIntent.metadata;
64
- logger.verbose(`Webhook tries to register payment credential with paymentProviderId: ${paymentProviderId}`, {
65
- type: event.type,
66
- userId,
67
- });
68
- await modules.payment.registerCredentials(paymentProviderId, {
69
- transactionContext: {
70
- setupIntentId: setupIntent.id,
71
- },
72
- userId,
73
- }, resolvedContext);
74
- logger.info(`Webhook registered payment credentials with paymentProviderId: ${paymentProviderId}`, {
75
- userId,
76
- type: event.type,
77
- });
78
- }
79
- else {
80
- logger.verbose(`Unhandled webhook type`, {
81
- type: event.type,
82
- });
83
- response.writeHead(404);
84
- response.end();
85
- return;
86
- }
87
- }
88
- catch (err) {
89
- logger.error(`Webhook failed: ${err.message}`, {
90
- type: event.type,
91
- });
92
- response.writeHead(400);
93
- response.end(err.message || 'Error');
94
- return;
95
- }
96
- // Return a 200 response to acknowledge receipt of the event
97
- response.end(JSON.stringify({ received: true }));
98
- };
99
- const createRegistrationIntent = async ({ userId, name, email, paymentProviderId }, options = {}) => {
100
- const customer = await stripe.customers.create({
101
- metadata: {
102
- userId,
103
- },
104
- name,
105
- email,
106
- });
107
- const setupIntent = await stripe.setupIntents.create({
108
- customer: customer.id,
109
- metadata: {
110
- userId,
111
- paymentProviderId,
112
- },
113
- ...options,
114
- });
115
- return setupIntent;
116
- };
117
- const createOrderPaymentIntent = async ({ order, orderPayment, pricing }, options = {}) => {
118
- const reference = EMAIL_WEBSITE_NAME || order._id;
119
- const { currency, amount } = pricing.total({ useNetPrice: false });
120
- const paymentIntent = await stripe.paymentIntents.create({
121
- amount: Math.round(amount),
122
- currency: currency.toLowerCase(),
123
- description: `${reference} #${order.orderNumber}`,
124
- statement_descriptor: order.orderNumber,
125
- statement_descriptor_suffix: reference,
126
- receipt_email: order.contact?.emailAddress,
127
- setup_future_usage: 'off_session',
128
- metadata: {
129
- orderPaymentId: orderPayment._id,
130
- },
131
- ...options,
132
- });
133
- return paymentIntent;
134
- };
135
6
  const Stripe = {
136
7
  ...PaymentAdapter,
137
8
  key: 'shop.unchained.payment.stripe',
@@ -144,10 +15,8 @@ const Stripe = {
144
15
  const { modules } = params.context;
145
16
  const adapterActions = {
146
17
  ...PaymentAdapter.actions(params),
147
- // eslint-disable-next-line
148
18
  configurationError() {
149
- // eslint-disable-line
150
- if (!STRIPE_SECRET || !STRIPE_ENDPOINT_SECRET) {
19
+ if (!stripe) {
151
20
  return PaymentError.INCOMPLETE_CONFIGURATION;
152
21
  }
153
22
  return null;
@@ -162,7 +31,6 @@ const Stripe = {
162
31
  },
163
32
  validate: async ({ token }) => {
164
33
  const paymentMethod = await stripe.paymentMethods.retrieve(token);
165
- // TODO: Add further checks like expiration of cards
166
34
  return !!paymentMethod;
167
35
  },
168
36
  register: async ({ setupIntentId }) => {
@@ -183,7 +51,6 @@ const Stripe = {
183
51
  return null;
184
52
  },
185
53
  sign: async (transactionContext = {}) => {
186
- // eslint-disable-line
187
54
  const { orderPayment, order, paymentProviderId } = params.paymentContext;
188
55
  if (orderPayment) {
189
56
  const pricing = await modules.orders.pricingSheet(order);
@@ -236,4 +103,4 @@ const Stripe = {
236
103
  },
237
104
  };
238
105
  PaymentDirector.registerAdapter(Stripe);
239
- //# sourceMappingURL=stripe.js.map
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/payment/stripe/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ import { createLogger } from '@unchainedshop/logger';
2
+ import stripe from './stripe.js';
3
+ const logger = createLogger('unchained:core-payment:stripe:webhook');
4
+ const { STRIPE_ENDPOINT_SECRET, STRIPE_WEBHOOK_ENVIRONMENT } = process.env;
5
+ function checkEnvironment(metadata) {
6
+ const environmentInMetadata = metadata?.environment;
7
+ const environmentInEnv = STRIPE_WEBHOOK_ENVIRONMENT;
8
+ if (!environmentInMetadata && !environmentInEnv) {
9
+ return true;
10
+ }
11
+ return environmentInEnv === environmentInMetadata;
12
+ }
13
+ export const stripeHandler = async (request, response) => {
14
+ const resolvedContext = request.unchainedContext;
15
+ const { modules } = resolvedContext;
16
+ let event;
17
+ try {
18
+ const sig = request.headers['stripe-signature'];
19
+ event = stripe.webhooks.constructEvent(request.body, sig, STRIPE_ENDPOINT_SECRET);
20
+ logger.verbose(`received event`, {
21
+ type: event.type,
22
+ });
23
+ }
24
+ catch (err) {
25
+ logger.error(`failed event validation with: ${err.message}`);
26
+ response.writeHead(400);
27
+ response.end(err.message);
28
+ return;
29
+ }
30
+ try {
31
+ if (event.type === 'payment_intent.succeeded') {
32
+ const paymentIntent = event.data.object;
33
+ const { orderPaymentId } = paymentIntent.metadata || {};
34
+ if (!checkEnvironment(paymentIntent.metadata)) {
35
+ logger.verbose(`event ignored because of environment difference`, {
36
+ type: event.type,
37
+ });
38
+ response.end(JSON.stringify({ received: true, ignored: true }));
39
+ return;
40
+ }
41
+ logger.verbose(`checkout with orderPaymentId: ${orderPaymentId}`, {
42
+ type: event.type,
43
+ });
44
+ await modules.orders.payments.logEvent(orderPaymentId, event);
45
+ const orderPayment = await modules.orders.payments.findOrderPayment({
46
+ orderPaymentId,
47
+ });
48
+ if (!orderPayment) {
49
+ throw new Error(`order payment not found with orderPaymentId: ${orderPaymentId}`);
50
+ }
51
+ const order = await modules.orders.checkout(orderPayment.orderId, {
52
+ transactionContext: {
53
+ paymentIntentId: paymentIntent.id,
54
+ },
55
+ paymentContext: {
56
+ paymentIntentId: paymentIntent.id,
57
+ },
58
+ }, resolvedContext);
59
+ logger.info(`confirmed checkout for order: ${order._id}`, {
60
+ orderId: order._id,
61
+ type: event.type,
62
+ });
63
+ }
64
+ else if (event.type === 'setup_intent.succeeded') {
65
+ const setupIntent = event.data.object;
66
+ const { paymentProviderId, userId } = setupIntent.metadata || {};
67
+ if (!checkEnvironment(setupIntent.metadata)) {
68
+ response.end(JSON.stringify({ received: true, ignored: true }));
69
+ return;
70
+ }
71
+ logger.verbose(`registered payment credential with paymentProviderId: ${paymentProviderId}`, {
72
+ type: event.type,
73
+ userId,
74
+ });
75
+ await modules.payment.registerCredentials(paymentProviderId, {
76
+ transactionContext: {
77
+ setupIntentId: setupIntent.id,
78
+ },
79
+ userId,
80
+ }, resolvedContext);
81
+ logger.info(`registered payment credentials with paymentProviderId: ${paymentProviderId}`, {
82
+ userId,
83
+ type: event.type,
84
+ });
85
+ }
86
+ else {
87
+ logger.verbose(`unhandled type`, {
88
+ type: event.type,
89
+ });
90
+ response.writeHead(404);
91
+ response.end();
92
+ return;
93
+ }
94
+ }
95
+ catch (err) {
96
+ logger.error(`failed with: ${err.message}`, {
97
+ type: event.type,
98
+ });
99
+ response.writeHead(400);
100
+ response.end(err.message || 'Error');
101
+ return;
102
+ }
103
+ // Return a 200 response to acknowledge receipt of the event
104
+ logger.verbose(`event processed`, {
105
+ type: event.type,
106
+ });
107
+ response.end(JSON.stringify({ received: true }));
108
+ };
109
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/payment/stripe/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import Stripe from 'stripe';
2
+ declare const stripe: Stripe;
3
+ export default stripe;
4
+ export declare const createRegistrationIntent: ({ userId, name, email, paymentProviderId }: {
5
+ userId: any;
6
+ name: any;
7
+ email: any;
8
+ paymentProviderId: any;
9
+ }, options?: {}) => Promise<Stripe.Response<Stripe.SetupIntent>>;
10
+ export declare const createOrderPaymentIntent: ({ order, orderPayment, pricing }: {
11
+ order: any;
12
+ orderPayment: any;
13
+ pricing: any;
14
+ }, options?: {}) => Promise<Stripe.Response<Stripe.PaymentIntent>>;
@@ -0,0 +1,46 @@
1
+ import Stripe from 'stripe';
2
+ const { STRIPE_SECRET, STRIPE_WEBHOOK_ENVIRONMENT, EMAIL_WEBSITE_NAME } = process.env;
3
+ const stripe = new Stripe(STRIPE_SECRET, {
4
+ apiVersion: '2023-08-16',
5
+ });
6
+ export default stripe;
7
+ export const createRegistrationIntent = async ({ userId, name, email, paymentProviderId }, options = {}) => {
8
+ const customer = await stripe.customers.create({
9
+ metadata: {
10
+ userId,
11
+ environment: STRIPE_WEBHOOK_ENVIRONMENT ?? null,
12
+ },
13
+ name,
14
+ email,
15
+ });
16
+ const setupIntent = await stripe.setupIntents.create({
17
+ customer: customer.id,
18
+ metadata: {
19
+ userId,
20
+ paymentProviderId,
21
+ environment: STRIPE_WEBHOOK_ENVIRONMENT ?? null,
22
+ },
23
+ ...options,
24
+ });
25
+ return setupIntent;
26
+ };
27
+ export const createOrderPaymentIntent = async ({ order, orderPayment, pricing }, options = {}) => {
28
+ const reference = EMAIL_WEBSITE_NAME || order._id;
29
+ const { currency, amount } = pricing.total({ useNetPrice: false });
30
+ const paymentIntent = await stripe.paymentIntents.create({
31
+ amount: Math.round(amount),
32
+ currency: currency.toLowerCase(),
33
+ description: `${reference} #${order.orderNumber}`,
34
+ statement_descriptor: order.orderNumber,
35
+ statement_descriptor_suffix: reference,
36
+ receipt_email: order.contact?.emailAddress,
37
+ setup_future_usage: 'off_session',
38
+ metadata: {
39
+ orderPaymentId: orderPayment._id,
40
+ environment: STRIPE_WEBHOOK_ENVIRONMENT ?? null,
41
+ },
42
+ ...options,
43
+ });
44
+ return paymentIntent;
45
+ };
46
+ //# sourceMappingURL=stripe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../../src/payment/stripe/stripe.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,EAAE,aAAa,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;AAEtF,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,aAAa,EAAE;IACvC,UAAU,EAAE,YAAY;CACzB,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC;AAEtB,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,EAC3C,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAC1C,OAAO,GAAG,EAAE,EACZ,EAAE;IACF,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAC7C,QAAQ,EAAE;YACR,MAAM;YACN,WAAW,EAAE,0BAA0B,IAAI,IAAI;SAChD;QACD,IAAI;QACJ,KAAK;KACN,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;QACnD,QAAQ,EAAE,QAAQ,CAAC,EAAE;QACrB,QAAQ,EAAE;YACR,MAAM;YACN,iBAAiB;YACjB,WAAW,EAAE,0BAA0B,IAAI,IAAI;SAChD;QACD,GAAG,OAAO;KACX,CAAC,CAAC;IACH,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE;IAC/F,MAAM,SAAS,GAAG,kBAAkB,IAAI,KAAK,CAAC,GAAG,CAAC;IAClD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;IACnE,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC;QACvD,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QAC1B,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE;QAChC,WAAW,EAAE,GAAG,SAAS,KAAK,KAAK,CAAC,WAAW,EAAE;QACjD,oBAAoB,EAAE,KAAK,CAAC,WAAW;QACvC,2BAA2B,EAAE,SAAS;QACtC,aAAa,EAAE,KAAK,CAAC,OAAO,EAAE,YAAY;QAC1C,kBAAkB,EAAE,aAAa;QACjC,QAAQ,EAAE;YACR,cAAc,EAAE,YAAY,CAAC,GAAG;YAChC,WAAW,EAAE,0BAA0B,IAAI,IAAI;SAChD;QACD,GAAG,OAAO;KACX,CAAC,CAAC;IACH,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC"}
@@ -13,7 +13,7 @@ import './payment/worldline-saferpay/index.js';
13
13
  import { datatransHandler } from './payment/datatrans-v2/index.js';
14
14
  import { configureCryptopayModule, cryptopayHandler } from './payment/cryptopay/index.js';
15
15
  import { appleIAPHandler, configureAppleTransactionsModule } from './payment/apple-iap/index.js';
16
- import { stripeHandler } from './payment/stripe.js';
16
+ import { stripeHandler } from './payment/stripe/index.js';
17
17
  import { postfinanceCheckoutHandler } from './payment/postfinance-checkout/index.js';
18
18
  // Warehousing
19
19
  import './warehousing/store.js';
@@ -1 +1 @@
1
- {"version":3,"file":"plugins-index.js","sourceRoot":"","sources":["../src/plugins-index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,qCAAqC,CAAC;AAEtF,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,WAAW;AACX,OAAO,oBAAoB,CAAC;AAC5B,OAAO,wBAAwB,CAAC;AAChC,OAAO,4BAA4B,CAAC;AACpC,OAAO,sBAAsB,CAAC;AAE9B,UAAU;AACV,OAAO,sBAAsB,CAAC;AAC9B,OAAO,8BAA8B,CAAC;AACtC,OAAO,8BAA8B,CAAC;AACtC,OAAO,uCAAuC,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AACjG,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AAErF,cAAc;AACd,OAAO,wBAAwB,CAAC;AAChC,OAAO,6BAA6B,CAAC;AAErC,UAAU;AACV,OAAO,yCAAyC,CAAC;AACjD,OAAO,+BAA+B,CAAC;AACvC,OAAO,2BAA2B,CAAC;AACnC,OAAO,4BAA4B,CAAC;AACpC,OAAO,0BAA0B,CAAC;AAClC,OAAO,6BAA6B,CAAC;AACrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,4BAA4B,CAAC;AACpC,OAAO,oCAAoC,CAAC;AAC5C,OAAO,2CAA2C,CAAC;AACnD,OAAO,+BAA+B,CAAC;AACvC,OAAO,gCAAgC,CAAC;AACxC,OAAO,iCAAiC,CAAC;AAEzC,kBAAkB;AAClB,OAAO,2BAA2B,CAAC;AACnC,OAAO,2BAA2B,CAAC;AAEnC,aAAa;AACb,OAAO,wBAAwB,CAAC;AAEhC,cAAc;AACd,OAAO,2BAA2B,CAAC;AAEnC,cAAc;AACd,OAAO,gCAAgC,CAAC;AAExC,UAAU;AACV,OAAO,yBAAyB,CAAC;AACjC,OAAO,2BAA2B,CAAC;AACnC,OAAO,qBAAqB,CAAC;AAC7B,OAAO,sBAAsB,CAAC;AAC9B,OAAO,0BAA0B,CAAC;AAClC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,mBAAmB,CAAC;AAC3B,OAAO,iBAAiB,CAAC;AACzB,OAAO,+BAA+B,CAAC;AACvC,OAAO,8BAA8B,CAAC;AACtC,OAAO,iCAAiC,CAAC;AACzC,OAAO,mCAAmC,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,oCAAoC,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AAEnF,mBAAmB;AACnB,OAAO,kCAAkC,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,+BAA+B,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,4BAA4B,CAAC;AACpC,OAAO,8BAA8B,CAAC;AAEtC,MAAM,EACJ,sBAAsB,GAAG,oBAAoB,EAC7C,mBAAmB,GAAG,iBAAiB,EACvC,uBAAuB,GAAG,+BAA+B,EACzD,sBAAsB,GAAG,4BAA4B,EACrD,sBAAsB,GAAG,oBAAoB;AAC7C,oCAAoC;AACpC,sBAAsB,GAAG,SAAS,GACnC,GAAG,OAAO,CAAC,GAAG,CAAC;AAEhB,wCAAwC;AACxC,8DAA8D;AAE9D,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,iBAAiB,EAAE;QACjB,SAAS,EAAE,gCAAgC;KAC5C;IACD,iBAAiB,EAAE;QACjB,SAAS,EAAE,+BAA+B;KAC3C;IACD,SAAS,EAAE;QACT,SAAS,EAAE,wBAAwB;KACpC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC7C,GAAG,EACH,EAAE,YAAY,EAAmC,EACjD,EAAE;IACF,+BAA+B,CAAC,GAAG,EAAE,sBAAsB,EAAE,aAAa,CAAC,CAAC;IAE5E,+BAA+B,CAAC,GAAG,EAAE,sBAAsB,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAE/F,+BAA+B,CAC7B,GAAG,EACH,mBAAmB,EACnB,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,EACzC,aAAa,CACd,CAAC;IAEF,+BAA+B,CAC7B,GAAG,EACH,uBAAuB,EACvB,OAAO,CAAC,IAAI,EAAE,EACd,0BAA0B,CAC3B,CAAC;IAEF,+BAA+B,CAC7B,GAAG,EACH,sBAAsB,EACtB,OAAO,CAAC,IAAI,CAAC;QACX,IAAI,EAAE,kBAAkB;KACzB,CAAC,EACF,gBAAgB,CACjB,CAAC;IAEF,+BAA+B,CAC7B,GAAG,EACH,sBAAsB,EACtB,OAAO,CAAC,IAAI,CAAC;QACX,MAAM,EAAE,KAAK;KACd,CAAC,EACF,eAAe,CAChB,CAAC;IAEF,mCAAmC;IACnC,SAAS;IACT,2BAA2B;IAC3B,mBAAmB;IACnB,qBAAqB;IACrB,QAAQ;IACR,iBAAiB;IACjB,KAAK;IAEL,oBAAoB,CAAC,YAAY,CAAC,CAAC;IACnC,6BAA6B,CAAC,YAAY,CAAC,CAAC;IAC5C,oCAAoC,EAAE,CAAC;AACzC,CAAC,CAAC"}
1
+ {"version":3,"file":"plugins-index.js","sourceRoot":"","sources":["../src/plugins-index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unchainedshop/plugins",
3
- "version": "2.5.5",
3
+ "version": "2.5.6",
4
4
  "main": "lib/plugins-index.js",
5
5
  "types": "lib/plugins-index.d.ts",
6
6
  "exports": {
@@ -79,29 +79,29 @@
79
79
  "express": "^4.18.2",
80
80
  "mongodb": "^5.9.0",
81
81
  "postfinancecheckout": "^4.1.1",
82
- "stripe": "^13.7.0",
82
+ "stripe": "^13.9.0",
83
83
  "tiny-secp256k1": "^2.2.3",
84
84
  "web-push": "^3.6.3"
85
85
  },
86
86
  "devDependencies": {
87
87
  "@redis/client": "^1.5.11",
88
- "@types/node": "^18.18.0",
89
- "@unchainedshop/api": "^2.5.5",
90
- "@unchainedshop/core-delivery": "^2.5.5",
91
- "@unchainedshop/core-enrollments": "^2.5.5",
92
- "@unchainedshop/core-filters": "^2.5.5",
93
- "@unchainedshop/core-messaging": "^2.5.5",
94
- "@unchainedshop/core-orders": "^2.5.5",
95
- "@unchainedshop/core-payment": "^2.5.5",
96
- "@unchainedshop/core-products": "^2.5.5",
97
- "@unchainedshop/core-quotations": "^2.5.5",
98
- "@unchainedshop/core-warehousing": "^2.5.5",
99
- "@unchainedshop/core-worker": "^2.5.5",
100
- "@unchainedshop/events": "^2.5.5",
101
- "@unchainedshop/file-upload": "^2.5.5",
102
- "@unchainedshop/logger": "^2.5.5",
103
- "@unchainedshop/types": "^2.5.5",
104
- "@unchainedshop/utils": "^2.5.5",
88
+ "@types/node": "^18.18.3",
89
+ "@unchainedshop/api": "^2.5.6",
90
+ "@unchainedshop/core-delivery": "^2.5.6",
91
+ "@unchainedshop/core-enrollments": "^2.5.6",
92
+ "@unchainedshop/core-filters": "^2.5.6",
93
+ "@unchainedshop/core-messaging": "^2.5.6",
94
+ "@unchainedshop/core-orders": "^2.5.6",
95
+ "@unchainedshop/core-payment": "^2.5.6",
96
+ "@unchainedshop/core-products": "^2.5.6",
97
+ "@unchainedshop/core-quotations": "^2.5.6",
98
+ "@unchainedshop/core-warehousing": "^2.5.6",
99
+ "@unchainedshop/core-worker": "^2.5.6",
100
+ "@unchainedshop/events": "^2.5.6",
101
+ "@unchainedshop/file-upload": "^2.5.6",
102
+ "@unchainedshop/logger": "^2.5.6",
103
+ "@unchainedshop/types": "^2.5.6",
104
+ "@unchainedshop/utils": "^2.5.6",
105
105
  "bluebird": "^3.7.2",
106
106
  "event-iterator": "^2.0.0",
107
107
  "express": "^4.18.2",
@@ -10,7 +10,7 @@ const {
10
10
  DATATRANS_SECURITY = Security.DYNAMIC_SIGN,
11
11
  } = process.env;
12
12
 
13
- const logger = createLogger('unchained:core-payment:datatrans');
13
+ const logger = createLogger('unchained:core-payment:datatrans:webhook');
14
14
 
15
15
  export const datatransHandler = async (req, res) => {
16
16
  const resolvedContext = req.unchainedContext as Context;
@@ -27,7 +27,7 @@ export const datatransHandler = async (req, res) => {
27
27
  })(timestamp, req.body);
28
28
 
29
29
  if (hash !== comparableSignature) {
30
- logger.error(`Datatrans Plugin: Hash mismatch: ${signature} / ${comparableSignature}`, req.body);
30
+ logger.error(`hash mismatch: ${signature} / ${comparableSignature}`, req.body);
31
31
  res.writeHead(403);
32
32
  res.end('Hash mismatch');
33
33
  return;
@@ -35,6 +35,10 @@ export const datatransHandler = async (req, res) => {
35
35
 
36
36
  const transaction: StatusResponseSuccess = JSON.parse(req.body) as StatusResponseSuccess;
37
37
 
38
+ logger.verbose(`received request`, {
39
+ type: transaction.type,
40
+ });
41
+
38
42
  if (transaction.status === 'authorized') {
39
43
  const userId = transaction.refno2;
40
44
  const referenceId = Buffer.from(transaction.refno, 'base64').toString('hex');
@@ -47,7 +51,7 @@ export const datatransHandler = async (req, res) => {
47
51
  { userId, transactionContext: { transactionId: transaction.transactionId } },
48
52
  { ...resolvedContext },
49
53
  );
50
- logger.info(`Datatrans Webhook: Unchained registered payment credentials for ${userId}`, {
54
+ logger.info(`registered payment credentials for ${userId}`, {
51
55
  userId,
52
56
  });
53
57
  res.writeHead(200);
@@ -67,14 +71,14 @@ export const datatransHandler = async (req, res) => {
67
71
  resolvedContext,
68
72
  );
69
73
  res.writeHead(200);
70
- logger.info(`Datatrans Webhook: Unchained confirmed checkout for order ${order.orderNumber}`, {
74
+ logger.info(`confirmed checkout for order ${order.orderNumber}`, {
71
75
  orderId: order._id,
72
76
  });
73
77
  res.end(JSON.stringify(order));
74
78
  return;
75
79
  }
76
80
  } catch (e) {
77
- logger.error(`Datatrans Webhook: Unchained rejected to checkout with message`, e);
81
+ logger.error(`rejected to checkout with message`, e);
78
82
  res.writeHead(500);
79
83
  res.end(JSON.stringify({ name: e.name, code: e.code, message: e.message }));
80
84
  return;
@@ -0,0 +1,145 @@
1
+ import { IPaymentAdapter } from '@unchainedshop/types/payments.js';
2
+ import { PaymentAdapter, PaymentDirector, PaymentError } from '@unchainedshop/core-payment';
3
+ import { createLogger } from '@unchainedshop/logger';
4
+ import stripe, { createOrderPaymentIntent, createRegistrationIntent } from './stripe.js';
5
+
6
+ export * from './middleware.js';
7
+
8
+ const logger = createLogger('unchained:core-payment:stripe');
9
+
10
+ const Stripe: IPaymentAdapter = {
11
+ ...PaymentAdapter,
12
+
13
+ key: 'shop.unchained.payment.stripe',
14
+ label: 'Stripe',
15
+ version: '2.0.0',
16
+
17
+ typeSupported(type) {
18
+ return type === 'GENERIC';
19
+ },
20
+
21
+ actions: (params) => {
22
+ const { modules } = params.context;
23
+
24
+ const adapterActions = {
25
+ ...PaymentAdapter.actions(params),
26
+
27
+ configurationError() {
28
+ if (!stripe) {
29
+ return PaymentError.INCOMPLETE_CONFIGURATION;
30
+ }
31
+ return null;
32
+ },
33
+
34
+ isActive: () => {
35
+ if (adapterActions.configurationError() === null) return true;
36
+ return false;
37
+ },
38
+
39
+ isPayLaterAllowed() {
40
+ return false;
41
+ },
42
+
43
+ validate: async ({ token }) => {
44
+ const paymentMethod = await stripe.paymentMethods.retrieve(token);
45
+ return !!paymentMethod;
46
+ },
47
+
48
+ register: async ({ setupIntentId }) => {
49
+ if (!setupIntentId) {
50
+ throw new Error('You have to provide a setupIntentId');
51
+ }
52
+
53
+ const setupIntent = await stripe.setupIntents.retrieve(setupIntentId);
54
+ if (setupIntent.status === 'succeeded') {
55
+ return {
56
+ token: setupIntent.payment_method,
57
+ customer: setupIntent.customer,
58
+ // payment_method_options: setupIntent.payment_method_options,
59
+ payment_method_types: setupIntent.payment_method_types,
60
+ usage: setupIntent.usage,
61
+ };
62
+ }
63
+
64
+ logger.warn('Registration declined', setupIntentId);
65
+ return null;
66
+ },
67
+
68
+ sign: async (transactionContext = {}) => {
69
+ const { orderPayment, order, paymentProviderId } = params.paymentContext;
70
+
71
+ if (orderPayment) {
72
+ const pricing = await modules.orders.pricingSheet(order);
73
+ const paymentIntent = await createOrderPaymentIntent(
74
+ { order, orderPayment, pricing },
75
+ transactionContext,
76
+ );
77
+ return paymentIntent.client_secret;
78
+ }
79
+
80
+ const userId = order?.userId || params.paymentContext?.userId;
81
+ const user = await modules.users.findUserById(userId);
82
+ const email = modules.users.primaryEmail(user)?.address;
83
+ const name = user.profile.displayName || user.username || email;
84
+
85
+ const paymentIntent = await createRegistrationIntent(
86
+ { userId, name, email, paymentProviderId },
87
+ transactionContext,
88
+ );
89
+ return paymentIntent.client_secret;
90
+ },
91
+
92
+ charge: async ({ paymentIntentId, paymentCredentials }) => {
93
+ if (!paymentIntentId && !paymentCredentials) {
94
+ throw new Error('You have to provide paymentIntentId or paymentCredentials');
95
+ }
96
+
97
+ const { order } = params.paymentContext;
98
+ const orderPayment = await modules.orders.payments.findOrderPayment({
99
+ orderPaymentId: order.paymentId,
100
+ });
101
+
102
+ const pricing = await modules.orders.pricingSheet(order);
103
+
104
+ const paymentIntentObject = paymentIntentId
105
+ ? await stripe.paymentIntents.retrieve(paymentIntentId)
106
+ : await createOrderPaymentIntent(
107
+ { orderPayment, order, pricing },
108
+ {
109
+ customer: paymentCredentials.meta?.customer,
110
+ confirm: true,
111
+ payment_method: paymentCredentials.token,
112
+ payment_method_types: paymentCredentials.meta?.payment_method_types, // eslint-disable-line
113
+ // payment_method_options: paymentCredentials.meta?.payment_method_options, // eslint-disable-line
114
+ },
115
+ );
116
+
117
+ const { currency, amount } = pricing.total({ useNetPrice: false });
118
+
119
+ if (
120
+ paymentIntentObject.currency !== currency.toLowerCase() ||
121
+ paymentIntentObject.amount !== Math.round(amount)
122
+ ) {
123
+ throw new Error('The price has changed since the intent has been created');
124
+ }
125
+ if (paymentIntentObject.metadata?.orderPaymentId !== orderPayment?._id) {
126
+ throw new Error('The order payment is different from the initiating intent');
127
+ }
128
+
129
+ if (paymentIntentObject.status === 'succeeded') {
130
+ return paymentIntentObject;
131
+ }
132
+
133
+ logger.verbose('Charge postponed because paymentIntent has wrong status', {
134
+ orderPaymentId: paymentIntentObject.id,
135
+ });
136
+
137
+ return false;
138
+ },
139
+ };
140
+
141
+ return adapterActions;
142
+ },
143
+ };
144
+
145
+ PaymentDirector.registerAdapter(Stripe);
@@ -0,0 +1,132 @@
1
+ import { Context } from '@unchainedshop/types/api.js';
2
+ import { createLogger } from '@unchainedshop/logger';
3
+ import stripe from './stripe.js';
4
+
5
+ const logger = createLogger('unchained:core-payment:stripe:webhook');
6
+
7
+ const { STRIPE_ENDPOINT_SECRET, STRIPE_WEBHOOK_ENVIRONMENT } = process.env;
8
+
9
+ function checkEnvironment(metadata: any) {
10
+ const environmentInMetadata = metadata?.environment;
11
+ const environmentInEnv = STRIPE_WEBHOOK_ENVIRONMENT;
12
+
13
+ if (!environmentInMetadata && !environmentInEnv) {
14
+ return true;
15
+ }
16
+
17
+ return environmentInEnv === environmentInMetadata;
18
+ }
19
+
20
+ export const stripeHandler = async (request, response) => {
21
+ const resolvedContext = request.unchainedContext as Context;
22
+ const { modules } = resolvedContext;
23
+
24
+ let event;
25
+
26
+ try {
27
+ const sig = request.headers['stripe-signature'];
28
+ event = stripe.webhooks.constructEvent(request.body, sig, STRIPE_ENDPOINT_SECRET);
29
+ logger.verbose(`received event`, {
30
+ type: event.type,
31
+ });
32
+ } catch (err) {
33
+ logger.error(`failed event validation with: ${err.message}`);
34
+ response.writeHead(400);
35
+ response.end(err.message);
36
+ return;
37
+ }
38
+
39
+ try {
40
+ if (event.type === 'payment_intent.succeeded') {
41
+ const paymentIntent = event.data.object;
42
+ const { orderPaymentId } = paymentIntent.metadata || {};
43
+
44
+ if (!checkEnvironment(paymentIntent.metadata)) {
45
+ logger.verbose(`event ignored because of environment difference`, {
46
+ type: event.type,
47
+ });
48
+ response.end(JSON.stringify({ received: true, ignored: true }));
49
+ return;
50
+ }
51
+
52
+ logger.verbose(`checkout with orderPaymentId: ${orderPaymentId}`, {
53
+ type: event.type,
54
+ });
55
+
56
+ await modules.orders.payments.logEvent(orderPaymentId, event);
57
+ const orderPayment = await modules.orders.payments.findOrderPayment({
58
+ orderPaymentId,
59
+ });
60
+
61
+ if (!orderPayment) {
62
+ throw new Error(`order payment not found with orderPaymentId: ${orderPaymentId}`);
63
+ }
64
+
65
+ const order = await modules.orders.checkout(
66
+ orderPayment.orderId,
67
+ {
68
+ transactionContext: {
69
+ paymentIntentId: paymentIntent.id,
70
+ },
71
+ paymentContext: {
72
+ paymentIntentId: paymentIntent.id,
73
+ },
74
+ },
75
+ resolvedContext,
76
+ );
77
+
78
+ logger.info(`confirmed checkout for order: ${order._id}`, {
79
+ orderId: order._id,
80
+ type: event.type,
81
+ });
82
+ } else if (event.type === 'setup_intent.succeeded') {
83
+ const setupIntent = event.data.object;
84
+ const { paymentProviderId, userId } = setupIntent.metadata || {};
85
+
86
+ if (!checkEnvironment(setupIntent.metadata)) {
87
+ response.end(JSON.stringify({ received: true, ignored: true }));
88
+ return;
89
+ }
90
+
91
+ logger.verbose(`registered payment credential with paymentProviderId: ${paymentProviderId}`, {
92
+ type: event.type,
93
+ userId,
94
+ });
95
+
96
+ await modules.payment.registerCredentials(
97
+ paymentProviderId,
98
+ {
99
+ transactionContext: {
100
+ setupIntentId: setupIntent.id,
101
+ },
102
+ userId,
103
+ },
104
+ resolvedContext,
105
+ );
106
+
107
+ logger.info(`registered payment credentials with paymentProviderId: ${paymentProviderId}`, {
108
+ userId,
109
+ type: event.type,
110
+ });
111
+ } else {
112
+ logger.verbose(`unhandled type`, {
113
+ type: event.type,
114
+ });
115
+ response.writeHead(404);
116
+ response.end();
117
+ return;
118
+ }
119
+ } catch (err) {
120
+ logger.error(`failed with: ${err.message}`, {
121
+ type: event.type,
122
+ });
123
+ response.writeHead(400);
124
+ response.end(err.message || 'Error');
125
+ return;
126
+ }
127
+ // Return a 200 response to acknowledge receipt of the event
128
+ logger.verbose(`event processed`, {
129
+ type: event.type,
130
+ });
131
+ response.end(JSON.stringify({ received: true }));
132
+ };
@@ -0,0 +1,8 @@
1
+ ## Test Webhooks:
2
+
3
+ ```
4
+ brew install stripe/stripe-cli/stripe
5
+ stripe login --api-key sk_....
6
+ stripe listen --forward-to http://localhost:4010/payment/stripe
7
+ stripe trigger payment_intent.succeeded
8
+ ```
@@ -0,0 +1,53 @@
1
+ import Stripe from 'stripe';
2
+
3
+ const { STRIPE_SECRET, STRIPE_WEBHOOK_ENVIRONMENT, EMAIL_WEBSITE_NAME } = process.env;
4
+
5
+ const stripe = new Stripe(STRIPE_SECRET, {
6
+ apiVersion: '2023-08-16',
7
+ });
8
+
9
+ export default stripe;
10
+
11
+ export const createRegistrationIntent = async (
12
+ { userId, name, email, paymentProviderId },
13
+ options = {},
14
+ ) => {
15
+ const customer = await stripe.customers.create({
16
+ metadata: {
17
+ userId,
18
+ environment: STRIPE_WEBHOOK_ENVIRONMENT ?? null,
19
+ },
20
+ name,
21
+ email,
22
+ });
23
+ const setupIntent = await stripe.setupIntents.create({
24
+ customer: customer.id,
25
+ metadata: {
26
+ userId,
27
+ paymentProviderId,
28
+ environment: STRIPE_WEBHOOK_ENVIRONMENT ?? null,
29
+ },
30
+ ...options,
31
+ });
32
+ return setupIntent;
33
+ };
34
+
35
+ export const createOrderPaymentIntent = async ({ order, orderPayment, pricing }, options = {}) => {
36
+ const reference = EMAIL_WEBSITE_NAME || order._id;
37
+ const { currency, amount } = pricing.total({ useNetPrice: false });
38
+ const paymentIntent = await stripe.paymentIntents.create({
39
+ amount: Math.round(amount),
40
+ currency: currency.toLowerCase(),
41
+ description: `${reference} #${order.orderNumber}`,
42
+ statement_descriptor: order.orderNumber,
43
+ statement_descriptor_suffix: reference,
44
+ receipt_email: order.contact?.emailAddress,
45
+ setup_future_usage: 'off_session', // Verify your integration in this guide by including this parameter
46
+ metadata: {
47
+ orderPaymentId: orderPayment._id,
48
+ environment: STRIPE_WEBHOOK_ENVIRONMENT ?? null,
49
+ },
50
+ ...options,
51
+ });
52
+ return paymentIntent;
53
+ };
@@ -16,7 +16,7 @@ import './payment/worldline-saferpay/index.js';
16
16
  import { datatransHandler } from './payment/datatrans-v2/index.js';
17
17
  import { configureCryptopayModule, cryptopayHandler } from './payment/cryptopay/index.js';
18
18
  import { appleIAPHandler, configureAppleTransactionsModule } from './payment/apple-iap/index.js';
19
- import { stripeHandler } from './payment/stripe.js';
19
+ import { stripeHandler } from './payment/stripe/index.js';
20
20
  import { postfinanceCheckoutHandler } from './payment/postfinance-checkout/index.js';
21
21
 
22
22
  // Warehousing
@@ -1 +0,0 @@
1
- {"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../src/payment/stripe.ts"],"names":[],"mappings":"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"}
@@ -1,307 +0,0 @@
1
- import { Context } from '@unchainedshop/types/api.js';
2
- import { IPaymentAdapter } from '@unchainedshop/types/payments.js';
3
- import { PaymentAdapter, PaymentDirector, PaymentError } from '@unchainedshop/core-payment';
4
- import { createLogger } from '@unchainedshop/logger';
5
-
6
- /*
7
- Test Webhooks:
8
-
9
- brew install stripe/stripe-cli/stripe
10
- stripe login --api-key sk_....
11
- stripe listen --forward-to http://localhost:4010/payment/stripe
12
- stripe trigger payment_intent.succeeded
13
- */
14
-
15
- import createStripeClient from 'stripe';
16
-
17
- const logger = createLogger('unchained:core-payment:stripe');
18
-
19
- const { STRIPE_SECRET, STRIPE_ENDPOINT_SECRET, EMAIL_WEBSITE_NAME } = process.env;
20
-
21
- // eslint-disable-next-line
22
- // @ts-ignore
23
- const stripe = createStripeClient(STRIPE_SECRET);
24
-
25
- export const stripeHandler = async (request, response) => {
26
- const sig = request.headers['stripe-signature'];
27
- const resolvedContext = request.unchainedContext as Context;
28
- const { modules } = resolvedContext;
29
-
30
- let event;
31
-
32
- try {
33
- event = stripe.webhooks.constructEvent(request.body, sig, STRIPE_ENDPOINT_SECRET);
34
- logger.verbose(`Webhook received`, {
35
- type: event.type,
36
- });
37
- } catch (err) {
38
- logger.error(`Webhook failed: ${err.message}`);
39
- response.writeHead(400);
40
- response.end(err.message);
41
- return;
42
- }
43
-
44
- try {
45
- if (event.type === 'payment_intent.succeeded') {
46
- const paymentIntent = event.data.object;
47
- const orderPaymentId = paymentIntent.metadata?.orderPaymentId;
48
-
49
- logger.verbose(`Webhook tries to checkout with orderPaymentId: ${orderPaymentId}`, {
50
- type: event.type,
51
- });
52
-
53
- await modules.orders.payments.logEvent(orderPaymentId, event);
54
- const orderPayment = await modules.orders.payments.findOrderPayment({
55
- orderPaymentId,
56
- });
57
-
58
- if (!orderPayment) {
59
- throw new Error(`Order payment object not found with orderPaymentId: ${orderPaymentId}`);
60
- }
61
-
62
- const order = await modules.orders.checkout(
63
- orderPayment.orderId,
64
- {
65
- transactionContext: {
66
- paymentIntentId: paymentIntent.id,
67
- },
68
- paymentContext: {
69
- paymentIntentId: paymentIntent.id,
70
- },
71
- },
72
- resolvedContext,
73
- );
74
-
75
- logger.info(`Webhook confirmed checkout for order: ${order._id}`, {
76
- orderId: order._id,
77
- type: event.type,
78
- });
79
- } else if (event.type === 'setup_intent.succeeded') {
80
- const setupIntent = event.data.object;
81
- const { paymentProviderId, userId } = setupIntent.metadata;
82
-
83
- logger.verbose(
84
- `Webhook tries to register payment credential with paymentProviderId: ${paymentProviderId}`,
85
- {
86
- type: event.type,
87
- userId,
88
- },
89
- );
90
-
91
- await modules.payment.registerCredentials(
92
- paymentProviderId,
93
- {
94
- transactionContext: {
95
- setupIntentId: setupIntent.id,
96
- },
97
- userId,
98
- },
99
- resolvedContext,
100
- );
101
-
102
- logger.info(
103
- `Webhook registered payment credentials with paymentProviderId: ${paymentProviderId}`,
104
- {
105
- userId,
106
- type: event.type,
107
- },
108
- );
109
- } else {
110
- logger.verbose(`Unhandled webhook type`, {
111
- type: event.type,
112
- });
113
- response.writeHead(404);
114
- response.end();
115
- return;
116
- }
117
- } catch (err) {
118
- logger.error(`Webhook failed: ${err.message}`, {
119
- type: event.type,
120
- });
121
- response.writeHead(400);
122
- response.end(err.message || 'Error');
123
- return;
124
- }
125
- // Return a 200 response to acknowledge receipt of the event
126
- response.end(JSON.stringify({ received: true }));
127
- };
128
-
129
- const createRegistrationIntent = async ({ userId, name, email, paymentProviderId }, options = {}) => {
130
- const customer = await stripe.customers.create({
131
- metadata: {
132
- userId,
133
- },
134
- name,
135
- email,
136
- });
137
- const setupIntent = await stripe.setupIntents.create({
138
- customer: customer.id,
139
- metadata: {
140
- userId,
141
- paymentProviderId,
142
- },
143
- ...options,
144
- });
145
- return setupIntent;
146
- };
147
-
148
- const createOrderPaymentIntent = async ({ order, orderPayment, pricing }, options = {}) => {
149
- const reference = EMAIL_WEBSITE_NAME || order._id;
150
- const { currency, amount } = pricing.total({ useNetPrice: false });
151
- const paymentIntent = await stripe.paymentIntents.create({
152
- amount: Math.round(amount),
153
- currency: currency.toLowerCase(),
154
- description: `${reference} #${order.orderNumber}`,
155
- statement_descriptor: order.orderNumber,
156
- statement_descriptor_suffix: reference,
157
- receipt_email: order.contact?.emailAddress,
158
- setup_future_usage: 'off_session', // Verify your integration in this guide by including this parameter
159
- metadata: {
160
- orderPaymentId: orderPayment._id,
161
- },
162
- ...options,
163
- });
164
- return paymentIntent;
165
- };
166
-
167
- const Stripe: IPaymentAdapter = {
168
- ...PaymentAdapter,
169
-
170
- key: 'shop.unchained.payment.stripe',
171
- label: 'Stripe',
172
- version: '2.0.0',
173
-
174
- typeSupported(type) {
175
- return type === 'GENERIC';
176
- },
177
-
178
- actions: (params) => {
179
- const { modules } = params.context;
180
-
181
- const adapterActions = {
182
- ...PaymentAdapter.actions(params),
183
-
184
- // eslint-disable-next-line
185
- configurationError() {
186
- // eslint-disable-line
187
- if (!STRIPE_SECRET || !STRIPE_ENDPOINT_SECRET) {
188
- return PaymentError.INCOMPLETE_CONFIGURATION;
189
- }
190
- return null;
191
- },
192
-
193
- isActive: () => {
194
- if (adapterActions.configurationError() === null) return true;
195
- return false;
196
- },
197
-
198
- isPayLaterAllowed() {
199
- return false;
200
- },
201
-
202
- validate: async ({ token }) => {
203
- const paymentMethod = await stripe.paymentMethods.retrieve(token);
204
- // TODO: Add further checks like expiration of cards
205
- return !!paymentMethod;
206
- },
207
-
208
- register: async ({ setupIntentId }) => {
209
- if (!setupIntentId) {
210
- throw new Error('You have to provide a setupIntentId');
211
- }
212
-
213
- const setupIntent = await stripe.setupIntents.retrieve(setupIntentId);
214
-
215
- if (setupIntent.status === 'succeeded') {
216
- return {
217
- token: setupIntent.payment_method,
218
- customer: setupIntent.customer,
219
- // payment_method_options: setupIntent.payment_method_options,
220
- payment_method_types: setupIntent.payment_method_types,
221
- usage: setupIntent.usage,
222
- };
223
- }
224
-
225
- logger.warn('Registration declined', setupIntentId);
226
- return null;
227
- },
228
-
229
- sign: async (transactionContext = {}) => {
230
- // eslint-disable-line
231
- const { orderPayment, order, paymentProviderId } = params.paymentContext;
232
-
233
- if (orderPayment) {
234
- const pricing = await modules.orders.pricingSheet(order);
235
- const paymentIntent = await createOrderPaymentIntent(
236
- { order, orderPayment, pricing },
237
- transactionContext,
238
- );
239
- return paymentIntent.client_secret;
240
- }
241
-
242
- const userId = order?.userId || params.paymentContext?.userId;
243
- const user = await modules.users.findUserById(userId);
244
- const email = modules.users.primaryEmail(user)?.address;
245
- const name = user.profile.displayName || user.username || email;
246
-
247
- const paymentIntent = await createRegistrationIntent(
248
- { userId, name, email, paymentProviderId },
249
- transactionContext,
250
- );
251
- return paymentIntent.client_secret;
252
- },
253
-
254
- charge: async ({ paymentIntentId, paymentCredentials }) => {
255
- if (!paymentIntentId && !paymentCredentials) {
256
- throw new Error('You have to provide paymentIntentId or paymentCredentials');
257
- }
258
-
259
- const { order } = params.paymentContext;
260
- const orderPayment = await modules.orders.payments.findOrderPayment({
261
- orderPaymentId: order.paymentId,
262
- });
263
-
264
- const pricing = await modules.orders.pricingSheet(order);
265
-
266
- const paymentIntentObject = paymentIntentId
267
- ? await stripe.paymentIntents.retrieve(paymentIntentId)
268
- : await createOrderPaymentIntent(
269
- { orderPayment, order, pricing },
270
- {
271
- customer: paymentCredentials.meta?.customer,
272
- confirm: true,
273
- payment_method: paymentCredentials.token,
274
- payment_method_types: paymentCredentials.meta?.payment_method_types, // eslint-disable-line
275
- // payment_method_options: paymentCredentials.meta?.payment_method_options, // eslint-disable-line
276
- },
277
- );
278
-
279
- const { currency, amount } = pricing.total({ useNetPrice: false });
280
-
281
- if (
282
- paymentIntentObject.currency !== currency.toLowerCase() ||
283
- paymentIntentObject.amount !== Math.round(amount)
284
- ) {
285
- throw new Error('The price has changed since the intent has been created');
286
- }
287
- if (paymentIntentObject.metadata?.orderPaymentId !== orderPayment?._id) {
288
- throw new Error('The order payment is different from the initiating intent');
289
- }
290
-
291
- if (paymentIntentObject.status === 'succeeded') {
292
- return paymentIntentObject;
293
- }
294
-
295
- logger.verbose('Charge postponed because paymentIntent has wrong status', {
296
- orderPaymentId: paymentIntentObject.id,
297
- });
298
-
299
- return false;
300
- },
301
- };
302
-
303
- return adapterActions;
304
- },
305
- };
306
-
307
- PaymentDirector.registerAdapter(Stripe);