@unchainedshop/plugins 5.0.0-alpha.1 → 5.0.0-alpha.3

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 (106) hide show
  1. package/lib/events/aws-eventbridge.d.ts +7 -1
  2. package/lib/events/aws-eventbridge.js +5 -11
  3. package/lib/events/redis.d.ts +3 -1
  4. package/lib/events/redis.js +5 -5
  5. package/lib/files/gridfs/api.js +1 -1
  6. package/lib/files/gridfs/handler-express.js +1 -1
  7. package/lib/filters/local-search/index.js +0 -6
  8. package/lib/filters/local-search.js +1 -9
  9. package/lib/payment/cryptopay/db/CryptopayTransactions.d.ts +1 -1
  10. package/lib/payment/cryptopay/db/CryptopayTransactions.js +8 -3
  11. package/lib/payment/cryptopay/module.d.ts +5 -5
  12. package/lib/payment/cryptopay/module.js +2 -2
  13. package/lib/payment/datatrans-v2/adapter.js +12 -9
  14. package/lib/payment/datatrans-v2/build3DSCardholder.d.ts +12 -0
  15. package/lib/payment/datatrans-v2/build3DSCardholder.js +44 -0
  16. package/lib/payment/paypal-checkout.js +17 -5
  17. package/lib/payment/payrexx/payrexx.js +4 -4
  18. package/lib/payment/stripe/stripe.js +1 -1
  19. package/lib/presets/base.js +4 -0
  20. package/lib/presets/countries/eu.d.ts +1 -0
  21. package/lib/presets/countries/eu.js +7 -0
  22. package/lib/presets/countries/uk.d.ts +1 -0
  23. package/lib/presets/countries/uk.js +7 -0
  24. package/lib/presets/countries/us.d.ts +1 -0
  25. package/lib/presets/countries/us.js +7 -0
  26. package/lib/pricing/delivery-eu-tax/adapter.d.ts +3 -0
  27. package/lib/pricing/delivery-eu-tax/adapter.js +55 -0
  28. package/lib/pricing/delivery-eu-tax/index.d.ts +4 -0
  29. package/lib/pricing/delivery-eu-tax/index.js +10 -0
  30. package/lib/pricing/delivery-eu-tax.d.ts +2 -0
  31. package/lib/pricing/delivery-eu-tax.js +55 -0
  32. package/lib/pricing/delivery-swiss-tax/adapter.js +7 -26
  33. package/lib/pricing/delivery-swiss-tax.js +7 -26
  34. package/lib/pricing/delivery-uk-tax/adapter.d.ts +3 -0
  35. package/lib/pricing/delivery-uk-tax/adapter.js +43 -0
  36. package/lib/pricing/delivery-uk-tax/index.d.ts +4 -0
  37. package/lib/pricing/delivery-uk-tax/index.js +10 -0
  38. package/lib/pricing/delivery-uk-tax.d.ts +2 -0
  39. package/lib/pricing/delivery-uk-tax.js +43 -0
  40. package/lib/pricing/delivery-us-sales-tax/adapter.d.ts +3 -0
  41. package/lib/pricing/delivery-us-sales-tax/adapter.js +58 -0
  42. package/lib/pricing/delivery-us-sales-tax/index.d.ts +4 -0
  43. package/lib/pricing/delivery-us-sales-tax/index.js +10 -0
  44. package/lib/pricing/delivery-us-sales-tax.d.ts +2 -0
  45. package/lib/pricing/delivery-us-sales-tax.js +58 -0
  46. package/lib/pricing/product-eu-tax/adapter.d.ts +3 -0
  47. package/lib/pricing/product-eu-tax/adapter.js +56 -0
  48. package/lib/pricing/product-eu-tax/index.d.ts +4 -0
  49. package/lib/pricing/product-eu-tax/index.js +10 -0
  50. package/lib/pricing/product-eu-tax.d.ts +2 -0
  51. package/lib/pricing/product-eu-tax.js +56 -0
  52. package/lib/pricing/product-quotation-price.d.ts +2 -0
  53. package/lib/pricing/product-quotation-price.js +40 -0
  54. package/lib/pricing/product-swiss-tax/adapter.js +7 -26
  55. package/lib/pricing/product-swiss-tax.js +7 -26
  56. package/lib/pricing/product-uk-tax/adapter.d.ts +3 -0
  57. package/lib/pricing/product-uk-tax/adapter.js +57 -0
  58. package/lib/pricing/product-uk-tax/index.d.ts +4 -0
  59. package/lib/pricing/product-uk-tax/index.js +10 -0
  60. package/lib/pricing/product-uk-tax.d.ts +2 -0
  61. package/lib/pricing/product-uk-tax.js +57 -0
  62. package/lib/pricing/product-us-sales-tax/adapter.d.ts +3 -0
  63. package/lib/pricing/product-us-sales-tax/adapter.js +52 -0
  64. package/lib/pricing/product-us-sales-tax/index.d.ts +4 -0
  65. package/lib/pricing/product-us-sales-tax/index.js +10 -0
  66. package/lib/pricing/product-us-sales-tax.d.ts +2 -0
  67. package/lib/pricing/product-us-sales-tax.js +52 -0
  68. package/lib/pricing/tax/applyTaxRateToTaxableRows.d.ts +23 -0
  69. package/lib/pricing/tax/applyTaxRateToTaxableRows.js +30 -0
  70. package/lib/pricing/tax/ch-tax-rates.json +24 -0
  71. package/lib/pricing/tax/ch.js +6 -30
  72. package/lib/pricing/tax/eraRates.d.ts +10 -0
  73. package/lib/pricing/tax/eraRates.js +15 -0
  74. package/lib/pricing/tax/eu-tax-rates.json +797 -0
  75. package/lib/pricing/tax/eu.d.ts +12 -0
  76. package/lib/pricing/tax/eu.js +38 -0
  77. package/lib/pricing/tax/uk-tax-rates.json +18 -0
  78. package/lib/pricing/tax/uk.d.ts +10 -0
  79. package/lib/pricing/tax/uk.js +23 -0
  80. package/lib/pricing/tax/us-tax-rates.json +334 -0
  81. package/lib/pricing/tax/us.d.ts +11 -0
  82. package/lib/pricing/tax/us.js +20 -0
  83. package/lib/pricing/utils/isDeliveryAddressInCountry.d.ts +1 -1
  84. package/lib/pricing/utils/isDeliveryAddressInCountry.js +3 -8
  85. package/lib/pricing/utils/resolveDeliveryLocation.d.ts +10 -0
  86. package/lib/pricing/utils/resolveDeliveryLocation.js +9 -0
  87. package/lib/quotations/manual/adapter.js +6 -1
  88. package/lib/quotations/manual.js +6 -1
  89. package/lib/worker/gc-guests/adapter.d.ts +9 -0
  90. package/lib/worker/gc-guests/adapter.js +56 -0
  91. package/lib/worker/gc-guests/index.d.ts +4 -0
  92. package/lib/worker/gc-guests/index.js +13 -0
  93. package/lib/worker/gc-guests.d.ts +8 -0
  94. package/lib/worker/gc-guests.js +55 -0
  95. package/lib/worker/invalidate-carts/adapter.d.ts +9 -0
  96. package/lib/worker/invalidate-carts/adapter.js +52 -0
  97. package/lib/worker/invalidate-carts/index.d.ts +4 -0
  98. package/lib/worker/invalidate-carts/index.js +13 -0
  99. package/lib/worker/invalidate-carts.d.ts +8 -0
  100. package/lib/worker/invalidate-carts.js +51 -0
  101. package/lib/worker/zombie-killer/adapter.d.ts +2 -0
  102. package/lib/worker/zombie-killer/adapter.js +26 -3
  103. package/lib/worker/zombie-killer/index.js +4 -1
  104. package/lib/worker/zombie-killer.d.ts +1 -0
  105. package/lib/worker/zombie-killer.js +24 -3
  106. package/package.json +25 -8
@@ -1 +1,7 @@
1
- export {};
1
+ import type { EmitAdapter } from '@unchainedshop/events';
2
+ export declare const EventBridgeEventEmitter: ({ region, source, busName }: {
3
+ region: any;
4
+ source: any;
5
+ busName: any;
6
+ }) => Promise<EmitAdapter>;
7
+ export default EventBridgeEventEmitter;
@@ -1,7 +1,6 @@
1
- import { setEmitAdapter } from '@unchainedshop/events';
2
1
  import { createLogger } from '@unchainedshop/logger';
3
2
  const logger = createLogger('unchained:eventbridge');
4
- const EventBridgeEventEmitter = async ({ region, source, busName }) => {
3
+ export const EventBridgeEventEmitter = async ({ region, source, busName }) => {
5
4
  const { EventBridgeClient, PutEventsCommand } = await import('@aws-sdk/client-eventbridge');
6
5
  const ebClient = new EventBridgeClient({ region });
7
6
  return {
@@ -24,14 +23,9 @@ const EventBridgeEventEmitter = async ({ region, source, busName }) => {
24
23
  subscribe: () => {
25
24
  throw new Error("You can't subscribe to EventBridge connected Events");
26
25
  },
26
+ shutdown: () => {
27
+ ebClient.destroy();
28
+ },
27
29
  };
28
30
  };
29
- const { EVENT_BRIDGE_REGION, EVENT_BRIDGE_SOURCE, EVENT_BRIDGE_BUS_NAME } = process.env;
30
- if (EVENT_BRIDGE_REGION && EVENT_BRIDGE_SOURCE && EVENT_BRIDGE_BUS_NAME) {
31
- const eventBridgeAdapter = await EventBridgeEventEmitter({
32
- source: EVENT_BRIDGE_SOURCE,
33
- region: EVENT_BRIDGE_REGION,
34
- busName: EVENT_BRIDGE_BUS_NAME,
35
- });
36
- setEmitAdapter(eventBridgeAdapter);
37
- }
31
+ export default EventBridgeEventEmitter;
@@ -1 +1,3 @@
1
- export {};
1
+ import type { EmitAdapter } from '@unchainedshop/events';
2
+ export declare const RedisEventEmitter: () => EmitAdapter;
3
+ export default RedisEventEmitter;
@@ -1,8 +1,7 @@
1
1
  import { createClient } from '@redis/client';
2
- import { setEmitAdapter } from '@unchainedshop/events';
3
2
  const { REDIS_PORT = '6379', REDIS_HOST, REDIS_DB = '0' } = process.env;
4
3
  const subscribedEvents = new Set();
5
- const RedisEventEmitter = () => {
4
+ export const RedisEventEmitter = () => {
6
5
  const redisPublisher = createClient({
7
6
  url: `redis://${REDIS_HOST}:${REDIS_PORT}`,
8
7
  database: parseInt(REDIS_DB, 10),
@@ -21,8 +20,9 @@ const RedisEventEmitter = () => {
21
20
  subscribedEvents.add(eventName);
22
21
  }
23
22
  },
23
+ shutdown: async () => {
24
+ await Promise.allSettled([redisPublisher.close(), redisSubscriber.close()]);
25
+ },
24
26
  };
25
27
  };
26
- if (REDIS_HOST && REDIS_PORT && REDIS_DB) {
27
- setEmitAdapter(RedisEventEmitter());
28
- }
28
+ export default RedisEventEmitter;
@@ -87,7 +87,7 @@ export const gridfsRouteHandler = async (request, context) => {
87
87
  const signature = url.searchParams.get('s');
88
88
  const expiryTimestamp = url.searchParams.get('e');
89
89
  const fileDocument = await modules.files.findFile({
90
- url: `${GRIDFS_PUT_SERVER_PATH}/${directoryName}/${fileName}`,
90
+ url: `${GRIDFS_PUT_SERVER_PATH}/${directoryName}/${encodeURIComponent(fileName)}`,
91
91
  });
92
92
  if (!fileDocument) {
93
93
  logger.error('File not found', { fileName });
@@ -59,7 +59,7 @@ const gridfsHandler = async (req, res) => {
59
59
  if (req.method === 'GET') {
60
60
  const { s: signature, e: expiryTimestamp } = req.query;
61
61
  const fileDocument = await modules.files.findFile({
62
- url: `${GRIDFS_PUT_SERVER_PATH}/${directoryName}/${fileName}`,
62
+ url: `${GRIDFS_PUT_SERVER_PATH}/${directoryName}/${encodeURIComponent(fileName)}`,
63
63
  });
64
64
  if (fileDocument?.meta?.isPrivate) {
65
65
  const expiry = parseInt(expiryTimestamp, 10);
@@ -1,16 +1,10 @@
1
1
  import {} from '@unchainedshop/core';
2
2
  import { LocalSearch } from "./adapter.js";
3
- import { isDocumentDBCompatModeEnabled } from '@unchainedshop/mongodb';
4
3
  export const LocalSearchPlugin = {
5
4
  key: 'shop.unchained.filters.local-search',
6
5
  label: 'Local Search Filter Plugin',
7
6
  version: '1.0.0',
8
7
  adapters: [LocalSearch],
9
- onRegister: () => {
10
- if (isDocumentDBCompatModeEnabled()) {
11
- throw new Error('Full-text search queries have been disabled due to DocumentDB compatibility mode (env UNCHAINED_DOCUMENTDB_COMPAT_MODE is trueish)');
12
- }
13
- },
14
8
  };
15
9
  export default LocalSearchPlugin;
16
10
  export { LocalSearch } from "./adapter.js";
@@ -1,7 +1,4 @@
1
1
  import { FilterDirector, FilterAdapter } from '@unchainedshop/core';
2
- import { createLogger } from '@unchainedshop/logger';
3
- import { isDocumentDBCompatModeEnabled } from '@unchainedshop/mongodb';
4
- const logger = createLogger('unchained:local-search');
5
2
  const LocalSearch = {
6
3
  ...FilterAdapter,
7
4
  key: 'shop.unchained.filters.local-search',
@@ -58,9 +55,4 @@ const LocalSearch = {
58
55
  },
59
56
  };
60
57
  export default LocalSearch;
61
- if (!isDocumentDBCompatModeEnabled()) {
62
- FilterDirector.registerAdapter(LocalSearch);
63
- }
64
- else {
65
- logger.warn('Free text search queries have been disabled due to DocumentDB compatibility mode (env UNCHAINED_DOCUMENTDB_COMPAT_MODE is trueish)');
66
- }
58
+ FilterDirector.registerAdapter(LocalSearch);
@@ -9,4 +9,4 @@ export type CryptopayTransaction = {
9
9
  decimals: number;
10
10
  orderPaymentId?: string;
11
11
  } & TimestampFields;
12
- export declare const CryptopayTransactionsCollection: (db: mongodb.Db) => mongodb.Collection<CryptopayTransaction>;
12
+ export declare const CryptopayTransactionsCollection: (db: mongodb.Db) => Promise<mongodb.Collection<CryptopayTransaction>>;
@@ -1,5 +1,10 @@
1
- import { mongodb } from '@unchainedshop/mongodb';
1
+ import { buildDbIndexes, mongodb } from '@unchainedshop/mongodb';
2
2
  import {} from '@unchainedshop/mongodb';
3
- export const CryptopayTransactionsCollection = (db) => {
4
- return db.collection('cryptopay_transactions');
3
+ export const CryptopayTransactionsCollection = async (db) => {
4
+ const CryptopayTransactions = db.collection('cryptopay_transactions');
5
+ await buildDbIndexes(CryptopayTransactions, [
6
+ { index: { currencyCode: 1 } },
7
+ { index: { orderPaymentId: 1 }, options: { sparse: true } },
8
+ ]);
9
+ return CryptopayTransactions;
5
10
  };
@@ -1,7 +1,7 @@
1
1
  import { type CryptopayTransaction } from './db/CryptopayTransactions.ts';
2
2
  declare const configureCryptopayModule: ({ db }: {
3
3
  db: any;
4
- }) => {
4
+ }) => Promise<{
5
5
  getWalletAddress: (address: string, contract?: string) => Promise<CryptopayTransaction | null>;
6
6
  updateMostRecentBlock: (currencyCode: string, blockHeight: number) => Promise<void>;
7
7
  updateWalletAddress: ({ address, blockHeight, amount, contract, currencyCode, decimals, }: {
@@ -20,12 +20,12 @@ declare const configureCryptopayModule: ({ db }: {
20
20
  }) => Promise<CryptopayTransaction>;
21
21
  getNextDerivationNumber: (currencyCode: string) => Promise<number>;
22
22
  getWalletAddressesByOrderPaymentId: (orderPaymentId: string) => Promise<CryptopayTransaction[]>;
23
- };
23
+ }>;
24
24
  declare const _default: {
25
25
  cryptopay: {
26
26
  configure: ({ db }: {
27
27
  db: any;
28
- }) => {
28
+ }) => Promise<{
29
29
  getWalletAddress: (address: string, contract?: string) => Promise<CryptopayTransaction | null>;
30
30
  updateMostRecentBlock: (currencyCode: string, blockHeight: number) => Promise<void>;
31
31
  updateWalletAddress: ({ address, blockHeight, amount, contract, currencyCode, decimals, }: {
@@ -44,10 +44,10 @@ declare const _default: {
44
44
  }) => Promise<CryptopayTransaction>;
45
45
  getNextDerivationNumber: (currencyCode: string) => Promise<number>;
46
46
  getWalletAddressesByOrderPaymentId: (orderPaymentId: string) => Promise<CryptopayTransaction[]>;
47
- };
47
+ }>;
48
48
  };
49
49
  };
50
50
  export default _default;
51
51
  export interface CryptopayModule {
52
- cryptopay: ReturnType<typeof configureCryptopayModule>;
52
+ cryptopay: Awaited<ReturnType<typeof configureCryptopayModule>>;
53
53
  }
@@ -1,7 +1,7 @@
1
1
  import { mongodb } from '@unchainedshop/mongodb';
2
2
  import { CryptopayTransactionsCollection, } from "./db/CryptopayTransactions.js";
3
- const configureCryptopayModule = ({ db }) => {
4
- const CryptoTransactions = CryptopayTransactionsCollection(db);
3
+ const configureCryptopayModule = async ({ db }) => {
4
+ const CryptoTransactions = await CryptopayTransactionsCollection(db);
5
5
  const getWalletAddress = async (address, contract) => {
6
6
  const addressId = [address, contract].filter(Boolean).join(':');
7
7
  return CryptoTransactions.findOne({ _id: addressId });
@@ -2,6 +2,7 @@ import { createLogger } from '@unchainedshop/logger';
2
2
  import createDatatransAPI from "./api/index.js";
3
3
  import parseRegistrationData from "./parseRegistrationData.js";
4
4
  import roundedAmountFromOrder from "./roundedAmountFromOrder.js";
5
+ import build3DSCardholder, { withCardCardholder, withSecureFieldsCardholder, } from "./build3DSCardholder.js";
5
6
  import { PaymentAdapter, PaymentError, PaymentPricingRowCategory, PaymentPricingSheet, OrderPricingSheet, } from '@unchainedshop/core';
6
7
  const logger = createLogger('unchained:datatrans');
7
8
  const { DATATRANS_SECRET, DATATRANS_SIGN_KEY, DATATRANS_API_ENDPOINT = 'https://api.sandbox.datatrans.com', DATATRANS_MERCHANT_ID, } = process.env;
@@ -221,22 +222,18 @@ export const Datatrans = {
221
222
  const price = order
222
223
  ? roundedAmountFromOrder(order)
223
224
  : {};
225
+ const cardholder = build3DSCardholder(order);
224
226
  if (useSecureFields) {
225
227
  const result = await api().secureFields({
226
- ...arbitraryFields,
228
+ ...withSecureFieldsCardholder(arbitraryFields, cardholder),
227
229
  currency: price.currencyCode || 'CHF',
228
- refno,
229
- refno2,
230
- customer: {
231
- id: userId,
232
- },
233
230
  amount: price.amount,
234
231
  });
235
232
  throwIfResponseError(result);
236
233
  return JSON.stringify(result);
237
234
  }
238
235
  const result = await api().init({
239
- ...arbitraryFields,
236
+ ...withCardCardholder(arbitraryFields, cardholder),
240
237
  currency: price.currencyCode || 'CHF',
241
238
  refno,
242
239
  refno2,
@@ -349,11 +346,17 @@ export const Datatrans = {
349
346
  if (status === 'authenticated') {
350
347
  if (authorizeAuthenticated) {
351
348
  checkIfTransactionAmountValid(transactionId, transaction);
349
+ const { orderPayment, order } = context;
350
+ const userId = order?.userId || context?.userId;
351
+ const refno = orderPayment
352
+ ? Buffer.from(orderPayment._id, 'hex').toString('base64')
353
+ : transaction.refno;
354
+ const refno2 = userId || transaction.refno2;
352
355
  await authorizeAuth({
353
356
  ...(authorizeAuthenticated || {}),
354
357
  transactionId,
355
- refno: transaction.refno,
356
- refno2: transaction.refno2,
358
+ refno,
359
+ refno2,
357
360
  });
358
361
  status = 'authorized';
359
362
  }
@@ -0,0 +1,12 @@
1
+ import type { Order } from '@unchainedshop/core-orders';
2
+ export interface Cardholder {
3
+ cardholderName?: string;
4
+ email?: string;
5
+ homePhone?: {
6
+ cc: string;
7
+ subscriber: string;
8
+ };
9
+ }
10
+ export default function build3DSCardholder(order?: Order): Cardholder | undefined;
11
+ export declare const withCardCardholder: (arbitraryFields: any, cardholder?: Cardholder) => any;
12
+ export declare const withSecureFieldsCardholder: (arbitraryFields: any, cardholder?: Cardholder) => any;
@@ -0,0 +1,44 @@
1
+ import { phoneNumberToParts } from '@unchainedshop/utils';
2
+ export default function build3DSCardholder(order) {
3
+ if (!order)
4
+ return undefined;
5
+ const { billingAddress, contact } = order;
6
+ const cardholderName = [billingAddress?.firstName, billingAddress?.lastName].filter(Boolean).join(' ');
7
+ const homePhone = phoneNumberToParts(contact?.telNumber, billingAddress?.countryCode);
8
+ const cardholder = {};
9
+ if (cardholderName)
10
+ cardholder.cardholderName = cardholderName;
11
+ if (contact?.emailAddress)
12
+ cardholder.email = contact.emailAddress;
13
+ if (homePhone)
14
+ cardholder.homePhone = homePhone;
15
+ if (Object.keys(cardholder).length === 0)
16
+ return undefined;
17
+ return cardholder;
18
+ }
19
+ const mergeCardholder = (container, cardholder) => {
20
+ const threeDS = container?.['3D'] || {};
21
+ return {
22
+ ...container,
23
+ '3D': {
24
+ ...threeDS,
25
+ cardholder: {
26
+ ...cardholder,
27
+ ...(threeDS.cardholder || {}),
28
+ },
29
+ },
30
+ };
31
+ };
32
+ export const withCardCardholder = (arbitraryFields, cardholder) => {
33
+ if (!cardholder)
34
+ return arbitraryFields;
35
+ return {
36
+ ...arbitraryFields,
37
+ card: mergeCardholder(arbitraryFields?.card, cardholder),
38
+ };
39
+ };
40
+ export const withSecureFieldsCardholder = (arbitraryFields, cardholder) => {
41
+ if (!cardholder)
42
+ return arbitraryFields;
43
+ return mergeCardholder(arbitraryFields, cardholder);
44
+ };
@@ -58,19 +58,31 @@ const PaypalCheckout = {
58
58
  throw new Error('You have to provide orderID in paymentContext');
59
59
  }
60
60
  try {
61
- const request = new checkoutNodeJssdk.orders.OrdersGetRequest(orderID);
62
61
  const client = new checkoutNodeJssdk.core.PayPalHttpClient(environment());
63
- const paypalOrder = await client.execute(request);
62
+ const getRequest = new checkoutNodeJssdk.orders.OrdersGetRequest(orderID);
63
+ let paypalResult = (await client.execute(getRequest)).result;
64
+ if (paypalResult.status === 'APPROVED') {
65
+ const captureRequest = new checkoutNodeJssdk.orders.OrdersCaptureRequest(orderID);
66
+ captureRequest.requestBody({});
67
+ paypalResult = (await client.execute(captureRequest)).result;
68
+ }
69
+ if (paypalResult.status !== 'COMPLETED') {
70
+ throw new Error(`PayPal order not completed (status: ${paypalResult.status})`);
71
+ }
72
+ const capture = paypalResult.purchase_units?.[0]?.payments?.captures?.find((c) => c.status === 'COMPLETED');
73
+ if (!capture) {
74
+ throw new Error('PayPal order has no completed capture');
75
+ }
64
76
  const pricing = OrderPricingSheet({
65
77
  calculation: order.calculation,
66
78
  currencyCode: order.currencyCode,
67
79
  });
68
80
  const ourTotal = roundToDecimals(pricing.total({ useNetPrice: false }).amount / 100, 2);
69
- const paypalTotal = roundToDecimals(paypalOrder.result.purchase_units[0].amount.value, 2);
70
- if (ourTotal === paypalTotal) {
81
+ const paidTotal = roundToDecimals(capture.amount.value, 2);
82
+ if (ourTotal === paidTotal && capture.amount.currency_code === order.currencyCode) {
71
83
  return order;
72
84
  }
73
- logger.warn('Missmatch PAYPAL ORDER', JSON.stringify(paypalOrder.result, null, 2));
85
+ logger.warn('Missmatch PAYPAL ORDER', JSON.stringify(paypalResult, null, 2));
74
86
  logger.debug('OUR ORDER', order);
75
87
  logger.debug('OUR PRICE', pricing);
76
88
  throw new Error(`Payment mismatch`);
@@ -1,9 +1,9 @@
1
1
  const getRedirects = () => {
2
- const { EMAIL_WEBSITE_URL, DATATRANS_SUCCESS_PATH = '/payrexx/success', DATATRANS_ERROR_PATH = '/payrexx/error', DATATRANS_CANCEL_PATH = '/payrexx/cancel', } = process.env;
2
+ const { EMAIL_WEBSITE_URL, PAYREXX_SUCCESS_PATH = '/payrexx/success', PAYREXX_ERROR_PATH = '/payrexx/error', PAYREXX_CANCEL_PATH = '/payrexx/cancel', } = process.env;
3
3
  return {
4
- successRedirectUrl: `${EMAIL_WEBSITE_URL}${DATATRANS_SUCCESS_PATH}`,
5
- failedRedirectUrl: `${EMAIL_WEBSITE_URL}${DATATRANS_ERROR_PATH}`,
6
- cancelRedirectUrl: `${EMAIL_WEBSITE_URL}${DATATRANS_CANCEL_PATH}`,
4
+ successRedirectUrl: `${EMAIL_WEBSITE_URL}${PAYREXX_SUCCESS_PATH}`,
5
+ failedRedirectUrl: `${EMAIL_WEBSITE_URL}${PAYREXX_ERROR_PATH}`,
6
+ cancelRedirectUrl: `${EMAIL_WEBSITE_URL}${PAYREXX_CANCEL_PATH}`,
7
7
  };
8
8
  };
9
9
  export const mapOrderDataToGatewayObject = ({ order, orderPayment, pricing }, options = {}) => {
@@ -7,7 +7,7 @@ if (STRIPE_SECRET) {
7
7
  try {
8
8
  const { default: Stripe } = await import('stripe');
9
9
  stripe = new Stripe(STRIPE_SECRET, {
10
- apiVersion: '2026-02-25.clover',
10
+ apiVersion: '2026-07-29.dahlia',
11
11
  });
12
12
  }
13
13
  catch {
@@ -19,6 +19,8 @@ import { LicensedEnrollmentsPlugin } from "../enrollments/licensed/index.js";
19
19
  import { NodeEventEmitter } from "../events/node-event-emitter.js";
20
20
  import { BulkImportPlugin } from "../worker/bulk-import/index.js";
21
21
  import { ZombieKillerPlugin } from "../worker/zombie-killer/index.js";
22
+ import { GCGuestsPlugin } from "../worker/gc-guests/index.js";
23
+ import { InvalidateCartsPlugin } from "../worker/invalidate-carts/index.js";
22
24
  import { MessagePlugin } from "../worker/message/index.js";
23
25
  import { ExternalPlugin } from "../worker/external/index.js";
24
26
  import { HttpRequestPlugin } from "../worker/http-request/index.js";
@@ -46,6 +48,8 @@ export function registerBasePlugins() {
46
48
  setEmitAdapter(NodeEventEmitter());
47
49
  pluginRegistry.register(BulkImportPlugin);
48
50
  pluginRegistry.register(ZombieKillerPlugin);
51
+ pluginRegistry.register(GCGuestsPlugin);
52
+ pluginRegistry.register(InvalidateCartsPlugin);
49
53
  pluginRegistry.register(MessagePlugin);
50
54
  pluginRegistry.register(ExternalPlugin);
51
55
  pluginRegistry.register(HttpRequestPlugin);
@@ -0,0 +1 @@
1
+ export declare function registerEuTaxPlugins(): void;
@@ -0,0 +1,7 @@
1
+ import { ProductEuTaxPlugin } from "../../pricing/product-eu-tax/index.js";
2
+ import { DeliveryEuTaxPlugin } from "../../pricing/delivery-eu-tax/index.js";
3
+ import { pluginRegistry } from '@unchainedshop/core';
4
+ export function registerEuTaxPlugins() {
5
+ pluginRegistry.register(ProductEuTaxPlugin);
6
+ pluginRegistry.register(DeliveryEuTaxPlugin);
7
+ }
@@ -0,0 +1 @@
1
+ export declare function registerUkTaxPlugins(): void;
@@ -0,0 +1,7 @@
1
+ import { ProductUkTaxPlugin } from "../../pricing/product-uk-tax/index.js";
2
+ import { DeliveryUkTaxPlugin } from "../../pricing/delivery-uk-tax/index.js";
3
+ import { pluginRegistry } from '@unchainedshop/core';
4
+ export function registerUkTaxPlugins() {
5
+ pluginRegistry.register(ProductUkTaxPlugin);
6
+ pluginRegistry.register(DeliveryUkTaxPlugin);
7
+ }
@@ -0,0 +1 @@
1
+ export declare function registerUsSalesTaxPlugins(): void;
@@ -0,0 +1,7 @@
1
+ import { ProductUsSalesTaxPlugin } from "../../pricing/product-us-sales-tax/index.js";
2
+ import { DeliveryUsSalesTaxPlugin } from "../../pricing/delivery-us-sales-tax/index.js";
3
+ import { pluginRegistry } from '@unchainedshop/core';
4
+ export function registerUsSalesTaxPlugins() {
5
+ pluginRegistry.register(ProductUsSalesTaxPlugin);
6
+ pluginRegistry.register(DeliveryUsSalesTaxPlugin);
7
+ }
@@ -0,0 +1,3 @@
1
+ import { type IDeliveryPricingAdapter } from '@unchainedshop/core';
2
+ export declare const DeliveryEuTax: IDeliveryPricingAdapter;
3
+ export default DeliveryEuTax;
@@ -0,0 +1,55 @@
1
+ import { DeliveryPricingRowCategory, DeliveryPricingAdapter, } from '@unchainedshop/core';
2
+ import { isEuMemberCountry, resolveEuTaxCategoryFromDeliveryProvider, resolveEuTaxRate, } from "../tax/eu.js";
3
+ import resolveDeliveryLocation from "../utils/resolveDeliveryLocation.js";
4
+ import { applyTaxRateToTaxableRows } from "../tax/applyTaxRateToTaxableRows.js";
5
+ export const DeliveryEuTax = {
6
+ ...DeliveryPricingAdapter,
7
+ key: 'shop.unchained.pricing.delivery-eu-tax',
8
+ version: '1.0.0',
9
+ label: 'Apply EU VAT on Delivery Fees',
10
+ orderIndex: 80,
11
+ isActivatedFor: (context) => {
12
+ if (!context.order)
13
+ return false;
14
+ if (!context.orderDelivery)
15
+ return false;
16
+ const { countryCode } = resolveDeliveryLocation({
17
+ order: context.order,
18
+ orderDelivery: context.orderDelivery,
19
+ countryCode: context.countryCode,
20
+ });
21
+ return isEuMemberCountry(countryCode);
22
+ },
23
+ actions: (params) => {
24
+ const pricingAdapter = DeliveryPricingAdapter.actions(params);
25
+ const { context } = params;
26
+ return {
27
+ ...pricingAdapter,
28
+ calculate: async () => {
29
+ const { countryCode } = resolveDeliveryLocation({
30
+ order: context.order,
31
+ orderDelivery: context.orderDelivery,
32
+ countryCode: context.countryCode,
33
+ });
34
+ const category = resolveEuTaxCategoryFromDeliveryProvider(context.provider);
35
+ const taxRate = resolveEuTaxRate({
36
+ countryCode,
37
+ category,
38
+ referenceDate: context.order?.ordered,
39
+ });
40
+ if (taxRate === null)
41
+ return pricingAdapter.calculate();
42
+ DeliveryPricingAdapter.log(`DeliveryEuTax -> Tax Multiplicator: ${taxRate}`);
43
+ applyTaxRateToTaxableRows({
44
+ calculationSheet: params.calculationSheet,
45
+ resultSheet: pricingAdapter.resultSheet(),
46
+ taxRate,
47
+ baseCategory: DeliveryPricingRowCategory.Delivery,
48
+ adapterKey: DeliveryEuTax.key,
49
+ });
50
+ return pricingAdapter.calculate();
51
+ },
52
+ };
53
+ },
54
+ };
55
+ export default DeliveryEuTax;
@@ -0,0 +1,4 @@
1
+ import { type IPlugin } from '@unchainedshop/core';
2
+ export declare const DeliveryEuTaxPlugin: IPlugin;
3
+ export default DeliveryEuTaxPlugin;
4
+ export { DeliveryEuTax } from './adapter.ts';
@@ -0,0 +1,10 @@
1
+ import {} from '@unchainedshop/core';
2
+ import { DeliveryEuTax } from "./adapter.js";
3
+ export const DeliveryEuTaxPlugin = {
4
+ key: 'shop.unchained.pricing.delivery-eu-tax',
5
+ label: 'Delivery EU Tax Plugin',
6
+ version: '1.0.0',
7
+ adapters: [DeliveryEuTax],
8
+ };
9
+ export default DeliveryEuTaxPlugin;
10
+ export { DeliveryEuTax } from "./adapter.js";
@@ -0,0 +1,2 @@
1
+ import { type IDeliveryPricingAdapter } from '@unchainedshop/core';
2
+ export declare const DeliveryEuTax: IDeliveryPricingAdapter;
@@ -0,0 +1,55 @@
1
+ import { DeliveryPricingRowCategory, DeliveryPricingAdapter, DeliveryPricingDirector, } from '@unchainedshop/core';
2
+ import { isEuMemberCountry, resolveEuTaxCategoryFromDeliveryProvider, resolveEuTaxRate, } from "./tax/eu.js";
3
+ import resolveDeliveryLocation from "./utils/resolveDeliveryLocation.js";
4
+ import { applyTaxRateToTaxableRows } from "./tax/applyTaxRateToTaxableRows.js";
5
+ export const DeliveryEuTax = {
6
+ ...DeliveryPricingAdapter,
7
+ key: 'shop.unchained.pricing.delivery-eu-tax',
8
+ version: '1.0.0',
9
+ label: 'Apply EU VAT on Delivery Fees',
10
+ orderIndex: 80,
11
+ isActivatedFor: (context) => {
12
+ if (!context.order)
13
+ return false;
14
+ if (!context.orderDelivery)
15
+ return false;
16
+ const { countryCode } = resolveDeliveryLocation({
17
+ order: context.order,
18
+ orderDelivery: context.orderDelivery,
19
+ countryCode: context.countryCode,
20
+ });
21
+ return isEuMemberCountry(countryCode);
22
+ },
23
+ actions: (params) => {
24
+ const pricingAdapter = DeliveryPricingAdapter.actions(params);
25
+ const { context } = params;
26
+ return {
27
+ ...pricingAdapter,
28
+ calculate: async () => {
29
+ const { countryCode } = resolveDeliveryLocation({
30
+ order: context.order,
31
+ orderDelivery: context.orderDelivery,
32
+ countryCode: context.countryCode,
33
+ });
34
+ const category = resolveEuTaxCategoryFromDeliveryProvider(context.provider);
35
+ const taxRate = resolveEuTaxRate({
36
+ countryCode,
37
+ category,
38
+ referenceDate: context.order?.ordered,
39
+ });
40
+ if (taxRate === null)
41
+ return pricingAdapter.calculate();
42
+ DeliveryPricingAdapter.log(`DeliveryEuTax -> Tax Multiplicator: ${taxRate}`);
43
+ applyTaxRateToTaxableRows({
44
+ calculationSheet: params.calculationSheet,
45
+ resultSheet: pricingAdapter.resultSheet(),
46
+ taxRate,
47
+ baseCategory: DeliveryPricingRowCategory.Delivery,
48
+ adapterKey: DeliveryEuTax.key,
49
+ });
50
+ return pricingAdapter.calculate();
51
+ },
52
+ };
53
+ },
54
+ };
55
+ DeliveryPricingDirector.registerAdapter(DeliveryEuTax);
@@ -1,6 +1,7 @@
1
1
  import { DeliveryPricingRowCategory, DeliveryPricingAdapter, } from '@unchainedshop/core';
2
2
  import { resolveTaxCategoryFromDeliveryProvider, SwissTaxCategories } from "../tax/ch.js";
3
3
  import isDeliveryAddressInCountry from "../utils/isDeliveryAddressInCountry.js";
4
+ import { applyTaxRateToTaxableRows } from "../tax/applyTaxRateToTaxableRows.js";
4
5
  export const DeliverySwissTax = {
5
6
  ...DeliveryPricingAdapter,
6
7
  key: 'shop.unchained.pricing.delivery-swiss-tax',
@@ -27,32 +28,12 @@ export const DeliverySwissTax = {
27
28
  const taxCategory = resolveTaxCategoryFromDeliveryProvider(context.provider) || SwissTaxCategories.DEFAULT;
28
29
  const taxRate = taxCategory.rate(context.order?.ordered);
29
30
  DeliveryPricingAdapter.log(`DeliverySwissTax -> Tax Multiplicator: ${taxRate}`);
30
- params.calculationSheet.filterBy({ isTaxable: true }).forEach(({ isNetPrice, ...row }) => {
31
- if (!isNetPrice) {
32
- const taxAmount = row.amount - row.amount / (1 + taxRate);
33
- pricingAdapter.resultSheet().calculation.push({
34
- ...row,
35
- amount: -taxAmount,
36
- isTaxable: false,
37
- isNetPrice: false,
38
- meta: { adapter: DeliverySwissTax.key },
39
- });
40
- pricingAdapter.resultSheet().addTax({
41
- amount: taxAmount,
42
- rate: taxRate,
43
- baseCategory: DeliveryPricingRowCategory.Delivery,
44
- meta: { adapter: DeliverySwissTax.key },
45
- });
46
- }
47
- else {
48
- const taxAmount = row.amount * taxRate;
49
- pricingAdapter.resultSheet().addTax({
50
- amount: taxAmount,
51
- rate: taxRate,
52
- baseCategory: DeliveryPricingRowCategory.Delivery,
53
- meta: { adapter: DeliverySwissTax.key },
54
- });
55
- }
31
+ applyTaxRateToTaxableRows({
32
+ calculationSheet: params.calculationSheet,
33
+ resultSheet: pricingAdapter.resultSheet(),
34
+ taxRate,
35
+ baseCategory: DeliveryPricingRowCategory.Delivery,
36
+ adapterKey: DeliverySwissTax.key,
56
37
  });
57
38
  return pricingAdapter.calculate();
58
39
  },