@unchainedshop/plugins 2.5.5 → 2.5.7
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/lib/payment/cryptopay/plugin.js +2 -2
- package/lib/payment/cryptopay/plugin.js.map +1 -1
- package/lib/payment/datatrans-v2/middleware.js +8 -5
- package/lib/payment/datatrans-v2/middleware.js.map +1 -1
- package/lib/payment/stripe/index.d.ts +1 -0
- package/lib/payment/{stripe.js → stripe/index.js} +4 -137
- package/lib/payment/stripe/index.js.map +1 -0
- package/lib/payment/stripe/middleware.js +109 -0
- package/lib/payment/stripe/middleware.js.map +1 -0
- package/lib/payment/stripe/stripe.d.ts +14 -0
- package/lib/payment/stripe/stripe.js +46 -0
- package/lib/payment/stripe/stripe.js.map +1 -0
- package/lib/plugins-index.js +1 -1
- package/lib/plugins-index.js.map +1 -1
- package/lib/worker/email.js +20 -26
- package/lib/worker/email.js.map +1 -1
- package/package.json +23 -23
- package/src/payment/cryptopay/plugin.ts +2 -2
- package/src/payment/datatrans-v2/middleware.ts +9 -5
- package/src/payment/stripe/index.ts +145 -0
- package/src/payment/stripe/middleware.ts +132 -0
- package/src/payment/stripe/readme.md +8 -0
- package/src/payment/stripe/stripe.ts +53 -0
- package/src/plugins-index.ts +1 -1
- package/src/worker/email.ts +21 -25
- package/lib/payment/stripe.js.map +0 -1
- package/src/payment/stripe.ts +0 -307
- /package/lib/payment/{stripe.d.ts → stripe/middleware.d.ts} +0 -0
package/src/payment/stripe.ts
DELETED
|
@@ -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);
|
|
File without changes
|