@unchainedshop/plugins 4.8.4 → 5.0.0-alpha.1
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/README.md +0 -2
- package/lib/events/node-event-emitter.d.ts +3 -1
- package/lib/events/node-event-emitter.js +3 -3
- package/lib/files/gridfs/adapter.d.ts +2 -2
- package/lib/files/gridfs/adapter.js +2 -5
- package/lib/files/gridfs/index.d.ts +5 -4
- package/lib/files/gridfs/index.js +30 -3
- package/lib/files/minio/api.js +1 -2
- package/lib/files/minio/index.d.ts +4 -5
- package/lib/files/minio/index.js +18 -157
- package/lib/files/temp-upload/api.d.ts +2 -0
- package/lib/files/temp-upload/api.js +1 -2
- package/lib/payment/apple-iap/adapter.d.ts +2 -1
- package/lib/payment/apple-iap/adapter.js +2 -3
- package/lib/payment/apple-iap/api.js +1 -1
- package/lib/payment/apple-iap/index.d.ts +6 -4
- package/lib/payment/apple-iap/index.js +28 -3
- package/lib/payment/cryptopay/api.js +1 -1
- package/lib/payment/cryptopay/handle-webhook.js +2 -1
- package/lib/payment/cryptopay/index.d.ts +5 -4
- package/lib/payment/cryptopay/index.js +31 -3
- package/lib/payment/datatrans-v2/adapter.js +11 -3
- package/lib/payment/datatrans-v2/api/authorize.js +1 -1
- package/lib/payment/datatrans-v2/api/authorizeAuthenticated.js +1 -1
- package/lib/payment/datatrans-v2/api/cancel.js +1 -1
- package/lib/payment/datatrans-v2/api/init.js +1 -1
- package/lib/payment/datatrans-v2/api/secureFields.js +1 -1
- package/lib/payment/datatrans-v2/api/settle.js +1 -1
- package/lib/payment/datatrans-v2/api/status.js +1 -1
- package/lib/payment/datatrans-v2/api/validate.js +1 -1
- package/lib/payment/datatrans-v2/index.d.ts +4 -1
- package/lib/payment/datatrans-v2/index.js +17 -411
- package/lib/payment/payrexx/api/index.js +2 -2
- package/lib/payment/payrexx/api.js +1 -1
- package/lib/payment/payrexx/index.d.ts +4 -1
- package/lib/payment/payrexx/index.js +19 -158
- package/lib/payment/postfinance-checkout/index.d.ts +5 -0
- package/lib/payment/postfinance-checkout/index.js +25 -186
- package/lib/payment/saferpay/adapter.js +1 -2
- package/lib/payment/saferpay/index.d.ts +5 -4
- package/lib/payment/saferpay/index.js +29 -3
- package/lib/payment/stripe/index.d.ts +4 -1
- package/lib/payment/stripe/index.js +22 -116
- package/lib/payment/stripe/stripe.js +1 -4
- package/lib/presets/all.d.ts +1 -70
- package/lib/presets/all.js +41 -27
- package/lib/presets/base.d.ts +1 -25
- package/lib/presets/base.js +56 -25
- package/lib/presets/countries/ch.d.ts +1 -2
- package/lib/presets/countries/ch.js +7 -2
- package/lib/presets/crypto.d.ts +3 -7
- package/lib/presets/crypto.js +18 -7
- package/lib/worker/bulk-import/api.d.ts +2 -0
- package/lib/worker/bulk-import/api.js +1 -2
- package/lib/worker/bulkgate/adapter.js +1 -1
- package/lib/worker/twilio/adapter.js +1 -1
- package/lib/worker/update-coinbase-rates/adapter.js +3 -5
- package/package.json +25 -30
|
@@ -1,418 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const logger = createLogger('unchained:datatrans');
|
|
7
|
-
const { DATATRANS_SECRET, DATATRANS_SIGN_KEY, DATATRANS_API_ENDPOINT = 'https://api.sandbox.datatrans.com', DATATRANS_MERCHANT_ID, } = process.env;
|
|
8
|
-
const newDatatransError = ({ code, message }) => {
|
|
9
|
-
const error = new Error(message);
|
|
10
|
-
error.name = `DATATRANS_${code}`;
|
|
11
|
-
return error;
|
|
12
|
-
};
|
|
13
|
-
const throwIfResponseError = (result) => {
|
|
14
|
-
if (result.error) {
|
|
15
|
-
const rawError = result.error;
|
|
16
|
-
throw newDatatransError(rawError);
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
const getMarketplaceSplits = async ({ order, orderPayment, config, }) => {
|
|
20
|
-
const pricingForOrderPayment = PaymentPricingSheet({
|
|
21
|
-
calculation: orderPayment.calculation,
|
|
22
|
-
currencyCode: order.currencyCode,
|
|
23
|
-
});
|
|
24
|
-
const pricing = OrderPricingSheet({
|
|
25
|
-
calculation: order.calculation,
|
|
26
|
-
currencyCode: order.currencyCode,
|
|
27
|
-
});
|
|
28
|
-
const { amount: total } = pricing.total({ useNetPrice: false });
|
|
29
|
-
const roundedTotal = Math.round(total);
|
|
30
|
-
const splits = await Promise.all(config
|
|
31
|
-
.filter((item) => item.key === 'marketplaceSplit')
|
|
32
|
-
.map((item) => {
|
|
33
|
-
const [subMerchantId, staticDiscountId, sharePercentage] = (item.value || '')
|
|
34
|
-
.split(';')
|
|
35
|
-
.map((f) => f.trim());
|
|
36
|
-
const { amount: discountSum } = pricingForOrderPayment.total({
|
|
37
|
-
category: PaymentPricingRowCategory.Discount,
|
|
38
|
-
discountId: staticDiscountId,
|
|
39
|
-
});
|
|
40
|
-
const shareFactor = sharePercentage ? parseInt(sharePercentage, 10) / 100 : 1;
|
|
41
|
-
const amount = Math.round(total * shareFactor);
|
|
42
|
-
const commission = Math.round(discountSum * -1 * shareFactor);
|
|
43
|
-
return {
|
|
44
|
-
subMerchantId,
|
|
45
|
-
amount,
|
|
46
|
-
commission,
|
|
47
|
-
};
|
|
48
|
-
}));
|
|
49
|
-
if (splits.length > 0) {
|
|
50
|
-
const currentSum = splits.reduce((sum, split) => sum + split.amount, 0);
|
|
51
|
-
const difference = roundedTotal - currentSum;
|
|
52
|
-
if (difference !== 0) {
|
|
53
|
-
splits[splits.length - 1].amount += difference;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return splits;
|
|
57
|
-
};
|
|
58
|
-
const Datatrans = {
|
|
59
|
-
...PaymentAdapter,
|
|
1
|
+
import { Datatrans } from "./adapter.js";
|
|
2
|
+
import { datatransWebhookHandler } from "./api.js";
|
|
3
|
+
const { DATATRANS_WEBHOOK_PATH = '/payment/datatrans/webhook' } = process.env;
|
|
4
|
+
const { DATATRANS_SIGN_KEY, DATATRANS_SIGN2_KEY } = process.env;
|
|
5
|
+
export const DatatransPlugin = {
|
|
60
6
|
key: 'shop.unchained.datatrans',
|
|
61
|
-
label: 'Datatrans',
|
|
7
|
+
label: 'Datatrans Payment Plugin',
|
|
62
8
|
version: '2.0.0',
|
|
63
|
-
|
|
9
|
+
adapters: [Datatrans],
|
|
10
|
+
routes: [
|
|
64
11
|
{
|
|
65
|
-
|
|
66
|
-
|
|
12
|
+
path: DATATRANS_WEBHOOK_PATH,
|
|
13
|
+
method: 'POST',
|
|
14
|
+
handler: datatransWebhookHandler,
|
|
67
15
|
},
|
|
68
16
|
],
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const getMerchantId = () => {
|
|
74
|
-
return config.find((item) => item.key === 'merchantId')?.value || DATATRANS_MERCHANT_ID;
|
|
75
|
-
};
|
|
76
|
-
const api = () => {
|
|
77
|
-
const merchantId = getMerchantId();
|
|
78
|
-
if (!DATATRANS_SECRET)
|
|
79
|
-
throw new Error('Credential DATATRANS_SECRET not Set');
|
|
80
|
-
if (!merchantId)
|
|
81
|
-
throw new Error('Credential Merchant ID not configured');
|
|
82
|
-
if (!DATATRANS_API_ENDPOINT)
|
|
83
|
-
throw new Error('Credential DATATRANS_API_ENDPOINT not Set');
|
|
84
|
-
return createDatatransAPI(DATATRANS_API_ENDPOINT, merchantId, DATATRANS_SECRET);
|
|
85
|
-
};
|
|
86
|
-
const shouldSettleInUnchained = () => {
|
|
87
|
-
return config.reduce((current, item) => {
|
|
88
|
-
if (item.key === 'settleInUnchained')
|
|
89
|
-
return Boolean(item.value);
|
|
90
|
-
return current;
|
|
91
|
-
}, true);
|
|
92
|
-
};
|
|
93
|
-
const authorize = async ({ paymentCredentials, ...arbitraryFields }) => {
|
|
94
|
-
const { order, orderPayment } = context;
|
|
95
|
-
if (!orderPayment)
|
|
96
|
-
throw new Error('Order Payment missing in context');
|
|
97
|
-
if (!order)
|
|
98
|
-
throw new Error('Order missing in context');
|
|
99
|
-
const refno = Buffer.from(orderPayment._id, 'hex').toString('base64');
|
|
100
|
-
const userId = order?.userId || context?.userId;
|
|
101
|
-
const refno2 = userId;
|
|
102
|
-
const { currencyCode, amount } = roundedAmountFromOrder(order);
|
|
103
|
-
const splits = await getMarketplaceSplits({
|
|
104
|
-
orderPayment,
|
|
105
|
-
order,
|
|
106
|
-
config,
|
|
107
|
-
});
|
|
108
|
-
const result = await api().authorize({
|
|
109
|
-
...arbitraryFields,
|
|
110
|
-
amount,
|
|
111
|
-
currency: currencyCode,
|
|
112
|
-
refno,
|
|
113
|
-
refno2,
|
|
114
|
-
autoSettle: false,
|
|
115
|
-
customer: {
|
|
116
|
-
id: userId,
|
|
117
|
-
},
|
|
118
|
-
marketplace: splits.length
|
|
119
|
-
? {
|
|
120
|
-
splits,
|
|
121
|
-
}
|
|
122
|
-
: undefined,
|
|
123
|
-
[paymentCredentials.meta.objectKey]: JSON.parse(paymentCredentials.token),
|
|
124
|
-
});
|
|
125
|
-
throwIfResponseError(result);
|
|
126
|
-
return result.transactionId;
|
|
127
|
-
};
|
|
128
|
-
const authorizeAuth = async ({ transactionId, refno, refno2, ...arbitraryFields }) => {
|
|
129
|
-
const { order } = context;
|
|
130
|
-
if (!order)
|
|
131
|
-
throw new Error('Order missing in context');
|
|
132
|
-
const { currencyCode, amount } = roundedAmountFromOrder(order);
|
|
133
|
-
const result = await api().authorizeAuthenticated({
|
|
134
|
-
...arbitraryFields,
|
|
135
|
-
transactionId,
|
|
136
|
-
amount,
|
|
137
|
-
currency: currencyCode,
|
|
138
|
-
refno,
|
|
139
|
-
refno2,
|
|
140
|
-
autoSettle: false,
|
|
141
|
-
});
|
|
142
|
-
throwIfResponseError(result);
|
|
143
|
-
return result.acquirerAuthorizationCode;
|
|
144
|
-
};
|
|
145
|
-
const isTransactionAmountValid = (transaction) => {
|
|
146
|
-
const { order } = context;
|
|
147
|
-
if (!order)
|
|
148
|
-
throw new Error('Order missing in context');
|
|
149
|
-
const { currencyCode, amount } = roundedAmountFromOrder(order);
|
|
150
|
-
if (transaction.currency !== currencyCode ||
|
|
151
|
-
transaction.detail.authorize?.amount !== amount) {
|
|
152
|
-
logger.info(`currency: ${transaction.currency} === ${currencyCode} => ${transaction.currency === currencyCode}, amount: ${transaction.detail.authorize?.amount} === ${amount} => ${transaction.detail.authorize?.amount === amount}`);
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
return true;
|
|
156
|
-
};
|
|
157
|
-
const checkIfTransactionAmountValid = (transactionId, transaction) => {
|
|
158
|
-
if (!isTransactionAmountValid(transaction)) {
|
|
159
|
-
logger.error(`Transaction declined / Transaction ID ${transactionId} because of amount/currency mismatch`);
|
|
160
|
-
throw newDatatransError({
|
|
161
|
-
code: `YOU_HAVE_TO_PAY_THE_FULL_AMOUNT_DUDE`,
|
|
162
|
-
message: 'Amount / Currency Mismatch with Cart',
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
const settle = async ({ transactionId, refno, refno2, extensions }) => {
|
|
167
|
-
const { order, orderPayment } = context;
|
|
168
|
-
if (!orderPayment)
|
|
169
|
-
throw new Error('Order Payment missing in context');
|
|
170
|
-
if (!order)
|
|
171
|
-
throw new Error('Order missing in context');
|
|
172
|
-
const { currencyCode, amount } = roundedAmountFromOrder(order);
|
|
173
|
-
const splits = await getMarketplaceSplits({
|
|
174
|
-
orderPayment,
|
|
175
|
-
order,
|
|
176
|
-
config,
|
|
177
|
-
});
|
|
178
|
-
const result = await api().settle({
|
|
179
|
-
transactionId,
|
|
180
|
-
amount,
|
|
181
|
-
refno,
|
|
182
|
-
refno2,
|
|
183
|
-
currency: currencyCode,
|
|
184
|
-
marketplace: splits.length
|
|
185
|
-
? {
|
|
186
|
-
splits,
|
|
187
|
-
}
|
|
188
|
-
: undefined,
|
|
189
|
-
extensions,
|
|
190
|
-
});
|
|
191
|
-
throwIfResponseError(result);
|
|
192
|
-
return result;
|
|
193
|
-
};
|
|
194
|
-
const cancel = async ({ transactionId, refno }) => {
|
|
195
|
-
const result = await api().cancel({
|
|
196
|
-
transactionId,
|
|
197
|
-
refno,
|
|
198
|
-
});
|
|
199
|
-
throwIfResponseError(result);
|
|
200
|
-
return result;
|
|
201
|
-
};
|
|
202
|
-
const adapterActions = {
|
|
203
|
-
...PaymentAdapter.actions(config, context),
|
|
204
|
-
configurationError() {
|
|
205
|
-
if (!getMerchantId() || !DATATRANS_SECRET || !DATATRANS_SIGN_KEY) {
|
|
206
|
-
return PaymentError.INCOMPLETE_CONFIGURATION;
|
|
207
|
-
}
|
|
208
|
-
return null;
|
|
209
|
-
},
|
|
210
|
-
isActive() {
|
|
211
|
-
if (adapterActions.configurationError() === null)
|
|
212
|
-
return true;
|
|
213
|
-
return false;
|
|
214
|
-
},
|
|
215
|
-
isPayLaterAllowed() {
|
|
216
|
-
return false;
|
|
217
|
-
},
|
|
218
|
-
async sign(transactionContext = {}) {
|
|
219
|
-
const { useSecureFields = false, ...arbitraryFields } = transactionContext || {};
|
|
220
|
-
const { orderPayment, paymentProvider, order } = context;
|
|
221
|
-
const refno = Buffer.from(orderPayment ? orderPayment._id : paymentProvider._id, 'hex').toString('base64');
|
|
222
|
-
const userId = order?.userId || context?.userId;
|
|
223
|
-
const refno2 = userId;
|
|
224
|
-
const price = order
|
|
225
|
-
? roundedAmountFromOrder(order)
|
|
226
|
-
: {};
|
|
227
|
-
if (useSecureFields) {
|
|
228
|
-
const result = await api().secureFields({
|
|
229
|
-
...arbitraryFields,
|
|
230
|
-
currency: price.currencyCode || 'CHF',
|
|
231
|
-
refno,
|
|
232
|
-
refno2,
|
|
233
|
-
customer: {
|
|
234
|
-
id: userId,
|
|
235
|
-
},
|
|
236
|
-
amount: price.amount,
|
|
237
|
-
});
|
|
238
|
-
throwIfResponseError(result);
|
|
239
|
-
return JSON.stringify(result);
|
|
240
|
-
}
|
|
241
|
-
const result = await api().init({
|
|
242
|
-
...arbitraryFields,
|
|
243
|
-
currency: price.currencyCode || 'CHF',
|
|
244
|
-
refno,
|
|
245
|
-
refno2,
|
|
246
|
-
customer: {
|
|
247
|
-
id: userId,
|
|
248
|
-
},
|
|
249
|
-
amount: price.amount,
|
|
250
|
-
});
|
|
251
|
-
throwIfResponseError(result);
|
|
252
|
-
return JSON.stringify(result);
|
|
253
|
-
},
|
|
254
|
-
async validate(credentials) {
|
|
255
|
-
if (!credentials.meta)
|
|
256
|
-
return false;
|
|
257
|
-
const { objectKey, currency } = credentials.meta;
|
|
258
|
-
const result = await api().validate({
|
|
259
|
-
refno: Buffer.from(`valid-${new Date().getTime()}`, 'hex').toString('base64'),
|
|
260
|
-
currency,
|
|
261
|
-
[objectKey]: JSON.parse(credentials.token),
|
|
262
|
-
});
|
|
263
|
-
return Boolean(result?.transactionId);
|
|
264
|
-
},
|
|
265
|
-
async register(transactionResponse) {
|
|
266
|
-
const { transactionId } = transactionResponse;
|
|
267
|
-
const result = (await api().status({
|
|
268
|
-
transactionId,
|
|
269
|
-
}));
|
|
270
|
-
if (result.transactionId) {
|
|
271
|
-
return parseRegistrationData(result);
|
|
272
|
-
}
|
|
273
|
-
return null;
|
|
274
|
-
},
|
|
275
|
-
async confirm() {
|
|
276
|
-
if (!shouldSettleInUnchained())
|
|
277
|
-
return false;
|
|
278
|
-
const { orderPayment, transactionContext } = context;
|
|
279
|
-
if (!orderPayment)
|
|
280
|
-
throw new Error('Order Payment missing in context');
|
|
281
|
-
const { transactionId } = orderPayment;
|
|
282
|
-
const { extensions } = transactionContext || {};
|
|
283
|
-
if (!transactionId) {
|
|
284
|
-
return false;
|
|
285
|
-
}
|
|
286
|
-
const transaction = (await api().status({
|
|
287
|
-
transactionId,
|
|
288
|
-
}));
|
|
289
|
-
throwIfResponseError(transaction);
|
|
290
|
-
const { status } = transaction;
|
|
291
|
-
if (status === 'authorized') {
|
|
292
|
-
await settle({
|
|
293
|
-
transactionId,
|
|
294
|
-
refno: transaction.refno,
|
|
295
|
-
refno2: transaction.refno2,
|
|
296
|
-
extensions,
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
return true;
|
|
300
|
-
},
|
|
301
|
-
async cancel() {
|
|
302
|
-
if (!shouldSettleInUnchained())
|
|
303
|
-
return false;
|
|
304
|
-
const { orderPayment } = context;
|
|
305
|
-
if (!orderPayment)
|
|
306
|
-
throw new Error('Order Payment missing in context');
|
|
307
|
-
const { transactionId } = orderPayment;
|
|
308
|
-
if (!transactionId) {
|
|
309
|
-
return false;
|
|
310
|
-
}
|
|
311
|
-
const transaction = (await api().status({
|
|
312
|
-
transactionId,
|
|
313
|
-
}));
|
|
314
|
-
throwIfResponseError(transaction);
|
|
315
|
-
const { status } = transaction;
|
|
316
|
-
if (status === 'authorized') {
|
|
317
|
-
await cancel({
|
|
318
|
-
transactionId,
|
|
319
|
-
refno: transaction.refno,
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
return true;
|
|
323
|
-
},
|
|
324
|
-
async charge({ transactionId: rawTransactionId, paymentCredentials, authorizeAuthenticated, ...arbitraryFields }) {
|
|
325
|
-
if (!rawTransactionId && !paymentCredentials) {
|
|
326
|
-
logger.warn('Not trying to charge because of missing payment authorization response, return false to provide later');
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
const transactionId = rawTransactionId ||
|
|
330
|
-
(await authorize({
|
|
331
|
-
...arbitraryFields,
|
|
332
|
-
paymentCredentials,
|
|
333
|
-
}));
|
|
334
|
-
const transaction = (await api().status({
|
|
335
|
-
transactionId,
|
|
336
|
-
}));
|
|
337
|
-
throwIfResponseError(transaction);
|
|
338
|
-
if (!transaction) {
|
|
339
|
-
throw newDatatransError({
|
|
340
|
-
code: `TRANSACTION_NOT_FOUND`,
|
|
341
|
-
message: 'Amount / Currency Mismatch with Cart',
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
let { status } = transaction;
|
|
345
|
-
if (status === 'canceled' || status === 'failed') {
|
|
346
|
-
logger.error(`Payment declined or canceled with Transaction ID ${transactionId} and status ${status}`);
|
|
347
|
-
throw newDatatransError({
|
|
348
|
-
code: `STATUS_${status.toUpperCase()}`,
|
|
349
|
-
message: 'Payment declined or canceled',
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
if (status === 'authenticated') {
|
|
353
|
-
if (authorizeAuthenticated) {
|
|
354
|
-
checkIfTransactionAmountValid(transactionId, transaction);
|
|
355
|
-
await authorizeAuth({
|
|
356
|
-
...(authorizeAuthenticated || {}),
|
|
357
|
-
transactionId,
|
|
358
|
-
refno: transaction.refno,
|
|
359
|
-
refno2: transaction.refno2,
|
|
360
|
-
});
|
|
361
|
-
status = 'authorized';
|
|
362
|
-
}
|
|
363
|
-
else {
|
|
364
|
-
logger.error(`Transaction declined / Transaction ID ${transactionId} not authorized yet`);
|
|
365
|
-
throw newDatatransError({
|
|
366
|
-
code: `STATUS_${status.toUpperCase()}`,
|
|
367
|
-
message: 'Payment authenticated but not authorized',
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
if (status === 'authorized' || status === 'settled') {
|
|
372
|
-
let settledTransaction = transaction;
|
|
373
|
-
let credentials;
|
|
374
|
-
try {
|
|
375
|
-
checkIfTransactionAmountValid(transactionId, settledTransaction);
|
|
376
|
-
credentials = await parseRegistrationData(settledTransaction);
|
|
377
|
-
const result = await api().status({
|
|
378
|
-
transactionId,
|
|
379
|
-
});
|
|
380
|
-
if (result?.error) {
|
|
381
|
-
settledTransaction = transaction;
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
settledTransaction = result;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
catch {
|
|
388
|
-
await cancel({ transactionId, refno: settledTransaction.refno });
|
|
389
|
-
logger.error(`Transaction declined / Transaction ID ${transactionId} authorization cancelled`);
|
|
390
|
-
throw newDatatransError({
|
|
391
|
-
code: `STATUS_${status.toUpperCase()}`,
|
|
392
|
-
message: 'Payment cancelled server-side because of amount missmatch',
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
return {
|
|
396
|
-
transactionId,
|
|
397
|
-
settledTransaction,
|
|
398
|
-
arbitraryFields,
|
|
399
|
-
credentials,
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
if (status === 'initialized' ||
|
|
403
|
-
status === 'challenge_required' ||
|
|
404
|
-
status === 'challenge_ongoing' ||
|
|
405
|
-
status === 'transmitted') {
|
|
406
|
-
logger.error(`Transaction ID ${transactionId} in transit with status ${status}`);
|
|
407
|
-
throw newDatatransError({
|
|
408
|
-
code: `STATUS_${status.toUpperCase()}`,
|
|
409
|
-
message: 'Transaction status invalid for checkout',
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
return false;
|
|
413
|
-
},
|
|
414
|
-
};
|
|
415
|
-
return adapterActions;
|
|
17
|
+
onRegister: () => {
|
|
18
|
+
if (!DATATRANS_SIGN_KEY && !DATATRANS_SIGN2_KEY) {
|
|
19
|
+
throw new Error('DATATRANS_SIGN_KEY not set');
|
|
20
|
+
}
|
|
416
21
|
},
|
|
417
22
|
};
|
|
418
|
-
|
|
23
|
+
export default DatatransPlugin;
|
|
24
|
+
export { Datatrans } from "./adapter.js";
|
|
@@ -35,7 +35,7 @@ const createPayrexxAPI = (instance, secret) => {
|
|
|
35
35
|
const result = await fetchPayrexx(`Gateway/${id}`, 'GET');
|
|
36
36
|
if (!result.ok)
|
|
37
37
|
throw new Error(await result.text());
|
|
38
|
-
const { status, data } = await result.json();
|
|
38
|
+
const { status, data } = (await result.json());
|
|
39
39
|
if (status !== 'success')
|
|
40
40
|
return null;
|
|
41
41
|
return data?.[0];
|
|
@@ -43,7 +43,7 @@ const createPayrexxAPI = (instance, secret) => {
|
|
|
43
43
|
async createGateway(params) {
|
|
44
44
|
const result = await fetchPayrexx('Gateway', 'POST', params);
|
|
45
45
|
if (result.ok) {
|
|
46
|
-
return result.json();
|
|
46
|
+
return (await result.json());
|
|
47
47
|
}
|
|
48
48
|
throw new Error(await result.text());
|
|
49
49
|
},
|
|
@@ -3,7 +3,7 @@ const logger = createLogger('unchained:payrexx');
|
|
|
3
3
|
export async function payrexxWebhookHandler(request, context) {
|
|
4
4
|
try {
|
|
5
5
|
const { modules, services } = context;
|
|
6
|
-
const body = await request.json();
|
|
6
|
+
const body = (await request.json());
|
|
7
7
|
const { transaction } = body;
|
|
8
8
|
if (!transaction) {
|
|
9
9
|
logger.info('Unhandled event type', {
|
|
@@ -1,162 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const logger = createLogger('unchained:payrexx');
|
|
6
|
-
const Payrexx = {
|
|
7
|
-
...PaymentAdapter,
|
|
1
|
+
import { Payrexx } from "./adapter.js";
|
|
2
|
+
import { payrexxWebhookHandler } from "./api.js";
|
|
3
|
+
const { PAYREXX_WEBHOOK_PATH = '/payment/payrexx' } = process.env;
|
|
4
|
+
export const PayrexxPlugin = {
|
|
8
5
|
key: 'shop.unchained.payment.payrexx',
|
|
9
|
-
label: 'Payrexx',
|
|
6
|
+
label: 'Payrexx Payment Plugin',
|
|
10
7
|
version: '1.0.0',
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (!instance) {
|
|
24
|
-
return PaymentError.INCOMPLETE_CONFIGURATION;
|
|
25
|
-
}
|
|
26
|
-
return null;
|
|
27
|
-
},
|
|
28
|
-
isActive: () => {
|
|
29
|
-
if (adapterActions.configurationError() === null)
|
|
30
|
-
return true;
|
|
31
|
-
return false;
|
|
32
|
-
},
|
|
33
|
-
isPayLaterAllowed() {
|
|
34
|
-
return false;
|
|
35
|
-
},
|
|
36
|
-
sign: async (transactionContext = {}) => {
|
|
37
|
-
const { orderPayment, userId, order } = context;
|
|
38
|
-
if (!order)
|
|
39
|
-
throw new Error('No order in context, can only sign with order context');
|
|
40
|
-
const api = createPayrexxAPI(instance, process.env.PAYREXX_SECRET);
|
|
41
|
-
if (orderPayment) {
|
|
42
|
-
const pricing = OrderPricingSheet({
|
|
43
|
-
calculation: order.calculation,
|
|
44
|
-
currencyCode: order.currencyCode,
|
|
45
|
-
});
|
|
46
|
-
const gatewayObject = mapOrderDataToGatewayObject({ order, orderPayment, pricing }, transactionContext);
|
|
47
|
-
const gateway = await api.createGateway(gatewayObject);
|
|
48
|
-
return JSON.stringify(gateway);
|
|
49
|
-
}
|
|
50
|
-
const user = userId && (await modules.users.findUserById(userId));
|
|
51
|
-
if (!user)
|
|
52
|
-
throw new Error('User not found, can only sign registrations with user context');
|
|
53
|
-
const emailAddress = modules.users.primaryEmail(user)?.address;
|
|
54
|
-
const gateway = await api.createGateway(mapUserToGatewayObject({
|
|
55
|
-
userId,
|
|
56
|
-
emailAddress,
|
|
57
|
-
currencyCode: order?.currencyCode,
|
|
58
|
-
}));
|
|
59
|
-
return JSON.stringify(gateway);
|
|
60
|
-
},
|
|
61
|
-
async register() {
|
|
62
|
-
throw new Error('Not implemented');
|
|
63
|
-
return null;
|
|
64
|
-
},
|
|
65
|
-
async confirm() {
|
|
66
|
-
const { orderPayment } = context;
|
|
67
|
-
if (!orderPayment?.transactionId) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
const { transactionId } = orderPayment;
|
|
71
|
-
const api = createPayrexxAPI(instance, process.env.PAYREXX_SECRET);
|
|
72
|
-
const gatewayObject = await api.getGateway(transactionId);
|
|
73
|
-
if (!gatewayObject) {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
if (gatewayObject.status === GatewayObjectStatus.reserved) {
|
|
77
|
-
const allTransactions = gatewayObject.invoices?.flatMap((invoice) => invoice.transactions);
|
|
78
|
-
await Promise.all(allTransactions.map(async (transaction) => api.chargePreAuthorized(transaction.id, { referenceId: '__IGNORE_WEBHOOK__' })));
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
if (gatewayObject.status === GatewayObjectStatus.confirmed) {
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
return false;
|
|
85
|
-
},
|
|
86
|
-
async cancel() {
|
|
87
|
-
const { orderPayment } = context;
|
|
88
|
-
if (!orderPayment?.transactionId) {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
const { transactionId } = orderPayment;
|
|
92
|
-
const api = createPayrexxAPI(instance, process.env.PAYREXX_SECRET);
|
|
93
|
-
const gatewayObject = await api.getGateway(transactionId);
|
|
94
|
-
if (!gatewayObject) {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
if (gatewayObject.status === GatewayObjectStatus.reserved) {
|
|
98
|
-
const allTransactions = gatewayObject.invoices?.flatMap((invoice) => invoice.transactions);
|
|
99
|
-
await Promise.all(allTransactions.map(async (transaction) => api.deleteReservation(transaction.id)));
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
if (gatewayObject.status === GatewayObjectStatus.confirmed) {
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
return true;
|
|
106
|
-
},
|
|
107
|
-
charge: async ({ gatewayId, paymentCredentials }) => {
|
|
108
|
-
if (!gatewayId && !paymentCredentials) {
|
|
109
|
-
throw new Error('You have to provide gatewayId or paymentCredentials');
|
|
110
|
-
}
|
|
111
|
-
const { order, orderPayment } = context;
|
|
112
|
-
if (!order)
|
|
113
|
-
throw new Error('No order in context, can only charge with order context');
|
|
114
|
-
if (!orderPayment)
|
|
115
|
-
throw new Error('Order payment not found');
|
|
116
|
-
const api = createPayrexxAPI(instance, process.env.PAYREXX_SECRET);
|
|
117
|
-
const gatewayObject = await api.getGateway(gatewayId);
|
|
118
|
-
if (!gatewayObject) {
|
|
119
|
-
throw new Error('Could not load gateway from the Payrexx API');
|
|
120
|
-
}
|
|
121
|
-
const pricing = OrderPricingSheet({
|
|
122
|
-
calculation: order.calculation,
|
|
123
|
-
currencyCode: order.currencyCode,
|
|
124
|
-
});
|
|
125
|
-
const { currencyCode, amount } = pricing.total({ useNetPrice: false });
|
|
126
|
-
if (gatewayObject.status === 'reserved' ||
|
|
127
|
-
gatewayObject.status === 'confirmed') {
|
|
128
|
-
try {
|
|
129
|
-
if (gatewayObject.currency !== currencyCode.toUpperCase() ||
|
|
130
|
-
gatewayObject.amount !== Math.round(amount)) {
|
|
131
|
-
throw new Error('The price has changed since the intent has been created');
|
|
132
|
-
}
|
|
133
|
-
if (gatewayObject.referenceId !== orderPayment?._id) {
|
|
134
|
-
throw new Error('The order payment is different from the initiating intent');
|
|
135
|
-
}
|
|
136
|
-
logger.info(`Mark as charged, status is ${gatewayObject.status}`, {
|
|
137
|
-
orderPaymentId: gatewayObject.referenceId,
|
|
138
|
-
});
|
|
139
|
-
return {
|
|
140
|
-
transactionId: gatewayId,
|
|
141
|
-
gatewayObject,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
catch (e) {
|
|
145
|
-
if (gatewayObject.status === GatewayObjectStatus.reserved) {
|
|
146
|
-
const allTransactions = gatewayObject.invoices?.flatMap((invoice) => invoice.transactions);
|
|
147
|
-
await Promise.all(allTransactions.map(async (transaction) => api.deleteReservation(transaction.id)));
|
|
148
|
-
}
|
|
149
|
-
throw e;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
logger.info('Charge not possible', {
|
|
153
|
-
orderPaymentId: gatewayObject.referenceId,
|
|
154
|
-
status: gatewayObject.status,
|
|
155
|
-
});
|
|
156
|
-
throw new Error(`Gateway Status ${gatewayObject.status} does not allow checkout`);
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
return adapterActions;
|
|
8
|
+
adapters: [Payrexx],
|
|
9
|
+
routes: [
|
|
10
|
+
{
|
|
11
|
+
path: PAYREXX_WEBHOOK_PATH,
|
|
12
|
+
method: 'POST',
|
|
13
|
+
handler: payrexxWebhookHandler,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
onRegister: () => {
|
|
17
|
+
if (!process.env.PAYREXX_SECRET) {
|
|
18
|
+
throw new Error('PAYREXX_SECRET not set');
|
|
19
|
+
}
|
|
160
20
|
},
|
|
161
21
|
};
|
|
162
|
-
|
|
22
|
+
export default PayrexxPlugin;
|
|
23
|
+
export { Payrexx } from "./adapter.js";
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
import type { IPlugin } from '@unchainedshop/core';
|
|
2
|
+
export declare const PostfinanceCheckoutPlugin: IPlugin;
|
|
3
|
+
export default PostfinanceCheckoutPlugin;
|
|
4
|
+
export { PostfinanceCheckout } from './adapter.ts';
|
|
1
5
|
export * from './types.ts';
|
|
2
6
|
export * from './api-types.ts';
|
|
7
|
+
export * from './utils.ts';
|