@unchainedshop/plugins 3.1.10 → 4.0.0-rc.2
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/delivery/pick-mup.js +0 -2
- package/lib/delivery/pick-mup.js.map +1 -1
- package/lib/files/gridfs/module.d.ts +2 -2
- package/lib/files/gridfs/module.d.ts.map +1 -1
- package/lib/filters/local-search.d.ts.map +1 -1
- package/lib/filters/local-search.js +9 -2
- package/lib/filters/local-search.js.map +1 -1
- package/lib/payment/apple-iap/adapter.js +5 -12
- package/lib/payment/apple-iap/adapter.js.map +1 -1
- package/lib/payment/apple-iap/fix-periods.d.ts.map +1 -1
- package/lib/payment/apple-iap/fix-periods.js +3 -5
- package/lib/payment/apple-iap/fix-periods.js.map +1 -1
- package/lib/payment/apple-iap/handler-express.js +3 -3
- package/lib/payment/apple-iap/handler-express.js.map +1 -1
- package/lib/payment/apple-iap/handler-fastify.js +3 -3
- package/lib/payment/apple-iap/handler-fastify.js.map +1 -1
- package/lib/payment/apple-iap/module.d.ts +2 -2
- package/lib/payment/apple-iap/module.d.ts.map +1 -1
- package/lib/payment/braintree.js +3 -4
- package/lib/payment/braintree.js.map +1 -1
- package/lib/payment/cryptopay/db/CryptopayTransactions.d.ts +1 -2
- package/lib/payment/cryptopay/db/CryptopayTransactions.d.ts.map +1 -1
- package/lib/payment/cryptopay/db/CryptopayTransactions.js.map +1 -1
- package/lib/payment/cryptopay/de.d.ts +2 -0
- package/lib/payment/cryptopay/de.d.ts.map +1 -0
- package/lib/payment/cryptopay/de.js +10 -0
- package/lib/payment/cryptopay/de.js.map +1 -0
- package/lib/payment/cryptopay/denote-amount.d.ts +25 -0
- package/lib/payment/cryptopay/denote-amount.d.ts.map +1 -0
- package/lib/payment/cryptopay/denote-amount.js +31 -0
- package/lib/payment/cryptopay/denote-amount.js.map +1 -0
- package/lib/payment/cryptopay/derive-btc-address.d.ts +3 -0
- package/lib/payment/cryptopay/derive-btc-address.d.ts.map +1 -0
- package/lib/payment/cryptopay/derive-btc-address.js +39 -0
- package/lib/payment/cryptopay/derive-btc-address.js.map +1 -0
- package/lib/payment/cryptopay/derive-btc-address.test copy.d.ts +2 -0
- package/lib/payment/cryptopay/derive-btc-address.test copy.d.ts.map +1 -0
- package/lib/payment/cryptopay/derive-btc-address.test copy.js +10 -0
- package/lib/payment/cryptopay/derive-btc-address.test copy.js.map +1 -0
- package/lib/payment/cryptopay/derive-eth-address.d.ts +3 -0
- package/lib/payment/cryptopay/derive-eth-address.d.ts.map +1 -0
- package/lib/payment/cryptopay/derive-eth-address.js +32 -0
- package/lib/payment/cryptopay/derive-eth-address.js.map +1 -0
- package/lib/payment/cryptopay/handle-webhook.d.ts +26 -0
- package/lib/payment/cryptopay/handle-webhook.d.ts.map +1 -0
- package/lib/payment/cryptopay/handle-webhook.js +61 -0
- package/lib/payment/cryptopay/handle-webhook.js.map +1 -0
- package/lib/payment/cryptopay/handler-express.d.ts.map +1 -1
- package/lib/payment/cryptopay/handler-express.js +3 -56
- package/lib/payment/cryptopay/handler-express.js.map +1 -1
- package/lib/payment/cryptopay/handler-fastify.d.ts.map +1 -1
- package/lib/payment/cryptopay/handler-fastify.js +3 -55
- package/lib/payment/cryptopay/handler-fastify.js.map +1 -1
- package/lib/payment/cryptopay/module.d.ts +26 -26
- package/lib/payment/cryptopay/module.d.ts.map +1 -1
- package/lib/payment/cryptopay/module.js +26 -24
- package/lib/payment/cryptopay/module.js.map +1 -1
- package/lib/payment/cryptopay/plugin.d.ts.map +1 -1
- package/lib/payment/cryptopay/plugin.js +33 -79
- package/lib/payment/cryptopay/plugin.js.map +1 -1
- package/lib/payment/datatrans-v2/api/types.d.ts +41 -41
- package/lib/payment/datatrans-v2/api/types.d.ts.map +1 -1
- package/lib/payment/datatrans-v2/getPaths.d.ts +2 -2
- package/lib/payment/datatrans-v2/getPaths.d.ts.map +1 -1
- package/lib/payment/datatrans-v2/index.js +16 -14
- package/lib/payment/datatrans-v2/index.js.map +1 -1
- package/lib/payment/datatrans-v2/roundedAmountFromOrder.d.ts +1 -1
- package/lib/payment/datatrans-v2/roundedAmountFromOrder.d.ts.map +1 -1
- package/lib/payment/datatrans-v2/roundedAmountFromOrder.js +3 -3
- package/lib/payment/datatrans-v2/roundedAmountFromOrder.js.map +1 -1
- package/lib/payment/paypal-checkout.js +1 -1
- package/lib/payment/paypal-checkout.js.map +1 -1
- package/lib/payment/payrexx/api/index.d.ts +2 -2
- package/lib/payment/payrexx/api/index.d.ts.map +1 -1
- package/lib/payment/payrexx/api/makeFetcher.d.ts.map +1 -1
- package/lib/payment/payrexx/api/makeFetcher.js.map +1 -1
- package/lib/payment/payrexx/index.js +4 -4
- package/lib/payment/payrexx/index.js.map +1 -1
- package/lib/payment/payrexx/payrexx.js +2 -2
- package/lib/payment/payrexx/payrexx.js.map +1 -1
- package/lib/payment/postfinance-checkout/index.js +3 -5
- package/lib/payment/postfinance-checkout/index.js.map +1 -1
- package/lib/payment/postfinance-checkout/types.d.ts +4 -4
- package/lib/payment/postfinance-checkout/types.d.ts.map +1 -1
- package/lib/payment/postfinance-checkout/utils.js +2 -2
- package/lib/payment/postfinance-checkout/utils.js.map +1 -1
- package/lib/payment/saferpay/adapter.d.ts.map +1 -1
- package/lib/payment/saferpay/adapter.js +4 -6
- package/lib/payment/saferpay/adapter.js.map +1 -1
- package/lib/payment/saferpay/api/models/Transaction.d.ts +2 -2
- package/lib/payment/saferpay/api/models/Transaction.d.ts.map +1 -1
- package/lib/payment/saferpay/module.d.ts +2 -2
- package/lib/payment/saferpay/module.d.ts.map +1 -1
- package/lib/payment/stripe/index.js +5 -5
- package/lib/payment/stripe/index.js.map +1 -1
- package/lib/payment/stripe/stripe.js +3 -3
- package/lib/payment/stripe/stripe.js.map +1 -1
- package/lib/presets/all.d.ts +12 -12
- package/lib/presets/all.d.ts.map +1 -1
- package/lib/pricing/delivery-swiss-tax.d.ts.map +1 -1
- package/lib/pricing/delivery-swiss-tax.js +2 -2
- package/lib/pricing/delivery-swiss-tax.js.map +1 -1
- package/lib/pricing/order-delivery.js +1 -1
- package/lib/pricing/order-delivery.js.map +1 -1
- package/lib/pricing/order-discount.js +3 -3
- package/lib/pricing/order-discount.js.map +1 -1
- package/lib/pricing/order-items-discount.js +1 -1
- package/lib/pricing/order-items-discount.js.map +1 -1
- package/lib/pricing/order-items.js +1 -1
- package/lib/pricing/order-items.js.map +1 -1
- package/lib/pricing/order-payment.js +1 -1
- package/lib/pricing/order-payment.js.map +1 -1
- package/lib/pricing/order-round.d.ts +1 -1
- package/lib/pricing/order-round.d.ts.map +1 -1
- package/lib/pricing/order-round.js +8 -8
- package/lib/pricing/order-round.js.map +1 -1
- package/lib/pricing/product-catalog-price.d.ts +0 -1
- package/lib/pricing/product-catalog-price.d.ts.map +1 -1
- package/lib/pricing/product-catalog-price.js +6 -18
- package/lib/pricing/product-catalog-price.js.map +1 -1
- package/lib/pricing/product-price-rateconversion.js +2 -2
- package/lib/pricing/product-price-rateconversion.js.map +1 -1
- package/lib/pricing/product-round.d.ts +1 -1
- package/lib/pricing/product-round.d.ts.map +1 -1
- package/lib/pricing/product-round.js +2 -2
- package/lib/pricing/product-round.js.map +1 -1
- package/lib/pricing/product-swiss-tax.d.ts +1 -1
- package/lib/pricing/product-swiss-tax.d.ts.map +1 -1
- package/lib/pricing/product-swiss-tax.js +2 -2
- package/lib/pricing/product-swiss-tax.js.map +1 -1
- package/lib/warehousing/eth-minter.js +2 -2
- package/lib/warehousing/eth-minter.js.map +1 -1
- package/lib/worker/bulk-import.js +1 -1
- package/lib/worker/bulk-import.js.map +1 -1
- package/lib/worker/enrollment-order-generator.js +1 -1
- package/lib/worker/enrollment-order-generator.js.map +1 -1
- package/lib/worker/error-notifications.d.ts +4 -4
- package/lib/worker/error-notifications.d.ts.map +1 -1
- package/lib/worker/message.d.ts +1 -1
- package/lib/worker/message.d.ts.map +1 -1
- package/lib/worker/sms.js +16 -16
- package/lib/worker/sms.js.map +1 -1
- package/lib/worker/update-coinbase-rates.js +1 -1
- package/lib/worker/update-coinbase-rates.js.map +1 -1
- package/lib/worker/update-ecb-rates.js.map +1 -1
- package/package.json +37 -53
- package/src/delivery/pick-mup.ts +0 -2
- package/src/files/gridfs/module.ts +2 -2
- package/src/filters/local-search.ts +11 -2
- package/src/payment/apple-iap/adapter.ts +6 -13
- package/src/payment/apple-iap/fix-periods.ts +4 -7
- package/src/payment/apple-iap/handler-express.ts +3 -3
- package/src/payment/apple-iap/handler-fastify.ts +3 -3
- package/src/payment/apple-iap/module.ts +2 -2
- package/src/payment/braintree.ts +3 -4
- package/src/payment/cryptopay/db/CryptopayTransactions.ts +1 -2
- package/src/payment/cryptopay/denote-amount.test.ts +28 -0
- package/src/payment/cryptopay/denote-amount.ts +29 -0
- package/src/payment/cryptopay/derive-btc-address.test.ts +22 -0
- package/src/payment/cryptopay/derive-btc-address.ts +48 -0
- package/src/payment/cryptopay/derive-eth-address.test.ts +22 -0
- package/src/payment/cryptopay/derive-eth-address.ts +38 -0
- package/src/payment/cryptopay/handle-webhook.ts +103 -0
- package/src/payment/cryptopay/handler-express.ts +3 -69
- package/src/payment/cryptopay/handler-fastify.ts +5 -70
- package/src/payment/cryptopay/module.ts +44 -40
- package/src/payment/cryptopay/plugin.ts +50 -87
- package/src/payment/cryptopay/readme.md +23 -1
- package/src/payment/datatrans-v2/api/types.ts +41 -41
- package/src/payment/datatrans-v2/getPaths.ts +2 -2
- package/src/payment/datatrans-v2/index.ts +17 -15
- package/src/payment/datatrans-v2/roundedAmountFromOrder.ts +4 -4
- package/src/payment/paypal-checkout.ts +1 -1
- package/src/payment/payrexx/api/index.ts +2 -2
- package/src/payment/payrexx/api/makeFetcher.ts +1 -1
- package/src/payment/payrexx/index.ts +4 -4
- package/src/payment/payrexx/payrexx.ts +2 -2
- package/src/payment/postfinance-checkout/index.ts +3 -5
- package/src/payment/postfinance-checkout/types.ts +6 -6
- package/src/payment/postfinance-checkout/utils.ts +2 -2
- package/src/payment/saferpay/adapter.ts +4 -6
- package/src/payment/saferpay/api/models/Transaction.ts +2 -2
- package/src/payment/saferpay/module.ts +2 -2
- package/src/payment/stripe/index.ts +5 -5
- package/src/payment/stripe/stripe.ts +3 -3
- package/src/pricing/delivery-swiss-tax.ts +6 -2
- package/src/pricing/order-delivery.ts +1 -1
- package/src/pricing/order-discount.ts +3 -3
- package/src/pricing/order-items-discount.ts +1 -1
- package/src/pricing/order-items.ts +1 -1
- package/src/pricing/order-payment.ts +1 -1
- package/src/pricing/order-round.ts +9 -9
- package/src/pricing/product-catalog-price.ts +6 -25
- package/src/pricing/product-price-rateconversion.ts +2 -2
- package/src/pricing/product-round.ts +3 -3
- package/src/pricing/product-swiss-tax.test.ts +6 -6
- package/src/pricing/product-swiss-tax.ts +2 -2
- package/src/warehousing/eth-minter.ts +2 -2
- package/src/worker/bulk-import.ts +1 -1
- package/src/worker/enrollment-order-generator.ts +3 -3
- package/src/worker/error-notifications.ts +4 -4
- package/src/worker/heartbeat.ts +2 -2
- package/src/worker/message.ts +1 -1
- package/src/worker/push-notification.ts +2 -2
- package/src/worker/sms.ts +19 -17
- package/src/worker/update-coinbase-rates.ts +3 -6
- package/src/worker/update-ecb-rates.ts +3 -3
- package/src/worker/BaseWorker.ts +0 -116
- package/src/worker/EventListenerWorker.ts +0 -61
- package/src/worker/FailedRescheduler.ts +0 -80
- package/src/worker/IntervalWorker.ts +0 -54
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { HDKey } from '@scure/bip32';
|
|
2
|
+
import { NETWORK, p2wpkh, TEST_NETWORK } from '@scure/btc-signer';
|
|
3
|
+
|
|
4
|
+
// const resolvePath = (prefix) => {
|
|
5
|
+
// if (prefix === 'x') return `m/44'/0'`;
|
|
6
|
+
// if (prefix === 'y') return `m/49'/0'`;
|
|
7
|
+
// if (prefix === 'z') return `m/84'/0'`;
|
|
8
|
+
// if (prefix === 't') return `m/44'/1'`;
|
|
9
|
+
// if (prefix === 'u') return `m/49'/1'`;
|
|
10
|
+
// if (prefix === 'v') return `m/84'/1'`;
|
|
11
|
+
// return `m`;
|
|
12
|
+
// };
|
|
13
|
+
|
|
14
|
+
const resolveNetwork = (prefix) => {
|
|
15
|
+
if (['x', 'y', 'z'].includes(prefix)) return NETWORK;
|
|
16
|
+
return TEST_NETWORK;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default (xpub, index) => {
|
|
20
|
+
const prefix = xpub.slice(0, 1);
|
|
21
|
+
if (prefix !== 'z' && prefix !== 'v')
|
|
22
|
+
throw new Error('Cryptopay only supports native segwit (zpub/vpub) extended key format for BTC');
|
|
23
|
+
|
|
24
|
+
const hardenedMaster = HDKey.fromExtendedKey(
|
|
25
|
+
xpub,
|
|
26
|
+
prefix === 'z'
|
|
27
|
+
? {
|
|
28
|
+
public: 0x04b24746, // zpub public
|
|
29
|
+
private: 0x04b2430c, // zpriv private
|
|
30
|
+
}
|
|
31
|
+
: {
|
|
32
|
+
public: 0x045f1cf6,
|
|
33
|
+
private: 0x045f18bc,
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
hardenedMaster.wipePrivateData(); // Neuter
|
|
37
|
+
|
|
38
|
+
const child = hardenedMaster.deriveChild(0).deriveChild(index);
|
|
39
|
+
|
|
40
|
+
// We don't need this AFAIK:
|
|
41
|
+
// const derivationPath = resolvePath(prefix);
|
|
42
|
+
// const child = hardenedMaster.derive(`${derivationPath}/0/${btcDerivationNumber}`);
|
|
43
|
+
|
|
44
|
+
const network = resolveNetwork(prefix);
|
|
45
|
+
const pubKey = p2wpkh(child.publicKey /* hex.decode( as string)*/, network);
|
|
46
|
+
|
|
47
|
+
return pubKey.address;
|
|
48
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { describe, it } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import deriveEthAddress from './derive-eth-address.js';
|
|
4
|
+
|
|
5
|
+
describe('ETH Address Derivation', () => {
|
|
6
|
+
it('returns a deterministic BIP44 child address', () => {
|
|
7
|
+
assert.equal(
|
|
8
|
+
deriveEthAddress(
|
|
9
|
+
'xpub6DTksA6DsGUgbEp1U29v3qGTt5P99iKThaqGEzerTfD73ZAg1F8j15TxWTPEjjA1uw1Zmwwc5dgVCXwTL2ZNhvdoLWNdqVuL1MDJMsQCLLs',
|
|
10
|
+
1,
|
|
11
|
+
),
|
|
12
|
+
'0x11CF7d607A1Dc45a002592891fF25DAAE6eC7dE9',
|
|
13
|
+
);
|
|
14
|
+
assert.equal(
|
|
15
|
+
deriveEthAddress(
|
|
16
|
+
'xpub6DTksA6DsGUgbEp1U29v3qGTt5P99iKThaqGEzerTfD73ZAg1F8j15TxWTPEjjA1uw1Zmwwc5dgVCXwTL2ZNhvdoLWNdqVuL1MDJMsQCLLs',
|
|
17
|
+
2,
|
|
18
|
+
),
|
|
19
|
+
'0x61663D505e3aB5b61a5344eA2c64b644c08013ea',
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { HDKey } from '@scure/bip32';
|
|
2
|
+
import { keccak_256 } from '@noble/hashes/sha3';
|
|
3
|
+
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
4
|
+
|
|
5
|
+
function getChecksumAddress(address) {
|
|
6
|
+
const chars = address.split('');
|
|
7
|
+
const expanded = new Uint8Array(40);
|
|
8
|
+
for (let i = 0; i < 40; i++) {
|
|
9
|
+
expanded[i] = chars[i].charCodeAt(0);
|
|
10
|
+
}
|
|
11
|
+
const hashed = keccak_256(expanded);
|
|
12
|
+
|
|
13
|
+
for (let i = 0; i < 40; i += 2) {
|
|
14
|
+
if (hashed[i >> 1] >> 4 >= 8) {
|
|
15
|
+
chars[i] = chars[i].toUpperCase();
|
|
16
|
+
}
|
|
17
|
+
if ((hashed[i >> 1] & 0x0f) >= 8) {
|
|
18
|
+
chars[i + 1] = chars[i + 1].toUpperCase();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return '0x' + chars.join('');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default (xpub, index) => {
|
|
26
|
+
const hardenedMaster = HDKey.fromExtendedKey(xpub);
|
|
27
|
+
hardenedMaster.wipePrivateData(); // Neuter
|
|
28
|
+
|
|
29
|
+
const child = hardenedMaster.deriveChild(0).deriveChild(index);
|
|
30
|
+
|
|
31
|
+
// ETH Address (secp256k1 + keccak_256 + checksum)
|
|
32
|
+
const childSigningKey = secp256k1.ProjectivePoint.fromHex(child.publicKey).toRawBytes(false);
|
|
33
|
+
const address = Buffer.from(keccak_256(childSigningKey.slice(1)))
|
|
34
|
+
.toString('hex')
|
|
35
|
+
.substring(24);
|
|
36
|
+
|
|
37
|
+
return getChecksumAddress(address);
|
|
38
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import { Context } from '@unchainedshop/api';
|
|
3
|
+
import { OrderStatus } from '@unchainedshop/core-orders';
|
|
4
|
+
import { CryptopayModule } from './module.js';
|
|
5
|
+
import { ProductPriceRate } from '@unchainedshop/core-products';
|
|
6
|
+
|
|
7
|
+
const { CRYPTOPAY_SECRET, CRYPTOPAY_MAX_RATE_AGE = '360' } = process.env;
|
|
8
|
+
|
|
9
|
+
const logger = createLogger('unchained:core-payment:cryptopay');
|
|
10
|
+
|
|
11
|
+
export default async function handleWebhook(
|
|
12
|
+
{
|
|
13
|
+
secret,
|
|
14
|
+
wallet,
|
|
15
|
+
price,
|
|
16
|
+
ping,
|
|
17
|
+
}: {
|
|
18
|
+
secret: string;
|
|
19
|
+
wallet?: {
|
|
20
|
+
address: string;
|
|
21
|
+
blockHeight: number;
|
|
22
|
+
amount: string;
|
|
23
|
+
contract?: string;
|
|
24
|
+
decimals?: number;
|
|
25
|
+
currencyCode: string;
|
|
26
|
+
};
|
|
27
|
+
price?: {
|
|
28
|
+
baseCurrency: string;
|
|
29
|
+
token: string;
|
|
30
|
+
rate: number;
|
|
31
|
+
timestamp: string; // ISO date string
|
|
32
|
+
};
|
|
33
|
+
ping?: {
|
|
34
|
+
blockHeight: number;
|
|
35
|
+
currencyCode: string;
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
context: Context & {
|
|
39
|
+
modules: CryptopayModule;
|
|
40
|
+
},
|
|
41
|
+
) {
|
|
42
|
+
const { modules, services } = context;
|
|
43
|
+
|
|
44
|
+
if (secret !== CRYPTOPAY_SECRET) {
|
|
45
|
+
logger.warn(`webhook called with invalid secret`);
|
|
46
|
+
throw new Error('Secret invalid');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (wallet) {
|
|
50
|
+
const { address, blockHeight, amount, contract, decimals, currencyCode } = wallet;
|
|
51
|
+
logger.debug('webhook received wallet data', wallet);
|
|
52
|
+
const { orderPaymentId } = await modules.cryptopay.updateWalletAddress({
|
|
53
|
+
address,
|
|
54
|
+
blockHeight,
|
|
55
|
+
amount,
|
|
56
|
+
contract,
|
|
57
|
+
decimals,
|
|
58
|
+
currencyCode,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const orderPayment =
|
|
62
|
+
orderPaymentId &&
|
|
63
|
+
(await modules.orders.payments.findOrderPayment({
|
|
64
|
+
orderPaymentId,
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
if (orderPayment) {
|
|
68
|
+
// Try to process order to next status
|
|
69
|
+
// TODO: Not sure if it's correct to use processOrder here if status is PENDING!
|
|
70
|
+
const order = await modules.orders.findOrder({ orderId: orderPayment.orderId });
|
|
71
|
+
if (order.status === null) {
|
|
72
|
+
await services.orders.checkoutOrder(order._id, {});
|
|
73
|
+
} else if (order.status === OrderStatus.PENDING) {
|
|
74
|
+
await services.orders.processOrder(order, {});
|
|
75
|
+
} else {
|
|
76
|
+
throw new Error('Already processed');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (price) {
|
|
82
|
+
logger.debug('webhook received price data', price);
|
|
83
|
+
const { baseCurrency, token, rate, timestamp } = price;
|
|
84
|
+
|
|
85
|
+
const timestampDate = new Date(timestamp);
|
|
86
|
+
const expiresAt = new Date(new Date().getTime() + parseInt(CRYPTOPAY_MAX_RATE_AGE, 10) * 1000);
|
|
87
|
+
|
|
88
|
+
const rateData: ProductPriceRate = {
|
|
89
|
+
baseCurrency,
|
|
90
|
+
quoteCurrency: token,
|
|
91
|
+
rate,
|
|
92
|
+
expiresAt,
|
|
93
|
+
timestamp: timestampDate,
|
|
94
|
+
};
|
|
95
|
+
await modules.products.prices.rates.updateRates([rateData]);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (ping) {
|
|
99
|
+
logger.debug('webhook received ping data', ping);
|
|
100
|
+
const { blockHeight, currencyCode } = ping;
|
|
101
|
+
await modules.cryptopay.updateMostRecentBlock(currencyCode, blockHeight);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -1,83 +1,17 @@
|
|
|
1
1
|
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
-
import
|
|
3
|
-
import { OrderStatus } from '@unchainedshop/core-orders';
|
|
4
|
-
import { CryptopayModule } from './module.js';
|
|
5
|
-
import { ProductPriceRate } from '@unchainedshop/core-products';
|
|
6
|
-
|
|
7
|
-
const { CRYPTOPAY_SECRET, CRYPTOPAY_MAX_RATE_AGE = '360' } = process.env;
|
|
2
|
+
import handleWebhook from './handle-webhook.js';
|
|
8
3
|
|
|
9
4
|
const logger = createLogger('unchained:core-payment:cryptopay');
|
|
10
5
|
|
|
11
6
|
const cryptopayHandler = async (req, res) => {
|
|
12
|
-
const resolvedContext = req.unchainedContext as Context & {
|
|
13
|
-
modules: CryptopayModule;
|
|
14
|
-
};
|
|
15
|
-
const { modules, services } = resolvedContext;
|
|
16
|
-
|
|
17
7
|
if (req.method === 'POST') {
|
|
18
8
|
try {
|
|
19
|
-
|
|
20
|
-
if (secret !== CRYPTOPAY_SECRET) {
|
|
21
|
-
logger.warn(`Cryptopay Plugin: Invalid Cryptopay Secret provided`);
|
|
22
|
-
throw new Error('Secret invalid');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (wallet) {
|
|
26
|
-
const { address, blockHeight, amount, contract, decimals, currency } = wallet;
|
|
27
|
-
const { orderPaymentId } = await modules.cryptopay.updateWalletAddress({
|
|
28
|
-
addressId: address,
|
|
29
|
-
blockHeight,
|
|
30
|
-
amount,
|
|
31
|
-
contract,
|
|
32
|
-
decimals,
|
|
33
|
-
currency,
|
|
34
|
-
});
|
|
35
|
-
const orderPayment =
|
|
36
|
-
orderPaymentId &&
|
|
37
|
-
(await modules.orders.payments.findOrderPayment({
|
|
38
|
-
orderPaymentId,
|
|
39
|
-
}));
|
|
40
|
-
|
|
41
|
-
if (orderPayment) {
|
|
42
|
-
// Try to process order to next status
|
|
43
|
-
// TODO: Not sure if it's correct to use processOrder here if status is PENDING!
|
|
44
|
-
const order = await modules.orders.findOrder({ orderId: orderPayment.orderId });
|
|
45
|
-
if (order.status === null) {
|
|
46
|
-
await services.orders.checkoutOrder(order._id, {});
|
|
47
|
-
} else if (order.status === OrderStatus.PENDING) {
|
|
48
|
-
await services.orders.processOrder(order, {});
|
|
49
|
-
} else {
|
|
50
|
-
throw new Error('Already processed');
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (price) {
|
|
56
|
-
const { baseCurrency, token, rate, timestamp } = price;
|
|
57
|
-
|
|
58
|
-
const timestampDate = new Date(timestamp);
|
|
59
|
-
const expiresAt = new Date(new Date().getTime() + parseInt(CRYPTOPAY_MAX_RATE_AGE, 10) * 1000);
|
|
60
|
-
|
|
61
|
-
const rateData: ProductPriceRate = {
|
|
62
|
-
baseCurrency,
|
|
63
|
-
quoteCurrency: token,
|
|
64
|
-
rate,
|
|
65
|
-
expiresAt,
|
|
66
|
-
timestamp: timestampDate,
|
|
67
|
-
};
|
|
68
|
-
logger.info(`update rate ${JSON.stringify(price)}, ${JSON.stringify(rateData)}`);
|
|
69
|
-
await resolvedContext.modules.products.prices.rates.updateRates([rateData]);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (ping) {
|
|
73
|
-
const { blockHeight, currency } = ping;
|
|
74
|
-
await modules.cryptopay.updateMostRecentBlock(currency, blockHeight);
|
|
75
|
-
}
|
|
76
|
-
|
|
9
|
+
await handleWebhook(req.body, req.unchainedContext);
|
|
77
10
|
res.writeHead(200);
|
|
78
11
|
res.end(JSON.stringify({ success: true }));
|
|
79
12
|
return;
|
|
80
13
|
} catch (e) {
|
|
14
|
+
logger.error(e);
|
|
81
15
|
res.writeHead(500);
|
|
82
16
|
res.end(JSON.stringify({ success: false, error: e.message }));
|
|
83
17
|
return;
|
|
@@ -1,86 +1,21 @@
|
|
|
1
|
-
import { FastifyRequest, RouteHandlerMethod } from 'fastify';
|
|
2
1
|
import { createLogger } from '@unchainedshop/logger';
|
|
3
|
-
import
|
|
4
|
-
import { OrderStatus } from '@unchainedshop/core-orders';
|
|
5
|
-
import { CryptopayModule } from './module.js';
|
|
6
|
-
import { ProductPriceRate } from '@unchainedshop/core-products';
|
|
7
|
-
|
|
8
|
-
const { CRYPTOPAY_SECRET, CRYPTOPAY_MAX_RATE_AGE = '360' } = process.env;
|
|
2
|
+
import handleWebhook from './handle-webhook.js';
|
|
9
3
|
|
|
10
4
|
const logger = createLogger('unchained:core-payment:cryptopay');
|
|
5
|
+
import { FastifyRequest, RouteHandlerMethod } from 'fastify';
|
|
11
6
|
|
|
12
7
|
const cryptopayHandler: RouteHandlerMethod = async (
|
|
13
8
|
req: FastifyRequest & {
|
|
14
|
-
unchainedContext:
|
|
15
|
-
modules: CryptopayModule;
|
|
16
|
-
};
|
|
9
|
+
unchainedContext: any;
|
|
17
10
|
},
|
|
18
11
|
reply,
|
|
19
12
|
) => {
|
|
20
|
-
const { modules, services } = req.unchainedContext;
|
|
21
|
-
|
|
22
13
|
try {
|
|
23
|
-
|
|
24
|
-
if (secret !== CRYPTOPAY_SECRET) {
|
|
25
|
-
logger.warn(`Cryptopay Plugin: Invalid Cryptopay Secret provided`);
|
|
26
|
-
throw new Error('Secret invalid');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (wallet) {
|
|
30
|
-
const { address, blockHeight, amount, contract, decimals, currency } = wallet;
|
|
31
|
-
const { orderPaymentId } = await modules.cryptopay.updateWalletAddress({
|
|
32
|
-
addressId: address,
|
|
33
|
-
blockHeight,
|
|
34
|
-
amount,
|
|
35
|
-
contract,
|
|
36
|
-
decimals,
|
|
37
|
-
currency,
|
|
38
|
-
});
|
|
39
|
-
const orderPayment =
|
|
40
|
-
orderPaymentId &&
|
|
41
|
-
(await modules.orders.payments.findOrderPayment({
|
|
42
|
-
orderPaymentId,
|
|
43
|
-
}));
|
|
44
|
-
|
|
45
|
-
if (orderPayment) {
|
|
46
|
-
// Try to process order to next status
|
|
47
|
-
// TODO: Not sure if it's correct to use processOrder here if status is PENDING!
|
|
48
|
-
const order = await modules.orders.findOrder({ orderId: orderPayment.orderId });
|
|
49
|
-
if (order.status === null) {
|
|
50
|
-
await services.orders.checkoutOrder(order._id, {});
|
|
51
|
-
} else if (order.status === OrderStatus.PENDING) {
|
|
52
|
-
await services.orders.processOrder(order, {});
|
|
53
|
-
} else {
|
|
54
|
-
throw new Error('Already processed');
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (price) {
|
|
60
|
-
const { baseCurrency, token, rate, timestamp } = price;
|
|
61
|
-
|
|
62
|
-
const timestampDate = new Date(timestamp);
|
|
63
|
-
const expiresAt = new Date(new Date().getTime() + parseInt(CRYPTOPAY_MAX_RATE_AGE, 10) * 1000);
|
|
64
|
-
|
|
65
|
-
const rateData: ProductPriceRate = {
|
|
66
|
-
baseCurrency,
|
|
67
|
-
quoteCurrency: token,
|
|
68
|
-
rate,
|
|
69
|
-
expiresAt,
|
|
70
|
-
timestamp: timestampDate,
|
|
71
|
-
};
|
|
72
|
-
logger.info(`update rate ${JSON.stringify(price)}, ${JSON.stringify(rateData)}`);
|
|
73
|
-
await modules.products.prices.rates.updateRates([rateData]);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (ping) {
|
|
77
|
-
const { blockHeight, currency } = ping;
|
|
78
|
-
await modules.cryptopay.updateMostRecentBlock(currency, blockHeight);
|
|
79
|
-
}
|
|
80
|
-
|
|
14
|
+
await handleWebhook(req.body as any, req.unchainedContext);
|
|
81
15
|
reply.status(200);
|
|
82
16
|
return reply.send({ success: true });
|
|
83
17
|
} catch (e) {
|
|
18
|
+
logger.error(e);
|
|
84
19
|
reply.status(500);
|
|
85
20
|
return reply.send({ success: false, error: e.message });
|
|
86
21
|
}
|
|
@@ -4,14 +4,15 @@ import { CryptopayTransaction, CryptopayTransactionsCollection } from './db/Cryp
|
|
|
4
4
|
const configureCryptopayModule = ({ db }) => {
|
|
5
5
|
const CryptoTransactions = CryptopayTransactionsCollection(db);
|
|
6
6
|
|
|
7
|
-
const getWalletAddress = async (
|
|
8
|
-
|
|
7
|
+
const getWalletAddress = async (address: string, contract?: string): Promise<CryptopayTransaction> => {
|
|
8
|
+
const addressId = [address, contract].filter(Boolean).join(':');
|
|
9
|
+
return CryptoTransactions.findOne({ _id: addressId });
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
const updateMostRecentBlock = async (
|
|
12
|
+
const updateMostRecentBlock = async (currencyCode: string, blockHeight: number): Promise<void> => {
|
|
12
13
|
await CryptoTransactions.updateMany(
|
|
13
14
|
{
|
|
14
|
-
|
|
15
|
+
currencyCode,
|
|
15
16
|
},
|
|
16
17
|
{
|
|
17
18
|
$set: {
|
|
@@ -22,26 +23,26 @@ const configureCryptopayModule = ({ db }) => {
|
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const mapOrderPaymentToWalletAddress = async ({
|
|
25
|
-
|
|
26
|
+
address,
|
|
26
27
|
contract,
|
|
27
|
-
|
|
28
|
+
currencyCode,
|
|
28
29
|
orderPaymentId,
|
|
29
30
|
}: {
|
|
30
|
-
|
|
31
|
-
contract
|
|
32
|
-
|
|
31
|
+
address: string;
|
|
32
|
+
contract?: string;
|
|
33
|
+
currencyCode: string;
|
|
33
34
|
orderPaymentId: string;
|
|
34
35
|
}): Promise<CryptopayTransaction> => {
|
|
35
|
-
|
|
36
|
+
const addressId = [address, contract].filter(Boolean).join(':');
|
|
37
|
+
|
|
38
|
+
return CryptoTransactions.findOneAndUpdate(
|
|
36
39
|
{
|
|
37
|
-
_id: addressId
|
|
40
|
+
_id: addressId,
|
|
38
41
|
},
|
|
39
42
|
{
|
|
40
43
|
$setOnInsert: {
|
|
41
|
-
_id: addressId
|
|
42
|
-
|
|
43
|
-
currency,
|
|
44
|
-
decimals: null,
|
|
44
|
+
_id: addressId,
|
|
45
|
+
currencyCode,
|
|
45
46
|
mostRecentBlockHeight: 0,
|
|
46
47
|
blockHeight: 0,
|
|
47
48
|
amount: mongodb.Decimal128.fromString('0'),
|
|
@@ -52,62 +53,65 @@ const configureCryptopayModule = ({ db }) => {
|
|
|
52
53
|
updated: new Date(),
|
|
53
54
|
},
|
|
54
55
|
},
|
|
55
|
-
{ upsert: true },
|
|
56
|
+
{ upsert: true, returnDocument: 'after' },
|
|
56
57
|
);
|
|
57
|
-
|
|
58
|
-
return CryptoTransactions.findOne({ _id: addressId.toLowerCase() });
|
|
59
58
|
};
|
|
60
59
|
|
|
61
|
-
const getNextDerivationNumber = async (
|
|
62
|
-
return (await CryptoTransactions.countDocuments({
|
|
60
|
+
const getNextDerivationNumber = async (currencyCode: string): Promise<number> => {
|
|
61
|
+
return (await CryptoTransactions.countDocuments({ currencyCode })) + 1;
|
|
63
62
|
};
|
|
64
63
|
|
|
65
64
|
const getWalletAddressesByOrderPaymentId = async (
|
|
66
65
|
orderPaymentId: string,
|
|
67
66
|
): Promise<CryptopayTransaction[]> => {
|
|
68
|
-
return CryptoTransactions.find(
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
return CryptoTransactions.find(
|
|
68
|
+
{
|
|
69
|
+
orderPaymentId,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
sort: {
|
|
73
|
+
created: 1, // Sort by creation date, most recent first
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
).toArray();
|
|
71
77
|
};
|
|
72
78
|
|
|
73
79
|
const updateWalletAddress = async ({
|
|
74
|
-
|
|
80
|
+
address,
|
|
75
81
|
blockHeight,
|
|
76
82
|
amount,
|
|
77
83
|
contract,
|
|
78
|
-
|
|
84
|
+
currencyCode,
|
|
79
85
|
decimals,
|
|
80
86
|
}: {
|
|
81
|
-
|
|
87
|
+
address: string;
|
|
82
88
|
blockHeight: number;
|
|
83
|
-
amount: string;
|
|
84
|
-
contract
|
|
85
|
-
|
|
89
|
+
amount: string | number;
|
|
90
|
+
contract?: string;
|
|
91
|
+
currencyCode: string;
|
|
86
92
|
decimals: number;
|
|
87
93
|
}): Promise<CryptopayTransaction> => {
|
|
88
|
-
|
|
94
|
+
const addressId = [address, contract].filter(Boolean).join(':');
|
|
95
|
+
return CryptoTransactions.findOneAndUpdate(
|
|
89
96
|
{
|
|
90
|
-
_id: addressId
|
|
97
|
+
_id: addressId,
|
|
91
98
|
},
|
|
92
99
|
{
|
|
93
100
|
$setOnInsert: {
|
|
94
|
-
_id: addressId
|
|
95
|
-
currency,
|
|
96
|
-
contract,
|
|
101
|
+
_id: addressId,
|
|
97
102
|
created: new Date(),
|
|
98
103
|
},
|
|
99
104
|
$set: {
|
|
105
|
+
currencyCode,
|
|
100
106
|
decimals,
|
|
101
107
|
blockHeight,
|
|
102
108
|
mostRecentBlockHeight: blockHeight,
|
|
103
|
-
amount: mongodb.Decimal128.fromString(amount),
|
|
109
|
+
amount: mongodb.Decimal128.fromString(typeof amount === 'number' ? `${amount}` : amount),
|
|
104
110
|
updated: new Date(),
|
|
105
111
|
},
|
|
106
112
|
},
|
|
107
|
-
{ upsert: true },
|
|
113
|
+
{ upsert: true, returnDocument: 'after' },
|
|
108
114
|
);
|
|
109
|
-
|
|
110
|
-
return CryptoTransactions.findOne({ _id: addressId.toLowerCase() });
|
|
111
115
|
};
|
|
112
116
|
|
|
113
117
|
return {
|
|
@@ -126,6 +130,6 @@ export default {
|
|
|
126
130
|
},
|
|
127
131
|
};
|
|
128
132
|
|
|
129
|
-
export
|
|
133
|
+
export interface CryptopayModule {
|
|
130
134
|
cryptopay: ReturnType<typeof configureCryptopayModule>;
|
|
131
|
-
}
|
|
135
|
+
}
|