@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.
- package/lib/events/aws-eventbridge.d.ts +7 -1
- package/lib/events/aws-eventbridge.js +5 -11
- package/lib/events/redis.d.ts +3 -1
- package/lib/events/redis.js +5 -5
- package/lib/files/gridfs/api.js +1 -1
- package/lib/files/gridfs/handler-express.js +1 -1
- package/lib/filters/local-search/index.js +0 -6
- package/lib/filters/local-search.js +1 -9
- package/lib/payment/cryptopay/db/CryptopayTransactions.d.ts +1 -1
- package/lib/payment/cryptopay/db/CryptopayTransactions.js +8 -3
- package/lib/payment/cryptopay/module.d.ts +5 -5
- package/lib/payment/cryptopay/module.js +2 -2
- package/lib/payment/datatrans-v2/adapter.js +12 -9
- package/lib/payment/datatrans-v2/build3DSCardholder.d.ts +12 -0
- package/lib/payment/datatrans-v2/build3DSCardholder.js +44 -0
- package/lib/payment/paypal-checkout.js +17 -5
- package/lib/payment/payrexx/payrexx.js +4 -4
- package/lib/payment/stripe/stripe.js +1 -1
- package/lib/presets/base.js +4 -0
- package/lib/presets/countries/eu.d.ts +1 -0
- package/lib/presets/countries/eu.js +7 -0
- package/lib/presets/countries/uk.d.ts +1 -0
- package/lib/presets/countries/uk.js +7 -0
- package/lib/presets/countries/us.d.ts +1 -0
- package/lib/presets/countries/us.js +7 -0
- package/lib/pricing/delivery-eu-tax/adapter.d.ts +3 -0
- package/lib/pricing/delivery-eu-tax/adapter.js +55 -0
- package/lib/pricing/delivery-eu-tax/index.d.ts +4 -0
- package/lib/pricing/delivery-eu-tax/index.js +10 -0
- package/lib/pricing/delivery-eu-tax.d.ts +2 -0
- package/lib/pricing/delivery-eu-tax.js +55 -0
- package/lib/pricing/delivery-swiss-tax/adapter.js +7 -26
- package/lib/pricing/delivery-swiss-tax.js +7 -26
- package/lib/pricing/delivery-uk-tax/adapter.d.ts +3 -0
- package/lib/pricing/delivery-uk-tax/adapter.js +43 -0
- package/lib/pricing/delivery-uk-tax/index.d.ts +4 -0
- package/lib/pricing/delivery-uk-tax/index.js +10 -0
- package/lib/pricing/delivery-uk-tax.d.ts +2 -0
- package/lib/pricing/delivery-uk-tax.js +43 -0
- package/lib/pricing/delivery-us-sales-tax/adapter.d.ts +3 -0
- package/lib/pricing/delivery-us-sales-tax/adapter.js +58 -0
- package/lib/pricing/delivery-us-sales-tax/index.d.ts +4 -0
- package/lib/pricing/delivery-us-sales-tax/index.js +10 -0
- package/lib/pricing/delivery-us-sales-tax.d.ts +2 -0
- package/lib/pricing/delivery-us-sales-tax.js +58 -0
- package/lib/pricing/product-eu-tax/adapter.d.ts +3 -0
- package/lib/pricing/product-eu-tax/adapter.js +56 -0
- package/lib/pricing/product-eu-tax/index.d.ts +4 -0
- package/lib/pricing/product-eu-tax/index.js +10 -0
- package/lib/pricing/product-eu-tax.d.ts +2 -0
- package/lib/pricing/product-eu-tax.js +56 -0
- package/lib/pricing/product-quotation-price.d.ts +2 -0
- package/lib/pricing/product-quotation-price.js +40 -0
- package/lib/pricing/product-swiss-tax/adapter.js +7 -26
- package/lib/pricing/product-swiss-tax.js +7 -26
- package/lib/pricing/product-uk-tax/adapter.d.ts +3 -0
- package/lib/pricing/product-uk-tax/adapter.js +57 -0
- package/lib/pricing/product-uk-tax/index.d.ts +4 -0
- package/lib/pricing/product-uk-tax/index.js +10 -0
- package/lib/pricing/product-uk-tax.d.ts +2 -0
- package/lib/pricing/product-uk-tax.js +57 -0
- package/lib/pricing/product-us-sales-tax/adapter.d.ts +3 -0
- package/lib/pricing/product-us-sales-tax/adapter.js +52 -0
- package/lib/pricing/product-us-sales-tax/index.d.ts +4 -0
- package/lib/pricing/product-us-sales-tax/index.js +10 -0
- package/lib/pricing/product-us-sales-tax.d.ts +2 -0
- package/lib/pricing/product-us-sales-tax.js +52 -0
- package/lib/pricing/tax/applyTaxRateToTaxableRows.d.ts +23 -0
- package/lib/pricing/tax/applyTaxRateToTaxableRows.js +30 -0
- package/lib/pricing/tax/ch-tax-rates.json +24 -0
- package/lib/pricing/tax/ch.js +6 -30
- package/lib/pricing/tax/eraRates.d.ts +10 -0
- package/lib/pricing/tax/eraRates.js +15 -0
- package/lib/pricing/tax/eu-tax-rates.json +797 -0
- package/lib/pricing/tax/eu.d.ts +12 -0
- package/lib/pricing/tax/eu.js +38 -0
- package/lib/pricing/tax/uk-tax-rates.json +18 -0
- package/lib/pricing/tax/uk.d.ts +10 -0
- package/lib/pricing/tax/uk.js +23 -0
- package/lib/pricing/tax/us-tax-rates.json +334 -0
- package/lib/pricing/tax/us.d.ts +11 -0
- package/lib/pricing/tax/us.js +20 -0
- package/lib/pricing/utils/isDeliveryAddressInCountry.d.ts +1 -1
- package/lib/pricing/utils/isDeliveryAddressInCountry.js +3 -8
- package/lib/pricing/utils/resolveDeliveryLocation.d.ts +10 -0
- package/lib/pricing/utils/resolveDeliveryLocation.js +9 -0
- package/lib/quotations/manual/adapter.js +6 -1
- package/lib/quotations/manual.js +6 -1
- package/lib/worker/gc-guests/adapter.d.ts +9 -0
- package/lib/worker/gc-guests/adapter.js +56 -0
- package/lib/worker/gc-guests/index.d.ts +4 -0
- package/lib/worker/gc-guests/index.js +13 -0
- package/lib/worker/gc-guests.d.ts +8 -0
- package/lib/worker/gc-guests.js +55 -0
- package/lib/worker/invalidate-carts/adapter.d.ts +9 -0
- package/lib/worker/invalidate-carts/adapter.js +52 -0
- package/lib/worker/invalidate-carts/index.d.ts +4 -0
- package/lib/worker/invalidate-carts/index.js +13 -0
- package/lib/worker/invalidate-carts.d.ts +8 -0
- package/lib/worker/invalidate-carts.js +51 -0
- package/lib/worker/zombie-killer/adapter.d.ts +2 -0
- package/lib/worker/zombie-killer/adapter.js +26 -3
- package/lib/worker/zombie-killer/index.js +4 -1
- package/lib/worker/zombie-killer.d.ts +1 -0
- package/lib/worker/zombie-killer.js +24 -3
- package/package.json +25 -8
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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;
|
package/lib/events/redis.d.ts
CHANGED
package/lib/events/redis.js
CHANGED
|
@@ -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
|
-
|
|
27
|
-
setEmitAdapter(RedisEventEmitter());
|
|
28
|
-
}
|
|
28
|
+
export default RedisEventEmitter;
|
package/lib/files/gridfs/api.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
356
|
-
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
|
|
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
|
|
70
|
-
if (ourTotal ===
|
|
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(
|
|
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,
|
|
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}${
|
|
5
|
-
failedRedirectUrl: `${EMAIL_WEBSITE_URL}${
|
|
6
|
-
cancelRedirectUrl: `${EMAIL_WEBSITE_URL}${
|
|
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 = {}) => {
|
package/lib/presets/base.js
CHANGED
|
@@ -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,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,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,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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
},
|