@unchainedshop/plugins 4.7.2 → 4.8.0
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 +2 -0
- package/lib/delivery/post.d.ts +1 -0
- package/lib/delivery/post.js +23 -0
- package/lib/delivery/send-message.d.ts +1 -0
- package/lib/delivery/send-message.js +40 -0
- package/lib/delivery/stores.d.ts +1 -0
- package/lib/delivery/stores.js +43 -0
- package/lib/enrollments/licensed.d.ts +4 -0
- package/lib/enrollments/licensed.js +52 -0
- package/lib/events/node-event-emitter.d.ts +1 -3
- package/lib/events/node-event-emitter.js +3 -3
- package/lib/files/gridfs/adapter.d.ts +2 -2
- package/lib/files/gridfs/adapter.js +5 -2
- package/lib/files/gridfs/handler-express.d.ts +9 -0
- package/lib/files/gridfs/handler-express.js +110 -0
- package/lib/files/gridfs/handler-fastify.d.ts +3 -0
- package/lib/files/gridfs/handler-fastify.js +138 -0
- package/lib/files/gridfs/index.d.ts +4 -5
- package/lib/files/gridfs/index.js +3 -30
- package/lib/files/minio/handler-express.d.ts +2 -0
- package/lib/files/minio/handler-express.js +36 -0
- package/lib/files/minio/handler-fastify.d.ts +3 -0
- package/lib/files/minio/handler-fastify.js +40 -0
- package/lib/files/minio/index.d.ts +5 -4
- package/lib/files/minio/index.js +157 -18
- package/lib/filters/local-search.d.ts +3 -0
- package/lib/filters/local-search.js +66 -0
- package/lib/filters/strict-equal.d.ts +1 -0
- package/lib/filters/strict-equal.js +24 -0
- package/lib/payment/apple-iap/adapter.d.ts +1 -2
- package/lib/payment/apple-iap/adapter.js +3 -2
- package/lib/payment/apple-iap/handler-express.d.ts +2 -0
- package/lib/payment/apple-iap/handler-express.js +103 -0
- package/lib/payment/apple-iap/handler-fastify.d.ts +3 -0
- package/lib/payment/apple-iap/handler-fastify.js +102 -0
- package/lib/payment/apple-iap/index.d.ts +4 -6
- package/lib/payment/apple-iap/index.js +3 -28
- package/lib/payment/braintree.d.ts +1 -0
- package/lib/payment/braintree.js +138 -0
- package/lib/payment/cryptopay/handler-express.d.ts +2 -0
- package/lib/payment/cryptopay/handler-express.js +19 -0
- package/lib/payment/cryptopay/handler-fastify.d.ts +3 -0
- package/lib/payment/cryptopay/handler-fastify.js +17 -0
- package/lib/payment/cryptopay/index.d.ts +4 -5
- package/lib/payment/cryptopay/index.js +3 -31
- package/lib/payment/cryptopay/plugin.d.ts +3 -0
- package/lib/payment/cryptopay/plugin.js +159 -0
- package/lib/payment/datatrans-v2/handler-express.d.ts +1 -0
- package/lib/payment/datatrans-v2/handler-express.js +71 -0
- package/lib/payment/datatrans-v2/handler-fastify.d.ts +2 -0
- package/lib/payment/datatrans-v2/handler-fastify.js +71 -0
- package/lib/payment/datatrans-v2/index.d.ts +1 -4
- package/lib/payment/datatrans-v2/index.js +400 -17
- package/lib/payment/invoice-prepaid.d.ts +1 -0
- package/lib/payment/invoice-prepaid.js +30 -0
- package/lib/payment/invoice.d.ts +1 -0
- package/lib/payment/invoice.js +27 -0
- package/lib/payment/paypal-checkout.d.ts +1 -0
- package/lib/payment/paypal-checkout.js +87 -0
- package/lib/payment/payrexx/handler-express.d.ts +1 -0
- package/lib/payment/payrexx/handler-express.js +85 -0
- package/lib/payment/payrexx/handler-fastify.d.ts +2 -0
- package/lib/payment/payrexx/handler-fastify.js +91 -0
- package/lib/payment/payrexx/index.d.ts +1 -4
- package/lib/payment/payrexx/index.js +158 -19
- package/lib/payment/postfinance-checkout/handler-express.d.ts +1 -0
- package/lib/payment/postfinance-checkout/handler-express.js +37 -0
- package/lib/payment/postfinance-checkout/handler-fastify.d.ts +2 -0
- package/lib/payment/postfinance-checkout/handler-fastify.js +40 -0
- package/lib/payment/postfinance-checkout/index.d.ts +0 -5
- package/lib/payment/postfinance-checkout/index.js +186 -25
- package/lib/payment/saferpay/adapter.js +2 -1
- package/lib/payment/saferpay/handler-express.d.ts +1 -0
- package/lib/payment/saferpay/handler-express.js +59 -0
- package/lib/payment/saferpay/handler-fastify.d.ts +2 -0
- package/lib/payment/saferpay/handler-fastify.js +62 -0
- package/lib/payment/saferpay/index.d.ts +4 -5
- package/lib/payment/saferpay/index.js +3 -29
- package/lib/payment/stripe/handler-express.d.ts +5 -0
- package/lib/payment/stripe/handler-express.js +121 -0
- package/lib/payment/stripe/handler-fastify.d.ts +6 -0
- package/lib/payment/stripe/handler-fastify.js +131 -0
- package/lib/payment/stripe/index.d.ts +1 -4
- package/lib/payment/stripe/index.js +116 -22
- package/lib/payment/stripe/stripe.js +4 -1
- package/lib/presets/all-express.d.ts +6 -0
- package/lib/presets/all-express.js +26 -0
- package/lib/presets/all-fastify.d.ts +6 -0
- package/lib/presets/all-fastify.js +49 -0
- package/lib/presets/all.d.ts +70 -1
- package/lib/presets/all.js +27 -41
- package/lib/presets/base-express.d.ts +2 -0
- package/lib/presets/base-express.js +5 -0
- package/lib/presets/base-fastify.d.ts +3 -0
- package/lib/presets/base-fastify.js +16 -0
- package/lib/presets/base.d.ts +25 -1
- package/lib/presets/base.js +25 -56
- package/lib/presets/countries/ch.d.ts +2 -1
- package/lib/presets/countries/ch.js +2 -7
- package/lib/presets/crypto-express.d.ts +3 -0
- package/lib/presets/crypto-express.js +8 -0
- package/lib/presets/crypto-fastify.d.ts +3 -0
- package/lib/presets/crypto-fastify.js +11 -0
- package/lib/presets/crypto.d.ts +7 -3
- package/lib/presets/crypto.js +7 -18
- package/lib/pricing/delivery-swiss-tax.d.ts +2 -0
- package/lib/pricing/delivery-swiss-tax.js +62 -0
- package/lib/pricing/discount-100-off.d.ts +2 -0
- package/lib/pricing/discount-100-off.js +30 -0
- package/lib/pricing/discount-half-price-manual.d.ts +2 -0
- package/lib/pricing/discount-half-price-manual.js +30 -0
- package/lib/pricing/discount-half-price.d.ts +2 -0
- package/lib/pricing/discount-half-price.js +33 -0
- package/lib/pricing/free-delivery.d.ts +2 -0
- package/lib/pricing/free-delivery.js +27 -0
- package/lib/pricing/free-payment.d.ts +2 -0
- package/lib/pricing/free-payment.js +27 -0
- package/lib/pricing/order-delivery.d.ts +2 -0
- package/lib/pricing/order-delivery.js +33 -0
- package/lib/pricing/order-discount.d.ts +2 -0
- package/lib/pricing/order-discount.js +74 -0
- package/lib/pricing/order-items-discount.d.ts +2 -0
- package/lib/pricing/order-items-discount.js +50 -0
- package/lib/pricing/order-items.d.ts +2 -0
- package/lib/pricing/order-items.js +43 -0
- package/lib/pricing/order-payment.d.ts +2 -0
- package/lib/pricing/order-payment.js +33 -0
- package/lib/pricing/order-round.d.ts +10 -0
- package/lib/pricing/order-round.js +135 -0
- package/lib/pricing/product-catalog-price-options.d.ts +2 -0
- package/lib/pricing/product-catalog-price-options.js +51 -0
- package/lib/pricing/product-catalog-price.d.ts +2 -0
- package/lib/pricing/product-catalog-price.js +60 -0
- package/lib/pricing/product-discount.d.ts +2 -0
- package/lib/pricing/product-discount.js +69 -0
- package/lib/pricing/product-price-rateconversion.d.ts +2 -0
- package/lib/pricing/product-price-rateconversion.js +53 -0
- package/lib/pricing/product-round.d.ts +10 -0
- package/lib/pricing/product-round.js +44 -0
- package/lib/pricing/product-swiss-tax.d.ts +2 -0
- package/lib/pricing/product-swiss-tax.js +76 -0
- package/lib/quotations/manual.d.ts +1 -0
- package/lib/quotations/manual.js +20 -0
- package/lib/warehousing/eth-minter.d.ts +1 -0
- package/lib/warehousing/eth-minter.js +120 -0
- package/lib/warehousing/store.d.ts +1 -0
- package/lib/warehousing/store.js +34 -0
- package/lib/worker/budgetsms.d.ts +9 -0
- package/lib/worker/budgetsms.js +169 -0
- package/lib/worker/bulk-export.d.ts +2 -0
- package/lib/worker/bulk-export.js +64 -0
- package/lib/worker/bulk-import.d.ts +2 -0
- package/lib/worker/bulk-import.js +65 -0
- package/lib/worker/bulkgate.d.ts +1 -0
- package/lib/worker/bulkgate.js +91 -0
- package/lib/worker/email.d.ts +1 -0
- package/lib/worker/email.js +136 -0
- package/lib/worker/enrollment-order-generator.d.ts +1 -0
- package/lib/worker/enrollment-order-generator.js +77 -0
- package/lib/worker/error-notifications.d.ts +9 -0
- package/lib/worker/error-notifications.js +61 -0
- package/lib/worker/export-token.d.ts +4 -0
- package/lib/worker/export-token.js +26 -0
- package/lib/worker/external.d.ts +2 -0
- package/lib/worker/external.js +13 -0
- package/lib/worker/heartbeat.d.ts +1 -0
- package/lib/worker/heartbeat.js +26 -0
- package/lib/worker/http-request.d.ts +8 -0
- package/lib/worker/http-request.js +56 -0
- package/lib/worker/message.d.ts +10 -0
- package/lib/worker/message.js +42 -0
- package/lib/worker/push-notification.d.ts +1 -0
- package/lib/worker/push-notification.js +87 -0
- package/lib/worker/twilio.d.ts +1 -0
- package/lib/worker/twilio.js +59 -0
- package/lib/worker/update-coinbase-rates.d.ts +1 -0
- package/lib/worker/update-coinbase-rates.js +59 -0
- package/lib/worker/update-ecb-rates.d.ts +1 -0
- package/lib/worker/update-ecb-rates.js +92 -0
- package/lib/worker/update-token-ownership.d.ts +3 -0
- package/lib/worker/update-token-ownership.js +57 -0
- package/lib/worker/zombie-killer.d.ts +13 -0
- package/lib/worker/zombie-killer.js +91 -0
- package/package.json +10 -15
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ProductPricingDirector, ProductPricingAdapter, } from '@unchainedshop/core';
|
|
2
|
+
export const ProductPriceRateConversion = {
|
|
3
|
+
...ProductPricingAdapter,
|
|
4
|
+
key: 'shop.unchained.pricing.rate-conversion',
|
|
5
|
+
version: '1.0.0',
|
|
6
|
+
label: 'Generic rate conversion plugin that converts between rates if entry in DB exists.',
|
|
7
|
+
orderIndex: 10,
|
|
8
|
+
isActivatedFor: () => {
|
|
9
|
+
return true;
|
|
10
|
+
},
|
|
11
|
+
actions: (params) => {
|
|
12
|
+
const pricingAdapter = ProductPricingAdapter.actions(params);
|
|
13
|
+
const { modules } = params.context;
|
|
14
|
+
return {
|
|
15
|
+
...pricingAdapter,
|
|
16
|
+
calculate: async () => {
|
|
17
|
+
const { product, countryCode, quantity, currencyCode: targetCurrency } = params.context;
|
|
18
|
+
const { calculation = [] } = params.calculationSheet;
|
|
19
|
+
if (calculation?.length) {
|
|
20
|
+
return pricingAdapter.calculate();
|
|
21
|
+
}
|
|
22
|
+
const productPrice = await modules.products.prices.price(product, {
|
|
23
|
+
countryCode,
|
|
24
|
+
quantity,
|
|
25
|
+
});
|
|
26
|
+
if (!productPrice) {
|
|
27
|
+
return pricingAdapter.calculate();
|
|
28
|
+
}
|
|
29
|
+
const fromCurrencyObj = await modules.currencies.findCurrency({
|
|
30
|
+
isoCode: productPrice.currencyCode,
|
|
31
|
+
});
|
|
32
|
+
const targetCurrencyObj = await modules.currencies.findCurrency({
|
|
33
|
+
isoCode: targetCurrency,
|
|
34
|
+
});
|
|
35
|
+
if (!targetCurrencyObj?.isActive || !fromCurrencyObj?.isActive)
|
|
36
|
+
return pricingAdapter.calculate();
|
|
37
|
+
const rateData = await modules.products.prices.rates.getRate(fromCurrencyObj, targetCurrencyObj);
|
|
38
|
+
if (rateData && rateData.rate > 0) {
|
|
39
|
+
const convertedAmount = Math.round(productPrice.amount * rateData.rate);
|
|
40
|
+
pricingAdapter.resultSheet().resetCalculation(params.calculationSheet);
|
|
41
|
+
pricingAdapter.resultSheet().addItem({
|
|
42
|
+
amount: convertedAmount * quantity,
|
|
43
|
+
isTaxable: Boolean(productPrice?.isTaxable),
|
|
44
|
+
isNetPrice: Boolean(productPrice?.isNetPrice),
|
|
45
|
+
meta: { adapter: ProductPriceRateConversion.key, rate: rateData.rate },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return pricingAdapter.calculate();
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
ProductPricingDirector.registerAdapter(ProductPriceRateConversion);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type IProductPricingAdapter } from '@unchainedshop/core';
|
|
2
|
+
interface PriceRoundSettings {
|
|
3
|
+
defaultPrecision: number;
|
|
4
|
+
roundTo: (value: number, precision: number, currencyCode: string) => number;
|
|
5
|
+
}
|
|
6
|
+
export declare const ProductRound: IProductPricingAdapter & {
|
|
7
|
+
configure: (params: PriceRoundSettings) => void;
|
|
8
|
+
settings: PriceRoundSettings;
|
|
9
|
+
};
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ProductPricingAdapter, ProductPricingDirector, } from '@unchainedshop/core';
|
|
2
|
+
export const ProductRound = {
|
|
3
|
+
...ProductPricingAdapter,
|
|
4
|
+
key: 'shop.unchained.pricing.product-round',
|
|
5
|
+
version: '1.0.0',
|
|
6
|
+
label: 'Round product price',
|
|
7
|
+
orderIndex: 90,
|
|
8
|
+
isActivatedFor: () => {
|
|
9
|
+
return true;
|
|
10
|
+
},
|
|
11
|
+
settings: {
|
|
12
|
+
defaultPrecision: 5,
|
|
13
|
+
roundTo: (value, precision) => Math.round(value / precision) * precision,
|
|
14
|
+
},
|
|
15
|
+
configure({ defaultPrecision, roundTo }) {
|
|
16
|
+
if (defaultPrecision)
|
|
17
|
+
this.settings.defaultPrecision = defaultPrecision;
|
|
18
|
+
if (roundTo)
|
|
19
|
+
this.settings.roundTo = roundTo;
|
|
20
|
+
},
|
|
21
|
+
actions: (params) => {
|
|
22
|
+
const pricingAdapter = ProductPricingAdapter.actions(params);
|
|
23
|
+
return {
|
|
24
|
+
...pricingAdapter,
|
|
25
|
+
calculate: async () => {
|
|
26
|
+
const { currencyCode } = params.context;
|
|
27
|
+
const { defaultPrecision, roundTo } = ProductRound.settings;
|
|
28
|
+
const { calculation = [] } = params.calculationSheet;
|
|
29
|
+
if (calculation?.length) {
|
|
30
|
+
pricingAdapter.resultSheet().resetCalculation(params.calculationSheet);
|
|
31
|
+
calculation.forEach((item) => {
|
|
32
|
+
const newAmount = roundTo(item.amount, defaultPrecision, currencyCode);
|
|
33
|
+
pricingAdapter.resultSheet().calculation.push({
|
|
34
|
+
...item,
|
|
35
|
+
amount: newAmount,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return pricingAdapter.calculate();
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
ProductPricingDirector.registerAdapter(ProductRound);
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ProductPricingDirector, ProductPricingAdapter, ProductPricingRowCategory, } from '@unchainedshop/core';
|
|
2
|
+
import { resolveTaxCategoryFromDeliveryProvider, resolveTaxCategoryFromProduct, SwissTaxCategories, } from "./tax/ch.js";
|
|
3
|
+
import isDeliveryAddressInCountry from "./utils/isDeliveryAddressInCountry.js";
|
|
4
|
+
export const ProductSwissTax = {
|
|
5
|
+
...ProductPricingAdapter,
|
|
6
|
+
key: 'shop.unchained.pricing.product-swiss-tax',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
label: 'Apply Swiss Tax on Product',
|
|
9
|
+
orderIndex: 80,
|
|
10
|
+
isActivatedFor: () => {
|
|
11
|
+
return true;
|
|
12
|
+
},
|
|
13
|
+
actions: (params) => {
|
|
14
|
+
const pricingAdapter = ProductPricingAdapter.actions(params);
|
|
15
|
+
const { context } = params;
|
|
16
|
+
return {
|
|
17
|
+
...pricingAdapter,
|
|
18
|
+
calculate: async () => {
|
|
19
|
+
const orderDelivery = context.order?.deliveryId
|
|
20
|
+
? await context.modules.orders.deliveries.findDelivery({
|
|
21
|
+
orderDeliveryId: context.order?.deliveryId,
|
|
22
|
+
})
|
|
23
|
+
: null;
|
|
24
|
+
if (!isDeliveryAddressInCountry({
|
|
25
|
+
...context,
|
|
26
|
+
orderDelivery,
|
|
27
|
+
}, ['CH', 'LI'])) {
|
|
28
|
+
return pricingAdapter.calculate();
|
|
29
|
+
}
|
|
30
|
+
let taxCategory = resolveTaxCategoryFromProduct(context.product);
|
|
31
|
+
if (!taxCategory) {
|
|
32
|
+
const provider = orderDelivery?.deliveryProviderId
|
|
33
|
+
? await context.modules.delivery.findProvider({
|
|
34
|
+
deliveryProviderId: orderDelivery?.deliveryProviderId,
|
|
35
|
+
})
|
|
36
|
+
: null;
|
|
37
|
+
if (provider)
|
|
38
|
+
taxCategory = resolveTaxCategoryFromDeliveryProvider(provider);
|
|
39
|
+
}
|
|
40
|
+
if (!taxCategory)
|
|
41
|
+
taxCategory = SwissTaxCategories.DEFAULT;
|
|
42
|
+
const taxRate = taxCategory.rate(context.order?.ordered);
|
|
43
|
+
ProductPricingAdapter.log(`ProductSwissTax -> Tax Multiplicator: ${taxRate}`);
|
|
44
|
+
params.calculationSheet.filterBy({ isTaxable: true }).forEach(({ isNetPrice, ...row }) => {
|
|
45
|
+
if (!isNetPrice) {
|
|
46
|
+
const taxAmount = row.amount - row.amount / (1 + taxRate);
|
|
47
|
+
pricingAdapter.resultSheet().calculation.push({
|
|
48
|
+
...row,
|
|
49
|
+
amount: -taxAmount,
|
|
50
|
+
isTaxable: false,
|
|
51
|
+
isNetPrice: false,
|
|
52
|
+
meta: { adapter: ProductSwissTax.key },
|
|
53
|
+
});
|
|
54
|
+
pricingAdapter.resultSheet().addTax({
|
|
55
|
+
amount: taxAmount,
|
|
56
|
+
rate: taxRate,
|
|
57
|
+
baseCategory: ProductPricingRowCategory.Item,
|
|
58
|
+
meta: { adapter: ProductSwissTax.key },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const taxAmount = row.amount * taxRate;
|
|
63
|
+
pricingAdapter.resultSheet().addTax({
|
|
64
|
+
amount: taxAmount,
|
|
65
|
+
rate: taxRate,
|
|
66
|
+
baseCategory: ProductPricingRowCategory.Item,
|
|
67
|
+
meta: { adapter: ProductSwissTax.key },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return pricingAdapter.calculate();
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
ProductPricingDirector.registerAdapter(ProductSwissTax);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { QuotationDirector, QuotationAdapter } from '@unchainedshop/core';
|
|
2
|
+
const ManualOffering = {
|
|
3
|
+
...QuotationAdapter,
|
|
4
|
+
key: 'shop.unchained.quotations.manual',
|
|
5
|
+
version: '1.0.0',
|
|
6
|
+
label: 'Manual Offerings',
|
|
7
|
+
orderIndex: 0,
|
|
8
|
+
isActivatedFor: () => true,
|
|
9
|
+
actions: (params) => {
|
|
10
|
+
return {
|
|
11
|
+
...QuotationAdapter.actions(params),
|
|
12
|
+
quote: async () => {
|
|
13
|
+
return {
|
|
14
|
+
expires: new Date(new Date().getTime() + 3600 * 1000),
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
QuotationDirector.registerAdapter(ManualOffering);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { WarehousingError, WarehousingDirector, WarehousingAdapter, } from '@unchainedshop/core';
|
|
2
|
+
import { WarehousingProviderType } from '@unchainedshop/core-warehousing';
|
|
3
|
+
import { ProductContractStandard, ProductType } from '@unchainedshop/core-products';
|
|
4
|
+
import { systemLocale } from '@unchainedshop/utils';
|
|
5
|
+
import { generateDbObjectId } from '@unchainedshop/mongodb';
|
|
6
|
+
import { getFileAdapter } from '@unchainedshop/core-files';
|
|
7
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
8
|
+
const logger = createLogger('unchained:eth-minter');
|
|
9
|
+
const ETHMinter = {
|
|
10
|
+
...WarehousingAdapter,
|
|
11
|
+
key: 'shop.unchained.warehousing.infinite-minter',
|
|
12
|
+
version: '1.0',
|
|
13
|
+
label: 'Infinite Minter',
|
|
14
|
+
orderIndex: 0,
|
|
15
|
+
initialConfiguration: [{ key: 'chainId', value: '0' }],
|
|
16
|
+
typeSupported: (type) => {
|
|
17
|
+
return type === WarehousingProviderType.VIRTUAL;
|
|
18
|
+
},
|
|
19
|
+
actions: (configuration, context) => {
|
|
20
|
+
const { MINTER_TOKEN_OFFSET = '0', ROOT_URL = 'http://localhost:4010' } = process.env;
|
|
21
|
+
const { product, orderPosition, token, modules, locale } = context;
|
|
22
|
+
const { contractAddress, contractStandard, tokenId, supply, ercMetadataProperties } = product?.tokenization || {};
|
|
23
|
+
const getTokensCreated = async () => {
|
|
24
|
+
const existingTokens = await modules.warehousing.findTokens(contractStandard === ProductContractStandard.ERC721
|
|
25
|
+
? { contractAddress: contractStandard }
|
|
26
|
+
: {
|
|
27
|
+
contractAddress: contractStandard,
|
|
28
|
+
tokenSerialNumber: tokenId,
|
|
29
|
+
});
|
|
30
|
+
const tokensCreated = existingTokens.reduce((acc, curToken) => {
|
|
31
|
+
return acc + curToken.quantity;
|
|
32
|
+
}, 0);
|
|
33
|
+
return tokensCreated;
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
...WarehousingAdapter.actions(configuration, context),
|
|
37
|
+
isActive() {
|
|
38
|
+
return product?.type === ProductType.TOKENIZED_PRODUCT;
|
|
39
|
+
},
|
|
40
|
+
configurationError() {
|
|
41
|
+
if (contractStandard === 'ERC1155' && !tokenId) {
|
|
42
|
+
logger.error(`Token ID is required for ERC1155 contract standard (productId: ${product._id})`);
|
|
43
|
+
return WarehousingError.INCOMPLETE_CONFIGURATION;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
},
|
|
47
|
+
stock: async () => {
|
|
48
|
+
const tokensCreated = await getTokensCreated();
|
|
49
|
+
return supply ? supply - tokensCreated : 0;
|
|
50
|
+
},
|
|
51
|
+
tokenize: async () => {
|
|
52
|
+
const chainId = configuration.find(({ key }) => key === 'chainId')?.value || undefined;
|
|
53
|
+
const meta = { contractStandard };
|
|
54
|
+
const tokensCreated = await getTokensCreated();
|
|
55
|
+
if (!orderPosition) {
|
|
56
|
+
throw new Error('Order position not found in context');
|
|
57
|
+
}
|
|
58
|
+
if (contractStandard === 'ERC721') {
|
|
59
|
+
const items = new Array(orderPosition.quantity).fill(null).map((_, i) => ({
|
|
60
|
+
_id: generateDbObjectId(),
|
|
61
|
+
tokenSerialNumber: Number(parseInt(MINTER_TOKEN_OFFSET, 10) + tokensCreated + (i + 1)).toString(),
|
|
62
|
+
contractAddress,
|
|
63
|
+
quantity: 1,
|
|
64
|
+
chainId,
|
|
65
|
+
meta,
|
|
66
|
+
}));
|
|
67
|
+
return items;
|
|
68
|
+
}
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
_id: generateDbObjectId(),
|
|
72
|
+
tokenSerialNumber: tokenId,
|
|
73
|
+
contractAddress,
|
|
74
|
+
quantity: orderPosition.quantity,
|
|
75
|
+
chainId,
|
|
76
|
+
meta,
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
},
|
|
80
|
+
tokenMetadata: async (tokenSerialNumber) => {
|
|
81
|
+
if (!product) {
|
|
82
|
+
throw new Error('Product not found in context');
|
|
83
|
+
}
|
|
84
|
+
const allLanguages = await modules.languages.findLanguages({
|
|
85
|
+
includeInactive: false,
|
|
86
|
+
});
|
|
87
|
+
const [firstMedia] = await modules.products.media.findProductMedias({
|
|
88
|
+
productId: product._id,
|
|
89
|
+
limit: 1,
|
|
90
|
+
});
|
|
91
|
+
const file = firstMedia && (await modules.files.findFile({ fileId: firstMedia.mediaId }));
|
|
92
|
+
const fileAdapter = file && getFileAdapter();
|
|
93
|
+
const signedUrl = await fileAdapter?.createDownloadURL(file);
|
|
94
|
+
const url = signedUrl && (await modules.files.normalizeUrl(signedUrl, {}));
|
|
95
|
+
const text = await modules.products.texts.findLocalizedText({
|
|
96
|
+
productId: product._id,
|
|
97
|
+
locale: locale || systemLocale,
|
|
98
|
+
});
|
|
99
|
+
const name = `${text.title} #${tokenSerialNumber}`;
|
|
100
|
+
const isDefaultLanguageActive = locale ? locale.language === systemLocale.language : true;
|
|
101
|
+
const localization = isDefaultLanguageActive
|
|
102
|
+
? {
|
|
103
|
+
uri: `${ROOT_URL}/erc-metadata/${product._id}/${locale}/${tokenId}.json`,
|
|
104
|
+
default: systemLocale.language,
|
|
105
|
+
locales: allLanguages.map((lang) => lang.isoCode),
|
|
106
|
+
}
|
|
107
|
+
: undefined;
|
|
108
|
+
return {
|
|
109
|
+
name,
|
|
110
|
+
description: text.description,
|
|
111
|
+
image: url,
|
|
112
|
+
properties: ercMetadataProperties,
|
|
113
|
+
localization,
|
|
114
|
+
...(token?.meta || {}),
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
WarehousingDirector.registerAdapter(ETHMinter);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { WarehousingDirector, WarehousingAdapter } from '@unchainedshop/core';
|
|
2
|
+
import { WarehousingProviderType } from '@unchainedshop/core-warehousing';
|
|
3
|
+
const Store = {
|
|
4
|
+
...WarehousingAdapter,
|
|
5
|
+
key: 'shop.unchained.warehousing.store',
|
|
6
|
+
label: 'Store',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
orderIndex: 0,
|
|
9
|
+
initialConfiguration: [{ key: 'name', value: 'Flagship Store' }],
|
|
10
|
+
typeSupported: (type) => {
|
|
11
|
+
return type === WarehousingProviderType.PHYSICAL;
|
|
12
|
+
},
|
|
13
|
+
actions: (configuration, context) => {
|
|
14
|
+
return {
|
|
15
|
+
...WarehousingAdapter.actions(configuration, context),
|
|
16
|
+
isActive() {
|
|
17
|
+
return true;
|
|
18
|
+
},
|
|
19
|
+
configurationError() {
|
|
20
|
+
return null;
|
|
21
|
+
},
|
|
22
|
+
stock: async () => {
|
|
23
|
+
return 99999;
|
|
24
|
+
},
|
|
25
|
+
productionTime: async () => {
|
|
26
|
+
return 0;
|
|
27
|
+
},
|
|
28
|
+
commissioningTime: async () => {
|
|
29
|
+
return 0;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
WarehousingDirector.registerAdapter(Store);
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { WorkerAdapter, WorkerDirector } from '@unchainedshop/core';
|
|
2
|
+
const { BUDGETSMS_USERNAME, BUDGETSMS_USERID, BUDGETSMS_HANDLE } = process.env;
|
|
3
|
+
const BudgetSMSWorker = {
|
|
4
|
+
...WorkerAdapter,
|
|
5
|
+
key: 'shop.unchained.worker-plugin.budgetsms',
|
|
6
|
+
label: 'Send Messages through BudgetSMS',
|
|
7
|
+
version: '1.0.0',
|
|
8
|
+
type: 'BUDGETSMS',
|
|
9
|
+
doWork: async ({ from, to, text, test = false, customid, price = false, mccmnc = false, credit = false, ...params }) => {
|
|
10
|
+
try {
|
|
11
|
+
if (!BUDGETSMS_USERNAME || !BUDGETSMS_USERID || !BUDGETSMS_HANDLE) {
|
|
12
|
+
throw new Error('Missing BudgetSMS credentials. Please set BUDGETSMS_USERNAME, BUDGETSMS_USERID, and BUDGETSMS_HANDLE');
|
|
13
|
+
}
|
|
14
|
+
const username = BUDGETSMS_USERNAME.trim();
|
|
15
|
+
const userid = BUDGETSMS_USERID.trim();
|
|
16
|
+
const handle = BUDGETSMS_HANDLE.trim();
|
|
17
|
+
if (!/^\d+$/.test(userid)) {
|
|
18
|
+
throw new Error('BUDGETSMS_USERID must be numeric only. Found: ' + userid);
|
|
19
|
+
}
|
|
20
|
+
const url = test ? 'https://api.budgetsms.net/testsms/' : 'https://api.budgetsms.net/sendsms/';
|
|
21
|
+
const formattedTo = to.replace(/^\+|^00/, '').replace(/[\s-]/g, '');
|
|
22
|
+
const queryParams = new URLSearchParams({
|
|
23
|
+
username: username,
|
|
24
|
+
userid: userid,
|
|
25
|
+
handle: handle,
|
|
26
|
+
msg: text || '',
|
|
27
|
+
from: from || '',
|
|
28
|
+
to: formattedTo,
|
|
29
|
+
...(customid && { customid }),
|
|
30
|
+
...(!test && price && { price: '1' }),
|
|
31
|
+
...(!test && mccmnc && { mccmnc: '1' }),
|
|
32
|
+
...(!test && credit && { credit: '1' }),
|
|
33
|
+
...params,
|
|
34
|
+
});
|
|
35
|
+
const fullUrl = `${url}?${queryParams.toString()}`;
|
|
36
|
+
const response = await fetch(fullUrl, {
|
|
37
|
+
method: 'GET',
|
|
38
|
+
headers: {
|
|
39
|
+
Accept: 'text/plain',
|
|
40
|
+
'User-Agent': 'Unchained/BudgetSMS-Plugin',
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const responseText = await response.text().then((text) => text.trim());
|
|
44
|
+
if (responseText.startsWith('OK')) {
|
|
45
|
+
const parts = responseText.split(' ');
|
|
46
|
+
const result = {
|
|
47
|
+
sms_id: parts[1] || null,
|
|
48
|
+
status: test ? 'test_successful' : 'sent',
|
|
49
|
+
test_mode: test,
|
|
50
|
+
};
|
|
51
|
+
if (test) {
|
|
52
|
+
result.message = 'Test SMS validated successfully (no credit deducted)';
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
let index = 2;
|
|
56
|
+
if (price && parts[index] && parts[index + 1]) {
|
|
57
|
+
result.price = parseFloat(parts[index]);
|
|
58
|
+
result.parts = parseInt(parts[index + 1]);
|
|
59
|
+
index += 2;
|
|
60
|
+
}
|
|
61
|
+
if (mccmnc && parts[index]) {
|
|
62
|
+
result.mccmnc = parts[index];
|
|
63
|
+
index += 1;
|
|
64
|
+
}
|
|
65
|
+
if (credit && parts[index]) {
|
|
66
|
+
result.remaining_credit = parseFloat(parts[index]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
result,
|
|
72
|
+
error: null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (responseText.startsWith('ERR')) {
|
|
76
|
+
const parts = responseText.split(' ');
|
|
77
|
+
const errorCode = parts[1] || 'UNKNOWN';
|
|
78
|
+
const errorMessages = {
|
|
79
|
+
'1001': 'Authentication failed OR insufficient credit (BudgetSMS returns 1001 for both)',
|
|
80
|
+
'1002': 'Account not active',
|
|
81
|
+
'1003': 'Insufficient credit',
|
|
82
|
+
'1004': 'No access to this API',
|
|
83
|
+
'1005': 'No handle provided',
|
|
84
|
+
'1006': 'No UserID provided',
|
|
85
|
+
'1007': 'No Username provided',
|
|
86
|
+
'2001': 'SMS message text is empty',
|
|
87
|
+
'2002': 'SMS numeric senderid can be max. 16 numbers',
|
|
88
|
+
'2003': 'SMS alphanumeric sender can be max. 11 characters',
|
|
89
|
+
'2004': 'SMS senderid is empty or invalid',
|
|
90
|
+
'2005': 'Destination number is too short',
|
|
91
|
+
'2006': 'Destination is not numeric',
|
|
92
|
+
'2007': 'Destination is empty',
|
|
93
|
+
'2008': 'SMS text is not OK (check encoding?)',
|
|
94
|
+
'2009': 'Parameter issue',
|
|
95
|
+
'2010': 'Destination number is invalidly formatted',
|
|
96
|
+
'2011': 'Destination is invalid',
|
|
97
|
+
'2012': 'SMS message text is too long',
|
|
98
|
+
'2013': 'SMS message is invalid',
|
|
99
|
+
'2014': 'SMS CustomID is used before',
|
|
100
|
+
'2015': 'Charset problem',
|
|
101
|
+
'2016': 'Invalid UTF-8 encoding',
|
|
102
|
+
'3001': 'No route to destination',
|
|
103
|
+
'3002': 'No routes are setup',
|
|
104
|
+
'3003': 'Invalid destination',
|
|
105
|
+
'4001': 'System error, related to customid',
|
|
106
|
+
'4002': 'System error, temporary issue',
|
|
107
|
+
};
|
|
108
|
+
let message = errorMessages[errorCode] || `Unknown error code: ${errorCode}`;
|
|
109
|
+
if (errorCode === '1001') {
|
|
110
|
+
message += '. Note: Check your account balance - BudgetSMS may return 1001 when credit is 0.';
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
success: false,
|
|
114
|
+
error: {
|
|
115
|
+
name: 'BUDGETSMS_ERROR',
|
|
116
|
+
code: errorCode,
|
|
117
|
+
message,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
success: false,
|
|
123
|
+
error: {
|
|
124
|
+
name: 'BUDGETSMS_ERROR',
|
|
125
|
+
message: 'Unexpected response format from BudgetSMS API',
|
|
126
|
+
response: responseText,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
error: {
|
|
134
|
+
name: err.name,
|
|
135
|
+
message: err.message,
|
|
136
|
+
stack: err.stack,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
WorkerDirector.registerAdapter(BudgetSMSWorker);
|
|
143
|
+
export async function checkBudgetSmsCredentials() {
|
|
144
|
+
const url = 'https://api.budgetsms.net/checkcredit/';
|
|
145
|
+
const username = process.env.BUDGETSMS_USERNAME?.trim();
|
|
146
|
+
const userid = process.env.BUDGETSMS_USERID?.trim();
|
|
147
|
+
const handle = process.env.BUDGETSMS_HANDLE?.trim();
|
|
148
|
+
if (!username || !userid || !handle) {
|
|
149
|
+
return { error: 'Missing credentials' };
|
|
150
|
+
}
|
|
151
|
+
if (!/^\d+$/.test(userid)) {
|
|
152
|
+
return { error: 'UserID must be numeric only' };
|
|
153
|
+
}
|
|
154
|
+
const queryParams = new URLSearchParams({ username, userid, handle });
|
|
155
|
+
try {
|
|
156
|
+
const response = await fetch(`${url}?${queryParams.toString()}`, { method: 'GET' });
|
|
157
|
+
const text = await response.text();
|
|
158
|
+
if (text.startsWith('OK')) {
|
|
159
|
+
const credit = parseFloat(text.split(' ')[1]) || 0;
|
|
160
|
+
return { success: true, credit };
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
return { error: text };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
return { error: err.message };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { WorkerDirector, WorkerAdapter } from '@unchainedshop/core';
|
|
2
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
3
|
+
const isSupportedLocale = (locale) => {
|
|
4
|
+
try {
|
|
5
|
+
const dtf = new Intl.DateTimeFormat(locale);
|
|
6
|
+
const resolved = dtf.resolvedOptions().locale;
|
|
7
|
+
return resolved.toLowerCase() === locale.toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
console.error(e);
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const createLanguageDialectList = (languages, countries) => {
|
|
15
|
+
const result = new Set();
|
|
16
|
+
(languages || []).forEach(({ isoCode: baseIsoCode }) => {
|
|
17
|
+
result.add(baseIsoCode);
|
|
18
|
+
(countries || []).forEach(({ isoCode: countryIsoCode }) => {
|
|
19
|
+
const dialectIsoCode = [baseIsoCode, countryIsoCode.toUpperCase()].join('-');
|
|
20
|
+
if (isSupportedLocale(dialectIsoCode))
|
|
21
|
+
result.add(dialectIsoCode);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
return Array.from(result);
|
|
25
|
+
};
|
|
26
|
+
const logger = createLogger('unchained:bulk-export');
|
|
27
|
+
export const BulkExportWorker = {
|
|
28
|
+
...WorkerAdapter,
|
|
29
|
+
key: 'shop.unchained.worker-plugin.bulk-export',
|
|
30
|
+
label: 'Bulk Export',
|
|
31
|
+
version: '2.1.0',
|
|
32
|
+
type: 'BULK_EXPORT',
|
|
33
|
+
maxParallelAllocations: 1,
|
|
34
|
+
doWork: async (payload, unchainedAPI) => {
|
|
35
|
+
try {
|
|
36
|
+
const { modules } = unchainedAPI;
|
|
37
|
+
const languages = await modules.languages.findLanguages({ includeInactive: false });
|
|
38
|
+
const countries = await modules.countries.findCountries({ includeInactive: false });
|
|
39
|
+
const bulkExporter = unchainedAPI.bulkExporter.createBulkExporter({ entity: payload.type });
|
|
40
|
+
await bulkExporter.validate(payload);
|
|
41
|
+
const locales = createLanguageDialectList(languages, countries);
|
|
42
|
+
const [result, error] = await bulkExporter.execute(payload, locales, unchainedAPI);
|
|
43
|
+
if (error) {
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
result: result,
|
|
47
|
+
error,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
success: true,
|
|
52
|
+
result,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
logger.error(err);
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
error: { name: err.name, message: err.message, stack: err.stack },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
WorkerDirector.registerAdapter(BulkExportWorker);
|