@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,138 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import { PaymentAdapter, PaymentDirector, PaymentError, OrderPricingSheet, } from '@unchainedshop/core';
|
|
3
|
+
const logger = createLogger('unchained:braintree');
|
|
4
|
+
const { BRAINTREE_SANDBOX_TOKEN, BRAINTREE_PRIVATE_KEY } = process.env;
|
|
5
|
+
let braintree;
|
|
6
|
+
try {
|
|
7
|
+
const braintreePackage = await import('braintree');
|
|
8
|
+
braintree = braintreePackage.default;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
logger.warn("npm dependency 'braintree' is not installed, paypal adapter will not work");
|
|
12
|
+
}
|
|
13
|
+
const BraintreeDirect = {
|
|
14
|
+
...PaymentAdapter,
|
|
15
|
+
key: 'shop.unchained.braintree-direct',
|
|
16
|
+
label: 'Braintree Direct',
|
|
17
|
+
version: '1.0.0',
|
|
18
|
+
initialConfiguration: [
|
|
19
|
+
{
|
|
20
|
+
key: 'publicKey',
|
|
21
|
+
value: null,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
key: 'merchantId',
|
|
25
|
+
value: null,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
typeSupported: (type) => {
|
|
29
|
+
return type === 'GENERIC';
|
|
30
|
+
},
|
|
31
|
+
actions: (config, context) => {
|
|
32
|
+
const getPublicKey = () => {
|
|
33
|
+
return config.reduce((current, item) => {
|
|
34
|
+
if (item.key === 'publicKey')
|
|
35
|
+
return item.value;
|
|
36
|
+
return current;
|
|
37
|
+
}, null);
|
|
38
|
+
};
|
|
39
|
+
const getMerchantId = () => {
|
|
40
|
+
return config.reduce((current, item) => {
|
|
41
|
+
if (item.key === 'merchantId')
|
|
42
|
+
return item.value;
|
|
43
|
+
return current;
|
|
44
|
+
}, null);
|
|
45
|
+
};
|
|
46
|
+
const getAccessToken = () => {
|
|
47
|
+
return BRAINTREE_SANDBOX_TOKEN;
|
|
48
|
+
};
|
|
49
|
+
const getPrivateKey = () => {
|
|
50
|
+
return BRAINTREE_PRIVATE_KEY;
|
|
51
|
+
};
|
|
52
|
+
const getGateway = (braintree) => {
|
|
53
|
+
const accessToken = getAccessToken();
|
|
54
|
+
if (accessToken) {
|
|
55
|
+
return braintree.connect({
|
|
56
|
+
accessToken,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return braintree.connect({
|
|
60
|
+
environment: braintree.Environment.Production,
|
|
61
|
+
merchantId: getMerchantId(),
|
|
62
|
+
publicKey: getPublicKey(),
|
|
63
|
+
privateKey: getPrivateKey(),
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
const adapter = {
|
|
67
|
+
...PaymentAdapter.actions(config, context),
|
|
68
|
+
configurationError: () => {
|
|
69
|
+
const publicCredentialsValid = getAccessToken() || (getMerchantId() && getPublicKey() && getPrivateKey());
|
|
70
|
+
if (!publicCredentialsValid) {
|
|
71
|
+
return PaymentError.WRONG_CREDENTIALS;
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
},
|
|
75
|
+
isActive: () => {
|
|
76
|
+
if (adapter.configurationError() === null)
|
|
77
|
+
return true;
|
|
78
|
+
return false;
|
|
79
|
+
},
|
|
80
|
+
isPayLaterAllowed: () => {
|
|
81
|
+
return false;
|
|
82
|
+
},
|
|
83
|
+
sign: async () => {
|
|
84
|
+
const gateway = getGateway(braintree);
|
|
85
|
+
const result = await gateway.clientToken.generate({});
|
|
86
|
+
if (result.success) {
|
|
87
|
+
return result.clientToken;
|
|
88
|
+
}
|
|
89
|
+
throw new Error('Could not retrieve the client token');
|
|
90
|
+
},
|
|
91
|
+
charge: async ({ paypalPaymentMethodNonce }) => {
|
|
92
|
+
const { order } = context;
|
|
93
|
+
if (!order)
|
|
94
|
+
throw new Error('Order is missing in payment context');
|
|
95
|
+
if (!paypalPaymentMethodNonce)
|
|
96
|
+
throw new Error('You have to provide paypalPaymentMethodNonce in paymentContext');
|
|
97
|
+
const gateway = getGateway(braintree);
|
|
98
|
+
const address = order.billingAddress;
|
|
99
|
+
const pricing = OrderPricingSheet({
|
|
100
|
+
calculation: order.calculation,
|
|
101
|
+
currencyCode: order.currencyCode,
|
|
102
|
+
});
|
|
103
|
+
const rounded = Math.round(pricing.total({ useNetPrice: false }).amount / 10 || 0) * 10;
|
|
104
|
+
const saleRequest = {
|
|
105
|
+
amount: rounded / 100,
|
|
106
|
+
merchantAccountId: order.currencyCode,
|
|
107
|
+
paymentMethodNonce: paypalPaymentMethodNonce,
|
|
108
|
+
orderId: order.orderNumber || order._id,
|
|
109
|
+
shipping: address
|
|
110
|
+
? {
|
|
111
|
+
firstName: address.firstName,
|
|
112
|
+
lastName: address.lastName,
|
|
113
|
+
company: address.company,
|
|
114
|
+
streetAddress: address.addressLine,
|
|
115
|
+
extendedAddress: address.addressLine2,
|
|
116
|
+
locality: address.city,
|
|
117
|
+
region: address.regionCode,
|
|
118
|
+
postalCode: address.postalCode,
|
|
119
|
+
countryCodeAlpha2: address.countryCode,
|
|
120
|
+
}
|
|
121
|
+
: undefined,
|
|
122
|
+
options: {
|
|
123
|
+
submitForSettlement: true,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
const result = await gateway.transaction.sale(saleRequest);
|
|
127
|
+
if (result.success) {
|
|
128
|
+
logger.info(`Braintree Plugin: ${result.message}`, saleRequest);
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
logger.warn(`Braintree Plugin: ${result.message}`, saleRequest);
|
|
132
|
+
throw new Error(result.message);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
return adapter;
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
PaymentDirector.registerAdapter(BraintreeDirect);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import handleWebhook from "./handle-webhook.js";
|
|
3
|
+
const logger = createLogger('unchained:cryptopay:handler');
|
|
4
|
+
const cryptopayHandler = async (req, res) => {
|
|
5
|
+
if (req.method === 'POST') {
|
|
6
|
+
try {
|
|
7
|
+
await handleWebhook(req.body, req.unchainedContext);
|
|
8
|
+
res.status(200).send({ success: true });
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
logger.error(e);
|
|
13
|
+
res.status(500).send({ success: false, error: e.message });
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
res.status(404).send();
|
|
18
|
+
};
|
|
19
|
+
export default cryptopayHandler;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import handleWebhook from "./handle-webhook.js";
|
|
3
|
+
import {} from 'fastify';
|
|
4
|
+
const logger = createLogger('unchained:cryptopay:handler');
|
|
5
|
+
const cryptopayHandler = async (req, reply) => {
|
|
6
|
+
try {
|
|
7
|
+
await handleWebhook(req.body, req.unchainedContext);
|
|
8
|
+
reply.status(200);
|
|
9
|
+
return reply.send({ success: true });
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
logger.error(e);
|
|
13
|
+
reply.status(500);
|
|
14
|
+
return reply.send({ success: false, error: e.message });
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
export default cryptopayHandler;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
export
|
|
5
|
-
export { type CryptopayModule } from './module.ts';
|
|
1
|
+
import './plugin.ts';
|
|
2
|
+
import cryptopayModules, { type CryptopayModule } from './module.ts';
|
|
3
|
+
export type { CryptopayModule };
|
|
4
|
+
export default cryptopayModules;
|
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
const { CRYPTOPAY_SECRET, CRYPTOPAY_BTC_XPUB, CRYPTOPAY_ETH_XPUB, CRYPTOPAY_WEBHOOK_PATH = '/payment/cryptopay', } = process.env;
|
|
5
|
-
export const CryptopayPlugin = {
|
|
6
|
-
key: 'shop.unchained.payment.cryptopay',
|
|
7
|
-
label: 'Cryptopay Payment Plugin',
|
|
8
|
-
version: '1.0.0',
|
|
9
|
-
adapters: [Cryptopay],
|
|
10
|
-
module: ({ db }) => ({
|
|
11
|
-
cryptopay: cryptopayModules.cryptopay.configure({ db }),
|
|
12
|
-
}),
|
|
13
|
-
routes: [
|
|
14
|
-
{
|
|
15
|
-
path: CRYPTOPAY_WEBHOOK_PATH,
|
|
16
|
-
method: 'POST',
|
|
17
|
-
handler: cryptopayWebhookHandler,
|
|
18
|
-
},
|
|
19
|
-
],
|
|
20
|
-
onRegister: () => {
|
|
21
|
-
if (!CRYPTOPAY_SECRET) {
|
|
22
|
-
throw new Error('CRYPTOPAY_SECRET not set');
|
|
23
|
-
}
|
|
24
|
-
if (!CRYPTOPAY_BTC_XPUB && !CRYPTOPAY_ETH_XPUB) {
|
|
25
|
-
throw new Error('No CRYPTOPAY_BTC_XPUB or CRYPTOPAY_ETH_XPUB set');
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
export default CryptopayPlugin;
|
|
30
|
-
export { Cryptopay } from "./adapter.js";
|
|
31
|
-
export {} from "./module.js";
|
|
1
|
+
import "./plugin.js";
|
|
2
|
+
import cryptopayModules, {} from "./module.js";
|
|
3
|
+
export default cryptopayModules;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { PaymentAdapter, PaymentDirector, PaymentError, OrderPricingSheet, } from '@unchainedshop/core';
|
|
2
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
3
|
+
import {} from "./module.js";
|
|
4
|
+
import deriveBtcAddress from "./derive-btc-address.js";
|
|
5
|
+
import deriveEthAddress from "./derive-eth-address.js";
|
|
6
|
+
import denoteAmount from "./denote-amount.js";
|
|
7
|
+
const logger = createLogger('unchained:cryptopay');
|
|
8
|
+
const { CRYPTOPAY_SECRET, CRYPTOPAY_BTC_XPUB, CRYPTOPAY_ETH_XPUB } = process.env;
|
|
9
|
+
const CryptopayCurrencies = {
|
|
10
|
+
BTC: 'BTC',
|
|
11
|
+
ETH: 'ETH',
|
|
12
|
+
};
|
|
13
|
+
const Cryptopay = {
|
|
14
|
+
...PaymentAdapter,
|
|
15
|
+
key: 'shop.unchained.payment.cryptopay',
|
|
16
|
+
label: 'Cryptopay',
|
|
17
|
+
version: '1.0.0',
|
|
18
|
+
typeSupported(type) {
|
|
19
|
+
return type === 'GENERIC';
|
|
20
|
+
},
|
|
21
|
+
actions: (config, context) => {
|
|
22
|
+
const { modules } = context;
|
|
23
|
+
const setConversionRates = async (currencyCode, existingAddresses) => {
|
|
24
|
+
const originCurrencyObj = await modules.currencies.findCurrency({ isoCode: currencyCode });
|
|
25
|
+
if (!originCurrencyObj?.isActive)
|
|
26
|
+
throw new Error('Origin currency is not supported');
|
|
27
|
+
const updatedAddresses = await Promise.all(existingAddresses.map(async (addressData) => {
|
|
28
|
+
const targetCurrencyObj = await modules.currencies.findCurrency({
|
|
29
|
+
isoCode: addressData.currencyCode,
|
|
30
|
+
});
|
|
31
|
+
if (!targetCurrencyObj?.isActive)
|
|
32
|
+
return null;
|
|
33
|
+
const rateData = await modules.products.prices.rates.getRate(originCurrencyObj, targetCurrencyObj);
|
|
34
|
+
return {
|
|
35
|
+
...addressData,
|
|
36
|
+
currencyConversionRate: rateData?.rate,
|
|
37
|
+
currencyConversionExpiryDate: rateData?.expiresAt,
|
|
38
|
+
};
|
|
39
|
+
}));
|
|
40
|
+
return updatedAddresses.filter(Boolean);
|
|
41
|
+
};
|
|
42
|
+
const updateTransactionsWithOrderPaymentId = async (orderPaymentId, cryptoAddresses) => {
|
|
43
|
+
for (const cryptoAddress of cryptoAddresses) {
|
|
44
|
+
const { address, currencyCode } = cryptoAddress;
|
|
45
|
+
await modules.cryptopay.mapOrderPaymentToWalletAddress({
|
|
46
|
+
address,
|
|
47
|
+
currencyCode,
|
|
48
|
+
orderPaymentId,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const adapterActions = {
|
|
53
|
+
...PaymentAdapter.actions(config, context),
|
|
54
|
+
configurationError() {
|
|
55
|
+
if (!CRYPTOPAY_SECRET) {
|
|
56
|
+
return PaymentError.INCOMPLETE_CONFIGURATION;
|
|
57
|
+
}
|
|
58
|
+
if (!CRYPTOPAY_BTC_XPUB && !CRYPTOPAY_ETH_XPUB) {
|
|
59
|
+
return PaymentError.INCOMPLETE_CONFIGURATION;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
},
|
|
63
|
+
isActive() {
|
|
64
|
+
if (adapterActions.configurationError() !== null)
|
|
65
|
+
return false;
|
|
66
|
+
if (!context.order)
|
|
67
|
+
return true;
|
|
68
|
+
return true;
|
|
69
|
+
},
|
|
70
|
+
isPayLaterAllowed() {
|
|
71
|
+
return false;
|
|
72
|
+
},
|
|
73
|
+
sign: async () => {
|
|
74
|
+
const { orderPayment, order } = context;
|
|
75
|
+
if (!orderPayment)
|
|
76
|
+
throw new Error('Payment Credential Registration is not yet supported for Cryptopay');
|
|
77
|
+
if (!order)
|
|
78
|
+
throw new Error('Order context is required for signing with Cryptopay');
|
|
79
|
+
const existingAddresses = await modules.cryptopay.getWalletAddressesByOrderPaymentId(orderPayment._id);
|
|
80
|
+
if (existingAddresses.length) {
|
|
81
|
+
const existingAddressesWithNewExpiration = await setConversionRates(order.currencyCode, existingAddresses.map(({ _id, currencyCode }) => ({
|
|
82
|
+
address: _id,
|
|
83
|
+
currencyCode,
|
|
84
|
+
})));
|
|
85
|
+
return JSON.stringify(existingAddressesWithNewExpiration);
|
|
86
|
+
}
|
|
87
|
+
const cryptoAddresses = [];
|
|
88
|
+
if (CRYPTOPAY_BTC_XPUB) {
|
|
89
|
+
const btcDerivationNumber = await modules.cryptopay.getNextDerivationNumber(CryptopayCurrencies.BTC);
|
|
90
|
+
const address = deriveBtcAddress(CRYPTOPAY_BTC_XPUB, btcDerivationNumber);
|
|
91
|
+
cryptoAddresses.push({
|
|
92
|
+
currencyCode: CryptopayCurrencies.BTC,
|
|
93
|
+
address,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (CRYPTOPAY_ETH_XPUB) {
|
|
97
|
+
const ethDerivationNumber = await modules.cryptopay.getNextDerivationNumber(CryptopayCurrencies.ETH);
|
|
98
|
+
const address = deriveEthAddress(CRYPTOPAY_ETH_XPUB, ethDerivationNumber);
|
|
99
|
+
cryptoAddresses.push({
|
|
100
|
+
currencyCode: CryptopayCurrencies.ETH,
|
|
101
|
+
address,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const cryptoAddressesWithExpiration = await setConversionRates(order.currencyCode, cryptoAddresses);
|
|
105
|
+
await updateTransactionsWithOrderPaymentId(orderPayment._id, cryptoAddresses);
|
|
106
|
+
return JSON.stringify(cryptoAddressesWithExpiration);
|
|
107
|
+
},
|
|
108
|
+
charge: async () => {
|
|
109
|
+
const { order, orderPayment } = context;
|
|
110
|
+
if (!orderPayment)
|
|
111
|
+
throw new Error('Order Payment context is required for charging with Cryptopay');
|
|
112
|
+
if (!order)
|
|
113
|
+
throw new Error('Order context is required for charging with Cryptopay');
|
|
114
|
+
const foundWalletsWithBalances = await modules.cryptopay.getWalletAddressesByOrderPaymentId(orderPayment._id);
|
|
115
|
+
const walletForOrderPayment = foundWalletsWithBalances.find((wallet) => BigInt(wallet.amount.toString()) > 0n);
|
|
116
|
+
const pricing = OrderPricingSheet({
|
|
117
|
+
calculation: order.calculation,
|
|
118
|
+
currencyCode: order.currencyCode,
|
|
119
|
+
});
|
|
120
|
+
const totalAmount = BigInt(pricing?.total({ useNetPrice: false }).amount);
|
|
121
|
+
if (walletForOrderPayment && walletForOrderPayment.currencyCode !== order.currencyCode) {
|
|
122
|
+
const baseCurrency = await modules.currencies.findCurrency({
|
|
123
|
+
isoCode: walletForOrderPayment.currencyCode,
|
|
124
|
+
});
|
|
125
|
+
if (!baseCurrency)
|
|
126
|
+
throw new Error('Base currency not found');
|
|
127
|
+
const quoteCurrency = await modules.currencies.findCurrency({ isoCode: order.currencyCode });
|
|
128
|
+
if (!quoteCurrency)
|
|
129
|
+
throw new Error('Quote currency not found');
|
|
130
|
+
const rate = await modules.products.prices.rates.getRateRange(baseCurrency, quoteCurrency, order.confirmed || new Date());
|
|
131
|
+
if (!rate)
|
|
132
|
+
throw new Error('No conversion rate found');
|
|
133
|
+
const convertedAmount = denoteAmount(walletForOrderPayment.amount.toString(), walletForOrderPayment.decimals);
|
|
134
|
+
const maxAmount = parseFloat(convertedAmount.toString()) * rate.max * 1.001;
|
|
135
|
+
if (maxAmount && maxAmount >= totalAmount) {
|
|
136
|
+
return {
|
|
137
|
+
transactionId: walletForOrderPayment._id,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
logger.info(`Cryptopay Plugin: Wallet ${walletForOrderPayment._id} balance too low (yet): ${maxAmount} < ${totalAmount}`);
|
|
141
|
+
}
|
|
142
|
+
if (walletForOrderPayment) {
|
|
143
|
+
const convertedAmount = denoteAmount(walletForOrderPayment.amount.toString(), walletForOrderPayment.decimals);
|
|
144
|
+
if (convertedAmount && convertedAmount >= totalAmount) {
|
|
145
|
+
return {
|
|
146
|
+
transactionId: walletForOrderPayment._id,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
logger.info(`Cryptopay Plugin: Wallet ${walletForOrderPayment._id} balance too low (yet): ${convertedAmount} < ${totalAmount}`);
|
|
150
|
+
}
|
|
151
|
+
logger.info(`Cryptopay Plugin: No confirmed payments found for currency ${order.currencyCode} and addresses ${JSON.stringify(foundWalletsWithBalances)}`);
|
|
152
|
+
return false;
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
return adapterActions;
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
PaymentDirector.registerAdapter(Cryptopay);
|
|
159
|
+
export default Cryptopay;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const datatransHandler: (req: any, res: any) => Promise<void>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import generateSignature, { Security } from "./generateSignature.js";
|
|
3
|
+
const { DATATRANS_SIGN_KEY, DATATRANS_SIGN2_KEY, DATATRANS_SECURITY = Security.DYNAMIC_SIGN, } = process.env;
|
|
4
|
+
const logger = createLogger('unchained:datatrans:handler');
|
|
5
|
+
export const datatransHandler = async (req, res) => {
|
|
6
|
+
const resolvedContext = req.unchainedContext;
|
|
7
|
+
const { modules, services } = resolvedContext;
|
|
8
|
+
const signature = req.headers['datatrans-signature'];
|
|
9
|
+
if (!DATATRANS_SIGN_KEY && !DATATRANS_SIGN2_KEY) {
|
|
10
|
+
logger.warn('No sign key configured');
|
|
11
|
+
res.status(404).end();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (req.method === 'POST' && signature) {
|
|
15
|
+
const [rawTimestamp, rawHash] = signature.split(',');
|
|
16
|
+
const [, hash] = rawHash.split('=');
|
|
17
|
+
const [, timestamp] = rawTimestamp.split('=');
|
|
18
|
+
const comparableSignature = await generateSignature({
|
|
19
|
+
security: DATATRANS_SECURITY,
|
|
20
|
+
signKey: DATATRANS_SIGN2_KEY || DATATRANS_SIGN_KEY,
|
|
21
|
+
})(timestamp, req.body);
|
|
22
|
+
if (hash !== comparableSignature) {
|
|
23
|
+
logger.error(`hash mismatch: ${signature} / ${comparableSignature}`);
|
|
24
|
+
res.status(403).send('Hash mismatch');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const transaction = JSON.parse(req.body);
|
|
28
|
+
logger.info(`received request`, {
|
|
29
|
+
type: transaction.type,
|
|
30
|
+
});
|
|
31
|
+
if (transaction.status === 'authorized') {
|
|
32
|
+
const userId = transaction.refno2;
|
|
33
|
+
const referenceId = Buffer.from(transaction.refno, 'base64').toString('hex');
|
|
34
|
+
try {
|
|
35
|
+
if (transaction.type === 'card_check') {
|
|
36
|
+
const paymentProviderId = referenceId;
|
|
37
|
+
const paymentCredentials = await services.orders.registerPaymentCredentials(paymentProviderId, { userId, transactionContext: { transactionId: transaction.transactionId } });
|
|
38
|
+
logger.info(`registered payment credentials for ${userId}`, {
|
|
39
|
+
userId,
|
|
40
|
+
});
|
|
41
|
+
res.status(200).send(paymentCredentials);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (transaction.type === 'payment') {
|
|
45
|
+
const orderPaymentId = referenceId;
|
|
46
|
+
const orderPayment = await modules.orders.payments.findOrderPayment({
|
|
47
|
+
orderPaymentId,
|
|
48
|
+
});
|
|
49
|
+
if (!orderPayment)
|
|
50
|
+
throw new Error(`Order Payment with id ${orderPaymentId} not found`);
|
|
51
|
+
const order = await services.orders.checkoutOrder(orderPayment.orderId, {
|
|
52
|
+
paymentContext: { userId, transactionId: transaction.transactionId },
|
|
53
|
+
});
|
|
54
|
+
if (!order)
|
|
55
|
+
throw new Error(`Order with id ${orderPayment.orderId} not found`);
|
|
56
|
+
logger.info(`confirmed checkout for order ${order.orderNumber}`, {
|
|
57
|
+
orderId: order._id,
|
|
58
|
+
});
|
|
59
|
+
res.status(200).send(order);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
logger.error(`rejected to checkout: ${e.name} - ${e.message}`);
|
|
65
|
+
res.status(500).send({ name: e.name, code: e.code, message: e.message });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
res.status(404).end();
|
|
71
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createLogger } from '@unchainedshop/logger';
|
|
2
|
+
import generateSignature, { Security } from "./generateSignature.js";
|
|
3
|
+
const { DATATRANS_SIGN_KEY, DATATRANS_SIGN2_KEY, DATATRANS_SECURITY = Security.DYNAMIC_SIGN, } = process.env;
|
|
4
|
+
const logger = createLogger('unchained:datatrans:handler');
|
|
5
|
+
export const datatransHandler = async (req, reply) => {
|
|
6
|
+
const resolvedContext = req.unchainedContext;
|
|
7
|
+
const { modules, services } = resolvedContext;
|
|
8
|
+
const signature = req.headers['datatrans-signature'];
|
|
9
|
+
if (!DATATRANS_SIGN_KEY && !DATATRANS_SIGN2_KEY) {
|
|
10
|
+
logger.warn('No sign key configured');
|
|
11
|
+
return reply.send({ success: false, message: 'No sign key configured', name: 'NO_SIGN_KEY' });
|
|
12
|
+
}
|
|
13
|
+
if (signature) {
|
|
14
|
+
const [rawTimestamp, rawHash] = signature.split(',');
|
|
15
|
+
const [, hash] = rawHash.split('=');
|
|
16
|
+
const [, timestamp] = rawTimestamp.split('=');
|
|
17
|
+
const comparableSignature = await generateSignature({
|
|
18
|
+
security: DATATRANS_SECURITY,
|
|
19
|
+
signKey: DATATRANS_SIGN2_KEY || DATATRANS_SIGN_KEY,
|
|
20
|
+
})(timestamp, req.body);
|
|
21
|
+
if (hash !== comparableSignature) {
|
|
22
|
+
logger.error(`hash mismatch: ${signature} / ${comparableSignature}`);
|
|
23
|
+
reply.status(403);
|
|
24
|
+
return reply.send({ success: false, message: 'Invalid Signature', name: 'HASH_MISMATCH' });
|
|
25
|
+
}
|
|
26
|
+
const transaction = JSON.parse(req.body);
|
|
27
|
+
logger.info(`received request`, {
|
|
28
|
+
type: transaction.type,
|
|
29
|
+
});
|
|
30
|
+
if (transaction.status === 'authorized') {
|
|
31
|
+
const userId = transaction.refno2;
|
|
32
|
+
const referenceId = Buffer.from(transaction.refno, 'base64').toString('hex');
|
|
33
|
+
try {
|
|
34
|
+
if (transaction.type === 'card_check') {
|
|
35
|
+
const paymentProviderId = referenceId;
|
|
36
|
+
const paymentCredentials = await services.orders.registerPaymentCredentials(paymentProviderId, { userId, transactionContext: { transactionId: transaction.transactionId } });
|
|
37
|
+
logger.info(`registered payment credentials for ${userId}`, {
|
|
38
|
+
userId,
|
|
39
|
+
});
|
|
40
|
+
reply.status(200);
|
|
41
|
+
return reply.send(paymentCredentials);
|
|
42
|
+
}
|
|
43
|
+
if (transaction.type === 'payment') {
|
|
44
|
+
const orderPaymentId = referenceId;
|
|
45
|
+
const orderPayment = await modules.orders.payments.findOrderPayment({
|
|
46
|
+
orderPaymentId,
|
|
47
|
+
});
|
|
48
|
+
if (!orderPayment)
|
|
49
|
+
throw new Error(`Order Payment with id ${orderPaymentId} not found`);
|
|
50
|
+
const order = await services.orders.checkoutOrder(orderPayment.orderId, {
|
|
51
|
+
paymentContext: { userId, transactionId: transaction.transactionId },
|
|
52
|
+
});
|
|
53
|
+
if (!order)
|
|
54
|
+
throw new Error(`Order with id ${orderPayment.orderId} not found`);
|
|
55
|
+
reply.status(200);
|
|
56
|
+
logger.info(`confirmed checkout for order ${order.orderNumber}`, {
|
|
57
|
+
orderId: order._id,
|
|
58
|
+
});
|
|
59
|
+
return reply.send(order);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
logger.error(e);
|
|
64
|
+
reply.status(500);
|
|
65
|
+
return reply.send({ success: false, name: e.name, code: e.code, message: e.message });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
reply.status(404);
|
|
70
|
+
return reply.send();
|
|
71
|
+
};
|