@unchainedshop/plugins 1.2.40 → 2.0.0-beta2
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/files/gridfs/gridfs-webhook.js +1 -1
- package/lib/files/gridfs/gridfs-webhook.js.map +1 -1
- package/lib/files/minio/minio-webhook.js +1 -1
- package/lib/files/minio/minio-webhook.js.map +1 -1
- package/lib/payment/apple-iap/adapter.js +1 -1
- package/lib/payment/apple-iap/adapter.js.map +1 -1
- package/lib/payment/bity/adapter.js +2 -2
- package/lib/payment/bity/adapter.js.map +1 -1
- package/lib/payment/cryptopay/db/CryptopayTransactions.d.ts +13 -0
- package/lib/payment/cryptopay/db/CryptopayTransactions.js +4 -0
- package/lib/payment/cryptopay/db/CryptopayTransactions.js.map +1 -0
- package/lib/payment/cryptopay/index.d.ts +5 -0
- package/lib/payment/cryptopay/index.js +6 -0
- package/lib/payment/cryptopay/index.js.map +1 -0
- package/lib/payment/{cryptopay.d.ts → cryptopay/middleware.d.ts} +0 -0
- package/lib/payment/cryptopay/middleware.js +79 -0
- package/lib/payment/cryptopay/middleware.js.map +1 -0
- package/lib/payment/cryptopay/module/configureCryptopayModule.d.ts +25 -0
- package/lib/payment/cryptopay/module/configureCryptopayModule.js +75 -0
- package/lib/payment/cryptopay/module/configureCryptopayModule.js.map +1 -0
- package/lib/payment/cryptopay/plugin.d.ts +3 -0
- package/lib/payment/cryptopay/plugin.js +201 -0
- package/lib/payment/cryptopay/plugin.js.map +1 -0
- package/lib/payment/datatrans-v2/middleware.js +1 -1
- package/lib/payment/datatrans-v2/middleware.js.map +1 -1
- package/lib/payment/postfinance-checkout/middleware.js +1 -1
- package/lib/payment/postfinance-checkout/middleware.js.map +1 -1
- package/lib/pricing/product-catalog-price.js.map +1 -1
- package/lib/pricing/product-price-rateconversion.js +23 -17
- package/lib/pricing/product-price-rateconversion.js.map +1 -1
- package/lib/pricing/product-price-round.js +3 -4
- package/lib/pricing/product-price-round.js.map +1 -1
- package/lib/pricing/utils/cache.d.ts +2 -1
- package/lib/warehousing/google-sheets.js +3 -2
- package/lib/warehousing/google-sheets.js.map +1 -1
- package/lib/{pricing/product-price-ECB-interbank-exchange.d.ts → warehousing/infinite-minter.d.ts} +0 -0
- package/lib/warehousing/infinite-minter.js +50 -0
- package/lib/warehousing/infinite-minter.js.map +1 -0
- package/lib/warehousing/store.js +2 -1
- package/lib/warehousing/store.js.map +1 -1
- package/lib/worker/BulkImportWorker.js +2 -3
- package/lib/worker/BulkImportWorker.js.map +1 -1
- package/lib/worker/ZombieKillerWorker.js +2 -3
- package/lib/worker/ZombieKillerWorker.js.map +1 -1
- package/lib/worker/email.js.map +1 -1
- package/lib/worker/export-token.d.ts +5 -0
- package/lib/worker/export-token.js +26 -0
- package/lib/worker/export-token.js.map +1 -0
- package/lib/worker/external.js +1 -0
- package/lib/worker/external.js.map +1 -1
- package/lib/{pricing/product-price-coinbase-exchange.d.ts → worker/update-coinbase-rates.d.ts} +0 -0
- package/lib/worker/update-coinbase-rates.js +69 -0
- package/lib/worker/update-coinbase-rates.js.map +1 -0
- package/lib/worker/update-ecb-rates.d.ts +1 -0
- package/lib/worker/update-ecb-rates.js +79 -0
- package/lib/worker/update-ecb-rates.js.map +1 -0
- package/lib/worker/update-token-ownership.d.ts +6 -0
- package/lib/worker/update-token-ownership.js +66 -0
- package/lib/worker/update-token-ownership.js.map +1 -0
- package/package.json +22 -19
- package/src/files/gridfs/gridfs-webhook.ts +1 -1
- package/src/files/minio/minio-webhook.js +1 -1
- package/src/payment/apple-iap/adapter.ts +1 -1
- package/src/payment/bity/adapter.ts +2 -2
- package/src/payment/cryptopay/db/CryptopayTransactions.ts +17 -0
- package/src/payment/cryptopay/index.ts +8 -0
- package/src/payment/cryptopay/middleware.ts +95 -0
- package/src/payment/cryptopay/module/configureCryptopayModule.ts +133 -0
- package/src/payment/cryptopay/plugin.ts +289 -0
- package/src/payment/cryptopay/readme.md +1 -0
- package/src/payment/datatrans-v2/middleware.ts +1 -1
- package/src/payment/postfinance-checkout/middleware.ts +1 -1
- package/src/pricing/product-catalog-price.ts +1 -0
- package/src/pricing/product-price-rateconversion.ts +25 -28
- package/src/pricing/product-price-round.ts +4 -5
- package/src/warehousing/google-sheets.ts +4 -2
- package/src/warehousing/infinite-minter.ts +66 -0
- package/src/warehousing/store.ts +3 -1
- package/src/worker/BulkImportWorker.ts +2 -3
- package/src/worker/ZombieKillerWorker.ts +2 -3
- package/src/worker/email.ts +0 -1
- package/src/worker/export-token.ts +33 -0
- package/src/worker/external.ts +1 -0
- package/src/worker/update-coinbase-rates.ts +91 -0
- package/src/worker/update-ecb-rates.ts +97 -0
- package/src/worker/update-token-ownership.ts +85 -0
- package/lib/payment/cryptopay.js +0 -161
- package/lib/payment/cryptopay.js.map +0 -1
- package/lib/pricing/product-price-ECB-interbank-exchange.js +0 -90
- package/lib/pricing/product-price-ECB-interbank-exchange.js.map +0 -1
- package/lib/pricing/product-price-coinbase-exchange.js +0 -60
- package/lib/pricing/product-price-coinbase-exchange.js.map +0 -1
- package/lib/pricing/product-price-cryptopay.d.ts +0 -2
- package/lib/pricing/product-price-cryptopay.js +0 -23
- package/lib/pricing/product-price-cryptopay.js.map +0 -1
- package/src/payment/cryptopay.ts +0 -197
- package/src/pricing/product-price-ECB-interbank-exchange.ts +0 -108
- package/src/pricing/product-price-coinbase-exchange.ts +0 -82
- package/src/pricing/product-price-cryptopay.ts +0 -27
package/src/payment/cryptopay.ts
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import { Context } from '@unchainedshop/types/api';
|
|
2
|
-
import { IPaymentAdapter } from '@unchainedshop/types/payments';
|
|
3
|
-
import bodyParser from 'body-parser';
|
|
4
|
-
import { useMiddlewareWithCurrentContext } from '@unchainedshop/api';
|
|
5
|
-
import { PaymentAdapter, PaymentDirector, PaymentError } from '@unchainedshop/core-payment';
|
|
6
|
-
import { OrderPricingSheet } from '@unchainedshop/core-orders';
|
|
7
|
-
import { ethers } from 'ethers';
|
|
8
|
-
import BIP32Factory from 'bip32';
|
|
9
|
-
import * as ecc from 'tiny-secp256k1';
|
|
10
|
-
import * as bitcoin from 'bitcoinjs-lib';
|
|
11
|
-
import { createLogger } from '@unchainedshop/logger';
|
|
12
|
-
|
|
13
|
-
const logger = createLogger('unchained:core-payment:cryptopay');
|
|
14
|
-
|
|
15
|
-
const {
|
|
16
|
-
CRYPTOPAY_SECRET,
|
|
17
|
-
CRYPTOPAY_WEBHOOK_PATH = '/payment/cryptopay',
|
|
18
|
-
CRYPTOPAY_BTC_XPUB,
|
|
19
|
-
CRYPTOPAY_ETH_XPUB,
|
|
20
|
-
CRYPTOPAY_BTC_TESTNET = false,
|
|
21
|
-
CRYPTOPAY_MAX_RATE_AGE = '360', // seconds
|
|
22
|
-
CRYPTOPAY_MAX_CONV_DIFF = '0.01',
|
|
23
|
-
} = process.env;
|
|
24
|
-
|
|
25
|
-
enum CryptopayCurrencies { // eslint-disable-line
|
|
26
|
-
BTC = 'BTC',
|
|
27
|
-
ETH = 'ETH',
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const MAX_RATE_AGE = parseInt(CRYPTOPAY_MAX_RATE_AGE, 10);
|
|
31
|
-
const MAX_ALLOWED_DIFF = parseFloat(CRYPTOPAY_MAX_CONV_DIFF); // Accept payments when the converted amount differs by adapterActions much (in percent)
|
|
32
|
-
|
|
33
|
-
export default (app) => {
|
|
34
|
-
useMiddlewareWithCurrentContext(app, CRYPTOPAY_WEBHOOK_PATH, bodyParser.json());
|
|
35
|
-
|
|
36
|
-
useMiddlewareWithCurrentContext(app, CRYPTOPAY_WEBHOOK_PATH, async (request, response) => {
|
|
37
|
-
// Return a 200 response to acknowledge receipt of the event
|
|
38
|
-
const resolvedContext = request.unchainedContext as Context;
|
|
39
|
-
const { currency, contract, decimals, address, amount, secret } = request.body;
|
|
40
|
-
if (secret !== CRYPTOPAY_SECRET) {
|
|
41
|
-
logger.warn(`Cryptopay Plugin: Invalid Cryptopay Secret provided`);
|
|
42
|
-
response.end(JSON.stringify({ success: false }));
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const orderPayment = await resolvedContext.modules.orders.payments.findOrderPaymentByContextData({
|
|
46
|
-
context: { cryptoAddresses: { currency, address } },
|
|
47
|
-
});
|
|
48
|
-
if (orderPayment) {
|
|
49
|
-
if (currency === CryptopayCurrencies.ETH && contract && contract !== '') {
|
|
50
|
-
const ERC20CurrencyCount = await resolvedContext.modules.currencies.count({
|
|
51
|
-
includeInactive: false,
|
|
52
|
-
contractAddress: contract,
|
|
53
|
-
});
|
|
54
|
-
if (!ERC20CurrencyCount) {
|
|
55
|
-
logger.warn(`Cryptopay Plugin: ERC20 token address ${contract} not whitelisted.`);
|
|
56
|
-
response.end(JSON.stringify({ success: false }));
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const order = await resolvedContext.modules.orders.findOrder({
|
|
61
|
-
orderId: orderPayment.orderId,
|
|
62
|
-
});
|
|
63
|
-
const pricing = OrderPricingSheet({
|
|
64
|
-
calculation: order.calculation,
|
|
65
|
-
currency: order.currency,
|
|
66
|
-
});
|
|
67
|
-
const totalAmount = pricing?.total({ useNetPrice: false }).amount;
|
|
68
|
-
let convertedAmount: number;
|
|
69
|
-
if (order.currency === currency) {
|
|
70
|
-
convertedAmount = amount / 10 ** (decimals - 8); // All crypto native prices denoted with 8 decimals
|
|
71
|
-
} else {
|
|
72
|
-
// Need to convert
|
|
73
|
-
const rate = await resolvedContext.modules.products.prices.rates.getRate(
|
|
74
|
-
order.currency,
|
|
75
|
-
contract && contract !== '' ? contract : currency, // Convert to the smart contract if given
|
|
76
|
-
MAX_RATE_AGE,
|
|
77
|
-
);
|
|
78
|
-
if (rate) {
|
|
79
|
-
// We assume that we are converting to a fiat currency here (with 2 decimals).
|
|
80
|
-
// Paying an order with prices in crypto in another crypto is not supported.
|
|
81
|
-
convertedAmount = Math.round((amount / 10 ** decimals) * rate * 100);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (convertedAmount && convertedAmount >= totalAmount * (1 - MAX_ALLOWED_DIFF)) {
|
|
85
|
-
await resolvedContext.modules.orders.checkout(
|
|
86
|
-
order._id,
|
|
87
|
-
{
|
|
88
|
-
transactionContext: { address },
|
|
89
|
-
paymentContext: { address },
|
|
90
|
-
},
|
|
91
|
-
resolvedContext,
|
|
92
|
-
);
|
|
93
|
-
response.end(JSON.stringify({ success: true }));
|
|
94
|
-
} else {
|
|
95
|
-
logger.warn(
|
|
96
|
-
`Cryptopay Plugin: OrderPayment ${orderPayment._id} not marked as paid. Converted amount is ${convertedAmount}`,
|
|
97
|
-
);
|
|
98
|
-
response.end(JSON.stringify({ success: false }));
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
logger.info(
|
|
102
|
-
`Cryptopay Plugin: No orderPayment with address ${address} and currency ${currency} found`,
|
|
103
|
-
);
|
|
104
|
-
response.end(JSON.stringify({ success: false }));
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const Cryptopay: IPaymentAdapter = {
|
|
110
|
-
...PaymentAdapter,
|
|
111
|
-
|
|
112
|
-
key: 'shop.unchained.payment.cryptopay',
|
|
113
|
-
label: 'Cryptopay',
|
|
114
|
-
version: '1.0',
|
|
115
|
-
|
|
116
|
-
typeSupported(type) {
|
|
117
|
-
return type === 'GENERIC';
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
actions: (params) => {
|
|
121
|
-
const { modules } = params.context;
|
|
122
|
-
|
|
123
|
-
const adapterActions = {
|
|
124
|
-
...PaymentAdapter.actions(params),
|
|
125
|
-
|
|
126
|
-
// eslint-disable-next-line
|
|
127
|
-
configurationError() {
|
|
128
|
-
// eslint-disable-line
|
|
129
|
-
if (!CRYPTOPAY_SECRET) {
|
|
130
|
-
return PaymentError.INCOMPLETE_CONFIGURATION;
|
|
131
|
-
}
|
|
132
|
-
return null;
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
isActive() {
|
|
136
|
-
if (adapterActions.configurationError() === null) return true;
|
|
137
|
-
return false;
|
|
138
|
-
},
|
|
139
|
-
|
|
140
|
-
isPayLaterAllowed() {
|
|
141
|
-
return false;
|
|
142
|
-
},
|
|
143
|
-
|
|
144
|
-
sign: async () => {
|
|
145
|
-
const { orderPayment } = params.paymentContext;
|
|
146
|
-
if (orderPayment?.context.length) {
|
|
147
|
-
// Do not derive address a second time for order payment, return existing address
|
|
148
|
-
const existingAddresses = orderPayment.context.filter((c) => c.currency);
|
|
149
|
-
if (existingAddresses) {
|
|
150
|
-
return JSON.stringify(existingAddresses);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
const cryptoAddresses: { currency: CryptopayCurrencies; address: string }[] = [];
|
|
154
|
-
if (CRYPTOPAY_BTC_XPUB) {
|
|
155
|
-
const network = CRYPTOPAY_BTC_TESTNET ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
|
|
156
|
-
const bip32 = BIP32Factory(ecc);
|
|
157
|
-
const hardenedMaster = bip32.fromBase58(CRYPTOPAY_BTC_XPUB, network);
|
|
158
|
-
const btcDerivationNumber = await modules.orders.payments.countOrderPaymentsByContextData({
|
|
159
|
-
context: { cryptoAddresses: { currency: CryptopayCurrencies.BTC } },
|
|
160
|
-
});
|
|
161
|
-
const child = hardenedMaster.derivePath(`0/${btcDerivationNumber}`);
|
|
162
|
-
cryptoAddresses.push({
|
|
163
|
-
currency: CryptopayCurrencies.BTC,
|
|
164
|
-
address: bitcoin.payments.p2pkh({
|
|
165
|
-
pubkey: child.publicKey,
|
|
166
|
-
network,
|
|
167
|
-
}).address,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
if (CRYPTOPAY_ETH_XPUB) {
|
|
171
|
-
const hardenedMaster = ethers.utils.HDNode.fromExtendedKey(CRYPTOPAY_ETH_XPUB);
|
|
172
|
-
const ethDerivationNumber = await modules.orders.payments.countOrderPaymentsByContextData({
|
|
173
|
-
context: { cryptoAddresses: { currency: CryptopayCurrencies.ETH } },
|
|
174
|
-
});
|
|
175
|
-
cryptoAddresses.push({
|
|
176
|
-
currency: CryptopayCurrencies.ETH,
|
|
177
|
-
address: hardenedMaster.derivePath(`0/${ethDerivationNumber}`).address,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
await modules.orders.payments.updateContext(
|
|
181
|
-
orderPayment._id,
|
|
182
|
-
{ cryptoAddresses },
|
|
183
|
-
params.context,
|
|
184
|
-
);
|
|
185
|
-
return JSON.stringify(cryptoAddresses);
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
charge: async (): Promise<false> => {
|
|
189
|
-
return false;
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return adapterActions;
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
PaymentDirector.registerAdapter(Cryptopay);
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { IProductPricingAdapter } from '@unchainedshop/types/products.pricing';
|
|
2
|
-
import fetch from 'node-fetch';
|
|
3
|
-
import { ProductPricingDirector, ProductPricingAdapter } from '@unchainedshop/core-products';
|
|
4
|
-
import xmlJs from 'xml-js';
|
|
5
|
-
|
|
6
|
-
import Cache from './utils/cache';
|
|
7
|
-
|
|
8
|
-
const CACHE_PERIOD = 60 * 60 * 24; // 1 day
|
|
9
|
-
|
|
10
|
-
const SUPPORTED_CURRENCIES = [
|
|
11
|
-
'USD',
|
|
12
|
-
'JPY',
|
|
13
|
-
'BGN',
|
|
14
|
-
'CZK',
|
|
15
|
-
'DKK',
|
|
16
|
-
'GBP',
|
|
17
|
-
'HUF',
|
|
18
|
-
'PLN',
|
|
19
|
-
'RON',
|
|
20
|
-
'SEK',
|
|
21
|
-
'CHF',
|
|
22
|
-
'ISK',
|
|
23
|
-
'NOK',
|
|
24
|
-
'HRK',
|
|
25
|
-
'RUB',
|
|
26
|
-
'TRY',
|
|
27
|
-
'AUD',
|
|
28
|
-
'BRL',
|
|
29
|
-
'CAD',
|
|
30
|
-
'CNY',
|
|
31
|
-
'HKD',
|
|
32
|
-
'IDR',
|
|
33
|
-
'ILS',
|
|
34
|
-
'INR',
|
|
35
|
-
'KRW',
|
|
36
|
-
'MXN',
|
|
37
|
-
'MYR',
|
|
38
|
-
'NZD',
|
|
39
|
-
'PHP',
|
|
40
|
-
'SGD',
|
|
41
|
-
'THB',
|
|
42
|
-
'ZAR',
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
const cache = new Cache(CACHE_PERIOD);
|
|
46
|
-
|
|
47
|
-
const getEURexchangeRateForCurrency = async (currency) => {
|
|
48
|
-
const response = cache.get(currency, () =>
|
|
49
|
-
fetch(`https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml`, {
|
|
50
|
-
method: 'GET',
|
|
51
|
-
})
|
|
52
|
-
.then((res) => res.text())
|
|
53
|
-
.then((text) => JSON.parse(xmlJs.xml2json(text)))
|
|
54
|
-
.then(
|
|
55
|
-
(json) =>
|
|
56
|
-
json.elements?.[0]?.elements
|
|
57
|
-
.filter((e) => e.name.toLowerCase() === 'cube')[0]
|
|
58
|
-
?.elements[0]?.elements.filter((a) => a.attributes.currency === currency)?.[0]?.attributes,
|
|
59
|
-
),
|
|
60
|
-
);
|
|
61
|
-
return response;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const ProductPriceECBIntraBankExchange: IProductPricingAdapter = {
|
|
65
|
-
...ProductPricingAdapter,
|
|
66
|
-
|
|
67
|
-
key: 'shop.unchained.pricing.price-ECB-intrabank-exchange',
|
|
68
|
-
version: '1.0',
|
|
69
|
-
label: 'Convert EUR to X with current exchange rate',
|
|
70
|
-
orderIndex: 1,
|
|
71
|
-
|
|
72
|
-
isActivatedFor: (context) => {
|
|
73
|
-
return SUPPORTED_CURRENCIES.indexOf(context.currency) !== -1;
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
actions: (params) => {
|
|
77
|
-
const pricingAdapter = ProductPricingAdapter.actions(params);
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
...pricingAdapter,
|
|
81
|
-
|
|
82
|
-
calculate: async () => {
|
|
83
|
-
const { product, country, quantity, currency, modules } = params.context;
|
|
84
|
-
const { calculation = [] } = params.calculationSheet;
|
|
85
|
-
const EURprice = await modules.products.prices.price(product, {
|
|
86
|
-
country,
|
|
87
|
-
currency: 'EUR',
|
|
88
|
-
quantity,
|
|
89
|
-
});
|
|
90
|
-
if (!EURprice || !EURprice?.amount || calculation?.length) return pricingAdapter.calculate();
|
|
91
|
-
|
|
92
|
-
const exchange = await getEURexchangeRateForCurrency(currency);
|
|
93
|
-
const convertedAmount = EURprice.amount * exchange.rate;
|
|
94
|
-
pricingAdapter.resultSheet().resetCalculation(params.calculationSheet);
|
|
95
|
-
pricingAdapter.resultSheet().addItem({
|
|
96
|
-
amount: convertedAmount * quantity,
|
|
97
|
-
isTaxable: EURprice.isTaxable,
|
|
98
|
-
isNetPrice: EURprice.isNetPrice,
|
|
99
|
-
meta: { adapter: ProductPriceECBIntraBankExchange.key },
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
return pricingAdapter.calculate();
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
ProductPricingDirector.registerAdapter(ProductPriceECBIntraBankExchange);
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { IProductPricingAdapter } from '@unchainedshop/types/products.pricing';
|
|
2
|
-
import fetch from 'node-fetch';
|
|
3
|
-
import { ProductPricingDirector, ProductPricingAdapter } from '@unchainedshop/core-products';
|
|
4
|
-
|
|
5
|
-
import Cache from './utils/cache';
|
|
6
|
-
|
|
7
|
-
const CACHE_PERIOD = 60 * 60 * 0.1; // 10 minutes
|
|
8
|
-
const SUPPORTED_CURRENCIES = ['BTC', 'ETH', 'XRP', 'USDT', 'BCH', 'BSV', 'LTC', 'EOS', 'BNB', 'XTZ'];
|
|
9
|
-
const cache = new Cache(CACHE_PERIOD);
|
|
10
|
-
|
|
11
|
-
const getFiatexchangeRateForCrypto = async (base, target) => {
|
|
12
|
-
const { data } = await cache.get(`${base}-${target}`, () =>
|
|
13
|
-
fetch(`https://api.coinbase.com/v2/exchange-rates?currency=${base}`, {
|
|
14
|
-
method: 'GET',
|
|
15
|
-
}).then((res) => res.json()),
|
|
16
|
-
);
|
|
17
|
-
return data?.rates?.[target];
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const ProductPriceCoinbaseExchange: IProductPricingAdapter = {
|
|
21
|
-
...ProductPricingAdapter,
|
|
22
|
-
|
|
23
|
-
key: 'shop.unchained.pricing.price-coinbase-exchange',
|
|
24
|
-
version: '1.0',
|
|
25
|
-
label: 'Convert fiat/crypto to crypto with current exchange rate',
|
|
26
|
-
orderIndex: 1,
|
|
27
|
-
|
|
28
|
-
isActivatedFor: (context) => {
|
|
29
|
-
return SUPPORTED_CURRENCIES.indexOf(context.currency.toUpperCase()) !== -1;
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
actions: (params) => {
|
|
33
|
-
const pricingAdapter = ProductPricingAdapter.actions(params);
|
|
34
|
-
const { services, modules } = params.context;
|
|
35
|
-
return {
|
|
36
|
-
...pricingAdapter,
|
|
37
|
-
|
|
38
|
-
calculate: async () => {
|
|
39
|
-
const { product, country, quantity, currency } = params.context;
|
|
40
|
-
const defaultCurrency = await services.countries.resolveDefaultCurrencyCode(
|
|
41
|
-
{
|
|
42
|
-
isoCode: country,
|
|
43
|
-
},
|
|
44
|
-
params.context,
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
const productPrice = await modules.products.prices.price(product, {
|
|
48
|
-
country,
|
|
49
|
-
currency: defaultCurrency,
|
|
50
|
-
quantity,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const { calculation = [] } = params.calculationSheet;
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
!productPrice ||
|
|
57
|
-
!productPrice?.amount ||
|
|
58
|
-
calculation?.length ||
|
|
59
|
-
defaultCurrency === currency
|
|
60
|
-
)
|
|
61
|
-
return pricingAdapter.calculate();
|
|
62
|
-
|
|
63
|
-
const rate = await getFiatexchangeRateForCrypto(defaultCurrency, currency);
|
|
64
|
-
|
|
65
|
-
if (rate > 0) {
|
|
66
|
-
const convertedAmount = productPrice.amount * rate;
|
|
67
|
-
pricingAdapter.resultSheet().resetCalculation(params.calculationSheet);
|
|
68
|
-
pricingAdapter.resultSheet().addItem({
|
|
69
|
-
amount: convertedAmount * quantity,
|
|
70
|
-
isTaxable: productPrice?.isTaxable,
|
|
71
|
-
isNetPrice: productPrice?.isNetPrice,
|
|
72
|
-
meta: { adapter: ProductPriceCoinbaseExchange.key },
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return pricingAdapter.calculate();
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
ProductPricingDirector.registerAdapter(ProductPriceCoinbaseExchange);
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Context } from '@unchainedshop/types/api';
|
|
2
|
-
import { ProductPriceRate } from '@unchainedshop/types/products.pricing';
|
|
3
|
-
import bodyParser from 'body-parser';
|
|
4
|
-
import { useMiddlewareWithCurrentContext } from '@unchainedshop/api';
|
|
5
|
-
|
|
6
|
-
const { CRYPTOPAY_SECRET, CRYPTOPAY_PRICING_WEBHOOK_PATH = '/pricing/cryptopay' } = process.env;
|
|
7
|
-
|
|
8
|
-
export default (app) => {
|
|
9
|
-
useMiddlewareWithCurrentContext(app, CRYPTOPAY_PRICING_WEBHOOK_PATH, bodyParser.json());
|
|
10
|
-
|
|
11
|
-
useMiddlewareWithCurrentContext(app, CRYPTOPAY_PRICING_WEBHOOK_PATH, async (request, response) => {
|
|
12
|
-
const resolvedContext = request.unchainedContext as Context;
|
|
13
|
-
const { baseCurrency, token, rate, timestamp, secret } = request.body;
|
|
14
|
-
if (secret !== CRYPTOPAY_SECRET) {
|
|
15
|
-
response.end(JSON.stringify({ success: false }));
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const rateData: ProductPriceRate = {
|
|
19
|
-
baseCurrency,
|
|
20
|
-
quoteCurrency: token,
|
|
21
|
-
rate,
|
|
22
|
-
timestamp,
|
|
23
|
-
};
|
|
24
|
-
const success = await resolvedContext.modules.products.prices.rates.updateRate(rateData);
|
|
25
|
-
response.end(JSON.stringify({ success }));
|
|
26
|
-
});
|
|
27
|
-
};
|