@riocrypto/common-server 1.0.2891 → 1.0.2892
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/build/helpers/get-processor.d.ts +9 -4
- package/build/helpers/get-processor.js +38 -15
- package/build/helpers/payin-destinations.d.ts +2 -1
- package/build/helpers/payin-destinations.js +8 -4
- package/build/helpers/payin-verification.js +19 -4
- package/build/helpers/processor-enablement.d.ts +23 -4
- package/build/helpers/processor-enablement.js +103 -18
- package/build/helpers/record-observed-processor-readiness.js +30 -5
- package/build/helpers/resolve-processor.d.ts +5 -7
- package/build/helpers/resolve-processor.js +90 -26
- package/build/models/order.d.ts +3 -1
- package/build/models/order.js +3 -0
- package/build/models/quote.d.ts +3 -1
- package/build/models/quote.js +3 -0
- package/build/models/rio-settings.d.ts +2 -0
- package/build/models/rio-settings.js +3 -0
- package/package.json +2 -2
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { Country, Fiat, Processor, ProcessorRoutingConfig, Side } from "@riocrypto/common";
|
|
2
|
-
export declare const
|
|
1
|
+
import { Country, Fiat, Processor, ProcessorEnablementState, ProcessorRoutingConfig, ProcessorRoutingPurpose, RoutingDecision, Side } from "@riocrypto/common";
|
|
2
|
+
export declare const getEnvironmentExcludedProcessors: (purpose: ProcessorRoutingPurpose) => Processor[];
|
|
3
|
+
export type ProcessorSelection = RoutingDecision & {
|
|
4
|
+
processor: Processor;
|
|
5
|
+
};
|
|
6
|
+
export declare const getProcessor: ({ country, fiat, side, isBinanceRFQ, routingConfig, enablement, requireExplicitPayinEnablement, customRioBankAccount, }: {
|
|
3
7
|
country: Country;
|
|
4
8
|
fiat: Fiat;
|
|
5
9
|
side: Side;
|
|
6
10
|
isBinanceRFQ: boolean;
|
|
7
11
|
routingConfig?: ProcessorRoutingConfig | undefined;
|
|
8
|
-
|
|
12
|
+
enablement?: ProcessorEnablementState | undefined;
|
|
13
|
+
requireExplicitPayinEnablement?: boolean | undefined;
|
|
9
14
|
customRioBankAccount?: {
|
|
10
15
|
processor?: Processor | undefined;
|
|
11
16
|
} | undefined;
|
|
12
|
-
}) =>
|
|
17
|
+
}) => ProcessorSelection;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getProcessor = void 0;
|
|
3
|
+
exports.getProcessor = exports.getEnvironmentExcludedProcessors = void 0;
|
|
4
4
|
const common_1 = require("@riocrypto/common");
|
|
5
5
|
const resolve_processor_1 = require("./resolve-processor");
|
|
6
6
|
// Alfin takes effect only in Production (Alfin prod). In every other
|
|
@@ -18,22 +18,33 @@ const isAlfinEnabled = () => [common_1.RioEnv.Production].includes(process.env.R
|
|
|
18
18
|
const isSTPAvailable = (purpose) => (purpose === common_1.ProcessorRoutingPurpose.Buy
|
|
19
19
|
? [common_1.RioEnv.Production]
|
|
20
20
|
: [common_1.RioEnv.Production, common_1.RioEnv.Development]).includes(process.env.RIO_ENV);
|
|
21
|
-
|
|
21
|
+
// Rails this deployment cannot reach for a purpose, whatever the settings say.
|
|
22
|
+
// Exported because seeding a customer's first payin account has to make the same
|
|
23
|
+
// call: handing someone an account that only exists in production would look like
|
|
24
|
+
// opt-in having locked them out.
|
|
25
|
+
const getEnvironmentExcludedProcessors = (purpose) => [
|
|
26
|
+
...(isAlfinEnabled() ? [] : [common_1.Processor.Alfin]),
|
|
27
|
+
...(isSTPAvailable(purpose) ? [] : [common_1.Processor.SPEI_STP]),
|
|
28
|
+
];
|
|
29
|
+
exports.getEnvironmentExcludedProcessors = getEnvironmentExcludedProcessors;
|
|
30
|
+
const getProcessor = ({ country, fiat, side, isBinanceRFQ, routingConfig, enablement, requireExplicitPayinEnablement, customRioBankAccount, }) => {
|
|
22
31
|
if (isBinanceRFQ === true) {
|
|
23
|
-
return
|
|
32
|
+
return {
|
|
33
|
+
processor: common_1.Processor.BinanceRFQ,
|
|
34
|
+
reason: common_1.RoutingDecisionReason.Structural,
|
|
35
|
+
corridor: [common_1.Processor.BinanceRFQ],
|
|
36
|
+
decidedAt: new Date(),
|
|
37
|
+
};
|
|
24
38
|
}
|
|
25
39
|
const purpose = side === common_1.Side.Buy
|
|
26
40
|
? common_1.ProcessorRoutingPurpose.Buy
|
|
27
41
|
: common_1.ProcessorRoutingPurpose.Sell;
|
|
28
|
-
const excludedProcessors =
|
|
29
|
-
...(isAlfinEnabled() ? [] : [common_1.Processor.Alfin]),
|
|
30
|
-
...(isSTPAvailable(purpose) ? [] : [common_1.Processor.SPEI_STP]),
|
|
31
|
-
];
|
|
42
|
+
const excludedProcessors = (0, exports.getEnvironmentExcludedProcessors)(purpose);
|
|
32
43
|
// Applied only inside the branches that genuinely have more than one rail, so
|
|
33
44
|
// a stray rule cannot hijack structural routing (COP, USD in the US or Mexico)
|
|
34
45
|
// or bypass the country checks below. Answers even when no rule is configured,
|
|
35
46
|
// because the corridor default is only safe to use once the rails the customer
|
|
36
|
-
//
|
|
47
|
+
// will not accept are out of the running. Undefined is left for the one case
|
|
37
48
|
// that genuinely belongs to structural routing: a custom Rio bank account whose
|
|
38
49
|
// rail we do not know.
|
|
39
50
|
const trySplit = () => {
|
|
@@ -43,24 +54,30 @@ const getProcessor = ({ country, fiat, side, isBinanceRFQ, routingConfig, disabl
|
|
|
43
54
|
purpose,
|
|
44
55
|
routingConfig,
|
|
45
56
|
excludedProcessors,
|
|
46
|
-
|
|
57
|
+
enablement,
|
|
58
|
+
requireExplicitEnablement: requireExplicitPayinEnablement,
|
|
47
59
|
customRioBankAccount,
|
|
48
60
|
});
|
|
49
61
|
if (routed.processor) {
|
|
50
|
-
return routed.processor;
|
|
62
|
+
return Object.assign(Object.assign({}, routed), { processor: routed.processor });
|
|
63
|
+
}
|
|
64
|
+
// The customer has never told us which of these accounts their bank will let
|
|
65
|
+
// them pay, so there is nothing to route to. Says so in their terms, since
|
|
66
|
+
// the fix is theirs to make rather than something support has to escalate.
|
|
67
|
+
if (routed.reason === common_1.RoutingDecisionReason.NoAcceptedCandidate) {
|
|
68
|
+
throw new common_1.GenericInputError("You need to turn on a payment account for this currency before you can buy. You can do that from the payment accounts page, once your bank allows transfers to it.");
|
|
51
69
|
}
|
|
52
70
|
// Every rail in this corridor is switched off for this direction, so there is
|
|
53
71
|
// nowhere to send them - a rail at zero percent would still have taken the
|
|
54
|
-
// order. Says so specifically, and names where to fix it
|
|
55
|
-
|
|
56
|
-
if (routed.reason === "noEnabledCandidate") {
|
|
72
|
+
// order. Says so specifically, and names where to fix it.
|
|
73
|
+
if (routed.reason === common_1.RoutingDecisionReason.NoEnabledCandidate) {
|
|
57
74
|
throw new common_1.GenericInputError(side === common_1.Side.Buy
|
|
58
75
|
? "You need to enable a payment account for this currency before you can buy. You can turn one back on from the payment accounts page."
|
|
59
76
|
: "You need to enable a payout account for this currency before you can sell. You can turn one back on from the payout accounts page.");
|
|
60
77
|
}
|
|
61
78
|
// No rail in the corridor can be reached in this environment at all, which is
|
|
62
79
|
// a deployment fact rather than anything the customer can act on.
|
|
63
|
-
if (routed.reason ===
|
|
80
|
+
if (routed.reason === common_1.RoutingDecisionReason.NoEligibleCandidate) {
|
|
64
81
|
throw new common_1.GenericInputError("No payment processor is currently available for this order");
|
|
65
82
|
}
|
|
66
83
|
return undefined;
|
|
@@ -75,7 +92,13 @@ const getProcessor = ({ country, fiat, side, isBinanceRFQ, routingConfig, disabl
|
|
|
75
92
|
if (!fallback) {
|
|
76
93
|
throw new common_1.GenericInputError("No payment processor is configured for this currency");
|
|
77
94
|
}
|
|
78
|
-
return
|
|
95
|
+
return {
|
|
96
|
+
processor: fallback,
|
|
97
|
+
reason: common_1.RoutingDecisionReason.Structural,
|
|
98
|
+
corridor: (0, common_1.getProcessorsForCorridor)(country, fiat, purpose),
|
|
99
|
+
excluded: excludedProcessors,
|
|
100
|
+
decidedAt: new Date(),
|
|
101
|
+
};
|
|
79
102
|
};
|
|
80
103
|
if (fiat === common_1.Fiat.COP) {
|
|
81
104
|
if (country === common_1.Country.Colombia) {
|
|
@@ -50,11 +50,12 @@ export interface PayinDestination {
|
|
|
50
50
|
isEnabled: boolean;
|
|
51
51
|
readinessStatus?: ProcessorReadinessStatus;
|
|
52
52
|
}
|
|
53
|
-
export declare const getPayinDestinations: ({ mongoose, user, country, fiat, processors, provision, }: {
|
|
53
|
+
export declare const getPayinDestinations: ({ mongoose, user, country, fiat, processors, provision, requireExplicitEnablement, }: {
|
|
54
54
|
mongoose: Mongoose;
|
|
55
55
|
user: User;
|
|
56
56
|
country: Country;
|
|
57
57
|
fiat: Fiat;
|
|
58
58
|
processors: Processor[];
|
|
59
59
|
provision?: boolean | undefined;
|
|
60
|
+
requireExplicitEnablement?: boolean | undefined;
|
|
60
61
|
}) => Promise<PayinDestination[]>;
|
|
@@ -17,6 +17,7 @@ const fintoc_deposit_CLABE_1 = require("../models/fintoc-deposit-CLABE");
|
|
|
17
17
|
const alfin_virtual_cci_1 = require("../models/alfin-virtual-cci");
|
|
18
18
|
const processor_readiness_1 = require("../models/processor-readiness");
|
|
19
19
|
const cluster_client_1 = require("../clients/cluster-client");
|
|
20
|
+
const processor_enablement_1 = require("./processor-enablement");
|
|
20
21
|
// Rails that give each user their own destination, which therefore has to exist
|
|
21
22
|
// before we can tell a customer where to pay. Every other rail is a shared house
|
|
22
23
|
// account that is always available.
|
|
@@ -288,7 +289,7 @@ exports.getPayinBankDetails = getPayinBankDetails;
|
|
|
288
289
|
// that tells them what to whitelist at their bank. Read-only by default so that
|
|
289
290
|
// viewing the list never allocates anything at a provider; pass `provision` to
|
|
290
291
|
// create the destinations that do not exist yet.
|
|
291
|
-
const getPayinDestinations = ({ mongoose, user, country, fiat, processors, provision = false, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
292
|
+
const getPayinDestinations = ({ mongoose, user, country, fiat, processors, provision = false, requireExplicitEnablement, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
292
293
|
var _d, _e;
|
|
293
294
|
const ProcessorReadiness = (0, processor_readiness_1.buildProcessorReadiness)(mongoose);
|
|
294
295
|
const readiness = yield ProcessorReadiness.find({
|
|
@@ -300,9 +301,12 @@ const getPayinDestinations = ({ mongoose, user, country, fiat, processors, provi
|
|
|
300
301
|
.lean();
|
|
301
302
|
const readinessByProcessor = new Map(readiness.map((record) => [record.processor, record]));
|
|
302
303
|
const isEnabled = (processor) => {
|
|
303
|
-
var _a
|
|
304
|
-
return (
|
|
305
|
-
|
|
304
|
+
var _a;
|
|
305
|
+
return (0, processor_enablement_1.isProcessorEnabled)({
|
|
306
|
+
enablement: (_a = readinessByProcessor.get(processor)) === null || _a === void 0 ? void 0 : _a.enablement,
|
|
307
|
+
side: common_1.Side.Buy,
|
|
308
|
+
requireExplicitEnablement,
|
|
309
|
+
});
|
|
306
310
|
};
|
|
307
311
|
const destinations = [];
|
|
308
312
|
for (const processor of processors) {
|
|
@@ -53,10 +53,11 @@ exports.extractTestReference = extractTestReference;
|
|
|
53
53
|
// arrives. Deliberately does not touch an already-verified record - there is
|
|
54
54
|
// nothing left to prove.
|
|
55
55
|
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
56
|
+
// Passing the test does switch the rail on for buys, when the customer has not
|
|
57
|
+
// already said either way. Sending us the deposit is their bank demonstrating the
|
|
58
|
+
// transfer is allowed, which is the same thing the toggle asserts, so asking for
|
|
59
|
+
// both would be asking them to confirm what we just watched happen. It never
|
|
60
|
+
// overrides a rail they have deliberately switched off.
|
|
60
61
|
//
|
|
61
62
|
// The token does not expire. Whitelisting an account at a bank can take days of
|
|
62
63
|
// back and forth at the customer's end, and expiring the token in the middle of
|
|
@@ -144,6 +145,20 @@ const matchPendingPayinVerification = ({ mongoose, reference, amount, fiat, proc
|
|
|
144
145
|
if (!matched) {
|
|
145
146
|
return undefined;
|
|
146
147
|
}
|
|
148
|
+
// Left out of the transition above so that a failure here cannot cost us the
|
|
149
|
+
// verification itself, which is the part that must not be repeated. A rail
|
|
150
|
+
// switched off keeps its answer, and one already on is not restamped.
|
|
151
|
+
yield ProcessorReadiness.updateOne({
|
|
152
|
+
_id: matched._id,
|
|
153
|
+
[`enablement.${common_1.Side.Buy}`]: { $exists: false },
|
|
154
|
+
}, {
|
|
155
|
+
$set: {
|
|
156
|
+
[`enablement.${common_1.Side.Buy}`]: {
|
|
157
|
+
isEnabled: true,
|
|
158
|
+
updatedAt: new Date(),
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
});
|
|
147
162
|
return {
|
|
148
163
|
userId: matched.userId,
|
|
149
164
|
country: matched.country,
|
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
import { Country, Fiat, Processor, Side } from "@riocrypto/common";
|
|
1
|
+
import { Country, Fiat, Processor, ProcessorEnablementState, Side } from "@riocrypto/common";
|
|
2
2
|
import { Mongoose } from "mongoose";
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const isProcessorEnabled: ({ enablement, side, requireExplicitEnablement, }: {
|
|
4
|
+
enablement?: {
|
|
5
|
+
buy?: {
|
|
6
|
+
isEnabled: boolean;
|
|
7
|
+
} | undefined;
|
|
8
|
+
sell?: {
|
|
9
|
+
isEnabled: boolean;
|
|
10
|
+
} | undefined;
|
|
11
|
+
} | undefined;
|
|
12
|
+
side: Side;
|
|
13
|
+
requireExplicitEnablement?: boolean | undefined;
|
|
14
|
+
}) => boolean;
|
|
15
|
+
export declare const getProcessorEnablementState: ({ mongoose, userId, country, fiat, side, }: {
|
|
4
16
|
mongoose: Mongoose;
|
|
5
17
|
userId: string;
|
|
6
18
|
country: Country;
|
|
7
19
|
fiat: Fiat;
|
|
8
20
|
side: Side;
|
|
9
|
-
}) => Promise<
|
|
10
|
-
export declare const setProcessorEnablement: ({ mongoose, userId, country, fiat, side, processor, isEnabled, adminId, }: {
|
|
21
|
+
}) => Promise<ProcessorEnablementState>;
|
|
22
|
+
export declare const setProcessorEnablement: ({ mongoose, userId, country, fiat, side, processor, isEnabled, adminId, requireExplicitEnablement, }: {
|
|
11
23
|
mongoose: Mongoose;
|
|
12
24
|
userId: string;
|
|
13
25
|
country: Country;
|
|
@@ -16,4 +28,11 @@ export declare const setProcessorEnablement: ({ mongoose, userId, country, fiat,
|
|
|
16
28
|
processor: Processor;
|
|
17
29
|
isEnabled: boolean;
|
|
18
30
|
adminId?: string | undefined;
|
|
31
|
+
requireExplicitEnablement?: boolean | undefined;
|
|
19
32
|
}) => Promise<void>;
|
|
33
|
+
export declare const ensureDefaultPayinEnablement: ({ mongoose, userId, country, fiat, }: {
|
|
34
|
+
mongoose: Mongoose;
|
|
35
|
+
userId: string;
|
|
36
|
+
country: Country;
|
|
37
|
+
fiat: Fiat;
|
|
38
|
+
}) => Promise<Processor | undefined>;
|
|
@@ -9,34 +9,69 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.setProcessorEnablement = exports.
|
|
12
|
+
exports.ensureDefaultPayinEnablement = exports.setProcessorEnablement = exports.getProcessorEnablementState = exports.isProcessorEnabled = void 0;
|
|
13
13
|
const common_1 = require("@riocrypto/common");
|
|
14
14
|
const processor_readiness_1 = require("../models/processor-readiness");
|
|
15
|
+
const get_processor_1 = require("./get-processor");
|
|
15
16
|
// Enablement is asked per side, so the purpose is fixed by the direction rather
|
|
16
17
|
// than chosen by the caller: verifications are ours to place and are never gated.
|
|
17
18
|
const getPurpose = (side) => side === common_1.Side.Buy
|
|
18
19
|
? common_1.ProcessorRoutingPurpose.Buy
|
|
19
20
|
: common_1.ProcessorRoutingPurpose.Sell;
|
|
20
|
-
//
|
|
21
|
-
// routing skips. On a buy that means a rail their bank will not let them send
|
|
22
|
-
// money to; on a sell, one it will not accept credits from.
|
|
21
|
+
// Whether one rail may carry one customer's orders in one direction.
|
|
23
22
|
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
23
|
+
// The two directions disagree about silence, which is the whole point. A payout
|
|
24
|
+
// needs nothing arranged at the customer's bank, so a rail nobody has spoken
|
|
25
|
+
// about can pay them. A payin asks them to send money to an account their bank
|
|
26
|
+
// has to allow first, so silence means no while payin accounts are opt-in.
|
|
27
|
+
const isProcessorEnabled = ({ enablement, side, requireExplicitEnablement, }) => {
|
|
28
|
+
var _a, _b;
|
|
29
|
+
return (_b = (_a = enablement === null || enablement === void 0 ? void 0 : enablement[side]) === null || _a === void 0 ? void 0 : _a.isEnabled) !== null && _b !== void 0 ? _b : (side === common_1.Side.Sell || !requireExplicitEnablement);
|
|
30
|
+
};
|
|
31
|
+
exports.isProcessorEnabled = isProcessorEnabled;
|
|
32
|
+
// What a customer has said about each rail in one corridor and one direction.
|
|
33
|
+
//
|
|
34
|
+
// Reports the three sets rather than a verdict so that the policy lives in one
|
|
35
|
+
// place, in routing, instead of being decided here and again wherever a page
|
|
36
|
+
// needs to show the same thing. Only rails the corridor currently serves are
|
|
37
|
+
// reported, so a record left behind by a rail we no longer route to is ignored
|
|
38
|
+
// rather than resurrected.
|
|
39
|
+
const getProcessorEnablementState = ({ mongoose, userId, country, fiat, side, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
40
|
const ProcessorReadiness = (0, processor_readiness_1.buildProcessorReadiness)(mongoose);
|
|
29
|
-
const
|
|
41
|
+
const records = yield ProcessorReadiness.find({
|
|
30
42
|
userId,
|
|
31
43
|
country,
|
|
32
44
|
fiat,
|
|
33
|
-
[`enablement.${side}.isEnabled`]: false,
|
|
34
45
|
})
|
|
35
|
-
.select({ processor: 1 })
|
|
46
|
+
.select({ processor: 1, enablement: 1 })
|
|
36
47
|
.lean();
|
|
37
|
-
|
|
48
|
+
const stated = new Map(records.map((record) => {
|
|
49
|
+
var _a, _b;
|
|
50
|
+
return [
|
|
51
|
+
record.processor,
|
|
52
|
+
(_b = (_a = record.enablement) === null || _a === void 0 ? void 0 : _a[side]) === null || _b === void 0 ? void 0 : _b.isEnabled,
|
|
53
|
+
];
|
|
54
|
+
}));
|
|
55
|
+
const state = {
|
|
56
|
+
enabled: [],
|
|
57
|
+
disabled: [],
|
|
58
|
+
unset: [],
|
|
59
|
+
};
|
|
60
|
+
for (const processor of (0, common_1.getProcessorsForCorridor)(country, fiat, getPurpose(side))) {
|
|
61
|
+
const isEnabled = stated.get(processor);
|
|
62
|
+
if (isEnabled === true) {
|
|
63
|
+
state.enabled.push(processor);
|
|
64
|
+
}
|
|
65
|
+
else if (isEnabled === false) {
|
|
66
|
+
state.disabled.push(processor);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
state.unset.push(processor);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return state;
|
|
38
73
|
});
|
|
39
|
-
exports.
|
|
74
|
+
exports.getProcessorEnablementState = getProcessorEnablementState;
|
|
40
75
|
// Turns a rail on or off for one user in one direction, which is what order
|
|
41
76
|
// routing gates on. Deliberately says nothing about verification: a customer can
|
|
42
77
|
// enable a rail they have never tested, and switching one off leaves its test
|
|
@@ -44,8 +79,9 @@ exports.getDisabledProcessors = getDisabledProcessors;
|
|
|
44
79
|
//
|
|
45
80
|
// Refuses to switch off the last rail standing. Enabling nothing means we have
|
|
46
81
|
// nowhere to send their money, and finding that out when they try to place an
|
|
47
|
-
// order is worse than being told here.
|
|
48
|
-
|
|
82
|
+
// order is worse than being told here. Turning another on first is always open to
|
|
83
|
+
// them, so nobody is stuck with a rail they cannot use.
|
|
84
|
+
const setProcessorEnablement = ({ mongoose, userId, country, fiat, side, processor, isEnabled, adminId, requireExplicitEnablement, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
49
85
|
const corridorProcessors = (0, common_1.getProcessorsForCorridor)(country, fiat, getPurpose(side));
|
|
50
86
|
if (!corridorProcessors.includes(processor)) {
|
|
51
87
|
throw new common_1.GenericInputError(side === common_1.Side.Buy
|
|
@@ -53,15 +89,18 @@ const setProcessorEnablement = ({ mongoose, userId, country, fiat, side, process
|
|
|
53
89
|
: "This payout rail is not available for this country and currency");
|
|
54
90
|
}
|
|
55
91
|
if (!isEnabled) {
|
|
56
|
-
const
|
|
92
|
+
const state = yield (0, exports.getProcessorEnablementState)({
|
|
57
93
|
mongoose,
|
|
58
94
|
userId,
|
|
59
95
|
country,
|
|
60
96
|
fiat,
|
|
61
97
|
side,
|
|
62
98
|
});
|
|
63
|
-
const
|
|
64
|
-
|
|
99
|
+
const stillUsable = [
|
|
100
|
+
...state.enabled,
|
|
101
|
+
...(side === common_1.Side.Sell || !requireExplicitEnablement ? state.unset : []),
|
|
102
|
+
].filter((candidate) => candidate !== processor);
|
|
103
|
+
if (!stillUsable.length) {
|
|
65
104
|
throw new common_1.GenericInputError(side === common_1.Side.Buy
|
|
66
105
|
? "At least one payment account has to stay enabled for this currency, otherwise there is nowhere to send your orders"
|
|
67
106
|
: "At least one payout account has to stay enabled for this currency, otherwise there is nowhere to pay your orders from");
|
|
@@ -85,3 +124,49 @@ const setProcessorEnablement = ({ mongoose, userId, country, fiat, side, process
|
|
|
85
124
|
}, { upsert: true });
|
|
86
125
|
});
|
|
87
126
|
exports.setProcessorEnablement = setProcessorEnablement;
|
|
127
|
+
// Gives a customer with no payin account turned on the one their corridor would
|
|
128
|
+
// have routed them to anyway, so opt-in never leaves somebody unable to be
|
|
129
|
+
// quoted. Called when an onboarding is approved, and by the backfill for accounts
|
|
130
|
+
// that predate all of this.
|
|
131
|
+
//
|
|
132
|
+
// Skips anyone who has already turned a rail on, because their answer is better
|
|
133
|
+
// than our guess and adding to it would put their money somewhere they have not
|
|
134
|
+
// agreed to. Where the default rail is one they have switched off, it falls to
|
|
135
|
+
// the corridor's own order, which is the rail routing would have picked for them
|
|
136
|
+
// before opt-in existed. Returns the rail it turned on, or nothing if it left
|
|
137
|
+
// them alone.
|
|
138
|
+
const ensureDefaultPayinEnablement = ({ mongoose, userId, country, fiat, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
139
|
+
const state = yield (0, exports.getProcessorEnablementState)({
|
|
140
|
+
mongoose,
|
|
141
|
+
userId,
|
|
142
|
+
country,
|
|
143
|
+
fiat,
|
|
144
|
+
side: common_1.Side.Buy,
|
|
145
|
+
});
|
|
146
|
+
if (state.enabled.length) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
// Environment exclusions are honoured here rather than left to routing, since
|
|
150
|
+
// seeding a rail this deployment cannot reach would hand the customer an
|
|
151
|
+
// account nothing can route to and read as opt-in having blocked them.
|
|
152
|
+
const unreachable = [
|
|
153
|
+
...(0, get_processor_1.getEnvironmentExcludedProcessors)(common_1.ProcessorRoutingPurpose.Buy),
|
|
154
|
+
...state.disabled,
|
|
155
|
+
];
|
|
156
|
+
const seed = (0, common_1.getDefaultProcessor)(country, fiat, common_1.ProcessorRoutingPurpose.Buy, unreachable) ||
|
|
157
|
+
state.unset.find((candidate) => !unreachable.includes(candidate));
|
|
158
|
+
if (!seed || !state.unset.includes(seed)) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
yield (0, exports.setProcessorEnablement)({
|
|
162
|
+
mongoose,
|
|
163
|
+
userId,
|
|
164
|
+
country,
|
|
165
|
+
fiat,
|
|
166
|
+
side: common_1.Side.Buy,
|
|
167
|
+
processor: seed,
|
|
168
|
+
isEnabled: true,
|
|
169
|
+
});
|
|
170
|
+
return seed;
|
|
171
|
+
});
|
|
172
|
+
exports.ensureDefaultPayinEnablement = ensureDefaultPayinEnablement;
|
|
@@ -22,16 +22,38 @@ const DUPLICATE_KEY_ERROR_CODE = 11000;
|
|
|
22
22
|
// penny test, because it is a real payment at real size. Called from the payment
|
|
23
23
|
// matching paths so the record stays current without anyone maintaining it.
|
|
24
24
|
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// is
|
|
25
|
+
// Also turns the rail on for buys when the customer has never said either way,
|
|
26
|
+
// which is what keeps opt-in from needing a second migration. A payin arriving is
|
|
27
|
+
// their bank demonstrating the transfer is allowed, so asking them to also flip a
|
|
28
|
+
// switch would be asking them to confirm something we just watched happen.
|
|
28
29
|
//
|
|
29
|
-
// Never
|
|
30
|
-
//
|
|
30
|
+
// Never overrides an explicit answer in either direction. A rail the customer
|
|
31
|
+
// switched off stays off even if a payment turns up on it, because the account
|
|
32
|
+
// they cannot send to next month is not made usable by one that arrived this
|
|
33
|
+
// month, and a verified record keeps the admin attribution it was granted with.
|
|
31
34
|
const recordObservedProcessorReadiness = ({ mongoose, userId, country, fiat, processor, CLABE, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
32
35
|
var _a;
|
|
33
36
|
const ProcessorReadiness = (0, processor_readiness_1.buildProcessorReadiness)(mongoose);
|
|
34
37
|
try {
|
|
38
|
+
const enablement = {
|
|
39
|
+
isEnabled: true,
|
|
40
|
+
updatedAt: new Date(),
|
|
41
|
+
};
|
|
42
|
+
// Separate from the write below because the two have different conditions.
|
|
43
|
+
// Verification is skipped once verified, while enablement is skipped once the
|
|
44
|
+
// customer has answered, and folding them together would let one already
|
|
45
|
+
// satisfied condition suppress the other.
|
|
46
|
+
yield ProcessorReadiness.updateOne({
|
|
47
|
+
userId,
|
|
48
|
+
country,
|
|
49
|
+
fiat,
|
|
50
|
+
processor,
|
|
51
|
+
[`enablement.${common_1.Side.Buy}`]: { $exists: false },
|
|
52
|
+
}, {
|
|
53
|
+
$set: {
|
|
54
|
+
[`enablement.${common_1.Side.Buy}`]: enablement,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
35
57
|
yield ProcessorReadiness.updateOne({
|
|
36
58
|
userId,
|
|
37
59
|
country,
|
|
@@ -46,6 +68,9 @@ const recordObservedProcessorReadiness = ({ mongoose, userId, country, fiat, pro
|
|
|
46
68
|
country,
|
|
47
69
|
fiat,
|
|
48
70
|
processor,
|
|
71
|
+
// The write above cannot reach a record that does not exist yet, and
|
|
72
|
+
// this one creates it.
|
|
73
|
+
[`enablement.${common_1.Side.Buy}`]: enablement,
|
|
49
74
|
},
|
|
50
75
|
}, { upsert: true });
|
|
51
76
|
}
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import { Country, Fiat, Processor, ProcessorRoutingConfig, ProcessorRoutingPurpose } from "@riocrypto/common";
|
|
1
|
+
import { Country, Fiat, Processor, ProcessorEnablementState, ProcessorRoutingConfig, ProcessorRoutingPurpose, RoutingDecision } from "@riocrypto/common";
|
|
2
2
|
export interface ResolveProcessorParams {
|
|
3
3
|
country: Country;
|
|
4
4
|
fiat: Fiat;
|
|
5
5
|
purpose: ProcessorRoutingPurpose;
|
|
6
6
|
routingConfig?: ProcessorRoutingConfig;
|
|
7
7
|
excludedProcessors?: Processor[];
|
|
8
|
-
|
|
8
|
+
enablement?: ProcessorEnablementState;
|
|
9
|
+
requireExplicitEnablement?: boolean;
|
|
9
10
|
customRioBankAccount?: {
|
|
10
11
|
processor?: Processor;
|
|
11
12
|
};
|
|
12
13
|
randomSeed?: number;
|
|
13
14
|
}
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
reason: "customRioBankAccount" | "customRioBankAccountWithoutRail" | "split" | "onlyCandidate" | "enabledRailWithoutShare" | "noEligibleCandidate" | "noEnabledCandidate";
|
|
17
|
-
}
|
|
18
|
-
export declare const resolveProcessor: ({ country, fiat, purpose, routingConfig, excludedProcessors, disabledProcessors, customRioBankAccount, randomSeed, }: ResolveProcessorParams) => ResolveProcessorResult;
|
|
15
|
+
export type ResolveProcessorResult = RoutingDecision;
|
|
16
|
+
export declare const resolveProcessor: ({ country, fiat, purpose, routingConfig, excludedProcessors, enablement, requireExplicitEnablement, customRioBankAccount, randomSeed, }: ResolveProcessorParams) => ResolveProcessorResult;
|
|
@@ -25,30 +25,28 @@ const pickByShare = (candidates, position) => {
|
|
|
25
25
|
// A percentage divides volume between the rails that are actually available to
|
|
26
26
|
// the customer; it is not a switch. A rail left at zero, left out of the rule, or
|
|
27
27
|
// belonging to a corridor with no rule at all still takes their orders when it is
|
|
28
|
-
// the only one
|
|
29
|
-
//
|
|
30
|
-
//
|
|
28
|
+
// the only one available to them, which is why the candidate set comes from the
|
|
29
|
+
// corridor rather than from the rule. What keeps an order away from a rail is the
|
|
30
|
+
// customer, by switching it off or, on buys, by never turning it on.
|
|
31
31
|
//
|
|
32
|
-
// Does no IO of its own - the routing config and the user's
|
|
32
|
+
// Does no IO of its own - the routing config and the user's enablement are passed
|
|
33
33
|
// in - and the only nondeterminism is the per-order roll, which a caller can
|
|
34
34
|
// pin down by supplying randomSeed. Because the roll happens once, the answer
|
|
35
35
|
// belongs to the record that stores it: anything that needs to know which rail
|
|
36
36
|
// an existing order uses reads it off the order rather than calling again.
|
|
37
37
|
//
|
|
38
|
+
// Returns the sets it filtered as well as the rail, because they are gone by the
|
|
39
|
+
// time anyone asks why. Settings move, a customer turns a rail off, an
|
|
40
|
+
// environment changes, and the question "why did this order go there" is then
|
|
41
|
+
// unanswerable from the order alone.
|
|
42
|
+
//
|
|
38
43
|
// Only handles rails that participate in a split. Structural routing that is not
|
|
39
44
|
// a commercial choice - Binance RFQ, COP, USD in the US or Mexico - stays in
|
|
40
45
|
// getProcessor and never reaches here.
|
|
41
|
-
const resolveProcessor = ({ country, fiat, purpose, routingConfig, excludedProcessors,
|
|
46
|
+
const resolveProcessor = ({ country, fiat, purpose, routingConfig, excludedProcessors, enablement, requireExplicitEnablement, customRioBankAccount, randomSeed, }) => {
|
|
47
|
+
const corridor = (0, common_1.getProcessorsForCorridor)(country, fiat, purpose);
|
|
48
|
+
const decidedAt = new Date();
|
|
42
49
|
const isEligible = (processor) => !(excludedProcessors === null || excludedProcessors === void 0 ? void 0 : excludedProcessors.includes(processor));
|
|
43
|
-
// A rail the customer has switched off for this direction is worthless whatever
|
|
44
|
-
// share it carries: on a buy they cannot send money to it, on a sell their bank
|
|
45
|
-
// will not accept the credit. Applies to every rail, verifiable or not - a bank
|
|
46
|
-
// can refuse a wire from Bancrea as easily as a SPEI transfer from Fintoc.
|
|
47
|
-
//
|
|
48
|
-
// Verifications are exempt because they are ours to place against an account the
|
|
49
|
-
// customer gave us, not a rail they have to accept.
|
|
50
|
-
const isUsable = (processor) => purpose === common_1.ProcessorRoutingPurpose.BankAccountVerification ||
|
|
51
|
-
!(disabledProcessors === null || disabledProcessors === void 0 ? void 0 : disabledProcessors.includes(processor));
|
|
52
50
|
// A custom Rio bank account outranks everything on buys, because it is where
|
|
53
51
|
// the money physically goes: `getBankDetails` returns it in place of the
|
|
54
52
|
// rail's own destination. Routing the order to a different processor would
|
|
@@ -62,16 +60,30 @@ const resolveProcessor = ({ country, fiat, purpose, routingConfig, excludedProce
|
|
|
62
60
|
// to the structural path and leave these users exactly as they were until an
|
|
63
61
|
// admin records which rail the account belongs to.
|
|
64
62
|
if (!processor) {
|
|
65
|
-
return {
|
|
63
|
+
return {
|
|
64
|
+
reason: common_1.RoutingDecisionReason.CustomRioBankAccountWithoutRail,
|
|
65
|
+
corridor,
|
|
66
|
+
decidedAt,
|
|
67
|
+
};
|
|
66
68
|
}
|
|
67
69
|
// Deliberately fail closed rather than fall through to the split. The payin
|
|
68
70
|
// destination is fixed, so if that rail cannot be used here the only honest
|
|
69
71
|
// options are to refuse the order or to keep sending payins to a provider we
|
|
70
72
|
// have stopped watching.
|
|
71
73
|
if (!isEligible(processor)) {
|
|
72
|
-
return {
|
|
74
|
+
return {
|
|
75
|
+
reason: common_1.RoutingDecisionReason.NoEligibleCandidate,
|
|
76
|
+
corridor,
|
|
77
|
+
excluded: excludedProcessors,
|
|
78
|
+
decidedAt,
|
|
79
|
+
};
|
|
73
80
|
}
|
|
74
|
-
return {
|
|
81
|
+
return {
|
|
82
|
+
processor,
|
|
83
|
+
reason: common_1.RoutingDecisionReason.CustomRioBankAccount,
|
|
84
|
+
corridor,
|
|
85
|
+
decidedAt,
|
|
86
|
+
};
|
|
75
87
|
}
|
|
76
88
|
// An absent rule is read as every rail at zero rather than as a reason to stop.
|
|
77
89
|
// Bailing out here would hand the decision to the structural default, which
|
|
@@ -83,14 +95,43 @@ const resolveProcessor = ({ country, fiat, purpose, routingConfig, excludedProce
|
|
|
83
95
|
// because a percentage decides how volume is divided between the rails a
|
|
84
96
|
// customer can use - it is not what makes a rail usable. Also drops any share
|
|
85
97
|
// stored against a rail this purpose does not serve, which nothing dispatches.
|
|
86
|
-
const
|
|
87
|
-
const eligible = corridorProcessors.filter(isEligible);
|
|
98
|
+
const eligible = corridor.filter(isEligible);
|
|
88
99
|
if (!eligible.length) {
|
|
89
|
-
return {
|
|
100
|
+
return {
|
|
101
|
+
reason: common_1.RoutingDecisionReason.NoEligibleCandidate,
|
|
102
|
+
corridor,
|
|
103
|
+
excluded: excludedProcessors,
|
|
104
|
+
decidedAt,
|
|
105
|
+
};
|
|
90
106
|
}
|
|
91
|
-
|
|
107
|
+
// A rail the customer has switched off for this direction is worthless whatever
|
|
108
|
+
// share it carries: on a buy they cannot send money to it, on a sell their bank
|
|
109
|
+
// will not accept the credit. Applies to every rail, verifiable or not - a bank
|
|
110
|
+
// can refuse a wire from Bancrea as easily as a SPEI transfer from Fintoc.
|
|
111
|
+
//
|
|
112
|
+
// Verifications are exempt because they are ours to place against an account the
|
|
113
|
+
// customer gave us, not a rail they have to accept.
|
|
114
|
+
const isGated = purpose !== common_1.ProcessorRoutingPurpose.BankAccountVerification;
|
|
115
|
+
const disabled = isGated ? (enablement === null || enablement === void 0 ? void 0 : enablement.disabled) || [] : [];
|
|
116
|
+
const unset = isGated && requireExplicitEnablement && purpose === common_1.ProcessorRoutingPurpose.Buy
|
|
117
|
+
? (enablement === null || enablement === void 0 ? void 0 : enablement.unset) || []
|
|
118
|
+
: [];
|
|
119
|
+
const candidates = eligible.filter((processor) => !disabled.includes(processor) && !unset.includes(processor));
|
|
92
120
|
if (!candidates.length) {
|
|
93
|
-
return {
|
|
121
|
+
return {
|
|
122
|
+
// Told apart because they are different conversations. One customer turned
|
|
123
|
+
// their rails off and can turn one back on; the other has never told us
|
|
124
|
+
// which account their bank will let them pay, and needs to whitelist one
|
|
125
|
+
// before anything can be routed.
|
|
126
|
+
reason: unset.length
|
|
127
|
+
? common_1.RoutingDecisionReason.NoAcceptedCandidate
|
|
128
|
+
: common_1.RoutingDecisionReason.NoEnabledCandidate,
|
|
129
|
+
corridor,
|
|
130
|
+
excluded: excludedProcessors,
|
|
131
|
+
disabled,
|
|
132
|
+
unset,
|
|
133
|
+
decidedAt,
|
|
134
|
+
};
|
|
94
135
|
}
|
|
95
136
|
const shares = candidates
|
|
96
137
|
.map((processor) => {
|
|
@@ -101,6 +142,7 @@ const resolveProcessor = ({ country, fiat, purpose, routingConfig, excludedProce
|
|
|
101
142
|
});
|
|
102
143
|
})
|
|
103
144
|
.filter((candidate) => candidate.percentage > 0);
|
|
145
|
+
const percentages = Object.fromEntries(shares.map((share) => [share.processor, share.percentage]));
|
|
104
146
|
// None of the rails this customer can use carries a share - or no rule is
|
|
105
147
|
// configured at all - so there is no split to draw from. Refusing the order
|
|
106
148
|
// would read a zero as "never use this rail", when all it says is where volume
|
|
@@ -111,18 +153,40 @@ const resolveProcessor = ({ country, fiat, purpose, routingConfig, excludedProce
|
|
|
111
153
|
const fallback = (0, common_1.getDefaultProcessor)(country, fiat, purpose, excludedProcessors);
|
|
112
154
|
return {
|
|
113
155
|
processor: fallback && candidates.includes(fallback) ? fallback : candidates[0],
|
|
114
|
-
reason:
|
|
156
|
+
reason: common_1.RoutingDecisionReason.EnabledRailWithoutShare,
|
|
157
|
+
corridor,
|
|
158
|
+
excluded: excludedProcessors,
|
|
159
|
+
disabled,
|
|
160
|
+
unset,
|
|
161
|
+
decidedAt,
|
|
115
162
|
};
|
|
116
163
|
}
|
|
117
164
|
if (shares.length === 1) {
|
|
118
|
-
return {
|
|
165
|
+
return {
|
|
166
|
+
processor: shares[0].processor,
|
|
167
|
+
reason: common_1.RoutingDecisionReason.OnlyCandidate,
|
|
168
|
+
corridor,
|
|
169
|
+
excluded: excludedProcessors,
|
|
170
|
+
disabled,
|
|
171
|
+
unset,
|
|
172
|
+
percentages,
|
|
173
|
+
decidedAt,
|
|
174
|
+
};
|
|
119
175
|
}
|
|
120
176
|
// Rolled per order, so the realized share converges on the configured
|
|
121
177
|
// percentages by order count. Nothing is persisted here: a caller that needs
|
|
122
178
|
// the same answer twice passes back the seed it used.
|
|
179
|
+
const seed = randomSeed !== null && randomSeed !== void 0 ? randomSeed : Math.random();
|
|
123
180
|
return {
|
|
124
|
-
processor: pickByShare(shares,
|
|
125
|
-
reason:
|
|
181
|
+
processor: pickByShare(shares, seed),
|
|
182
|
+
reason: common_1.RoutingDecisionReason.Split,
|
|
183
|
+
corridor,
|
|
184
|
+
excluded: excludedProcessors,
|
|
185
|
+
disabled,
|
|
186
|
+
unset,
|
|
187
|
+
percentages,
|
|
188
|
+
seed,
|
|
189
|
+
decidedAt,
|
|
126
190
|
};
|
|
127
191
|
};
|
|
128
192
|
exports.resolveProcessor = resolveProcessor;
|
package/build/models/order.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Country, Fees, Fiat, LiquidityProvider, OrderStatus, PaymentMethod, PayoutMethod, Processor, Side, Crypto, OrderAttribute, DeferredPaymentType, TwoWaySettlementType, OrderType } from "@riocrypto/common";
|
|
1
|
+
import { Country, Fees, Fiat, LiquidityProvider, OrderStatus, PaymentMethod, PayoutMethod, Processor, RoutingDecision, Side, Crypto, OrderAttribute, DeferredPaymentType, TwoWaySettlementType, OrderType } from "@riocrypto/common";
|
|
2
2
|
import { Mongoose, Model, Document, HydratedDocument } from "mongoose";
|
|
3
3
|
interface OrderAttrs {
|
|
4
4
|
quoteId: string;
|
|
@@ -21,6 +21,7 @@ interface OrderAttrs {
|
|
|
21
21
|
amountCryptoReceived?: number;
|
|
22
22
|
liquidityProvider: LiquidityProvider;
|
|
23
23
|
processor: Processor;
|
|
24
|
+
routingDecision?: RoutingDecision;
|
|
24
25
|
fees: Fees;
|
|
25
26
|
actualFees?: Fees;
|
|
26
27
|
wireMessage?: string;
|
|
@@ -172,6 +173,7 @@ interface OrderDoc extends Document {
|
|
|
172
173
|
amountCryptoReceived?: number;
|
|
173
174
|
liquidityProvider: LiquidityProvider;
|
|
174
175
|
processor: Processor;
|
|
176
|
+
routingDecision?: RoutingDecision;
|
|
175
177
|
fees: Fees;
|
|
176
178
|
actualFees?: Fees;
|
|
177
179
|
marketPrice: number;
|
package/build/models/order.js
CHANGED
package/build/models/quote.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Country, Fees, Fiat, LiquidityProvider, PaymentMethod, PayoutMethod, Processor, Side, Crypto, DeferredPaymentType, TwoWaySettlementType, OrderType } from "@riocrypto/common";
|
|
1
|
+
import { Country, Fees, Fiat, LiquidityProvider, PaymentMethod, PayoutMethod, Processor, RoutingDecision, Side, Crypto, DeferredPaymentType, TwoWaySettlementType, OrderType } from "@riocrypto/common";
|
|
2
2
|
import { Mongoose, Model, Document, HydratedDocument } from "mongoose";
|
|
3
3
|
interface QuoteAttrs {
|
|
4
4
|
userId: string;
|
|
@@ -15,6 +15,7 @@ interface QuoteAttrs {
|
|
|
15
15
|
amountCrypto: number;
|
|
16
16
|
liquidityProvider: LiquidityProvider;
|
|
17
17
|
processor: Processor;
|
|
18
|
+
routingDecision?: RoutingDecision;
|
|
18
19
|
fees: Fees;
|
|
19
20
|
assetPriceInUSD: number;
|
|
20
21
|
actualAssetPriceInUSD?: number;
|
|
@@ -61,6 +62,7 @@ interface QuoteDoc extends Document {
|
|
|
61
62
|
amountCrypto: number;
|
|
62
63
|
liquidityProvider: LiquidityProvider;
|
|
63
64
|
processor: Processor;
|
|
65
|
+
routingDecision?: RoutingDecision;
|
|
64
66
|
createdAt: Date;
|
|
65
67
|
timeInForceStartsAt?: Date;
|
|
66
68
|
timeInForceEndsAt?: Date;
|
package/build/models/quote.js
CHANGED
|
@@ -55,6 +55,7 @@ interface RioSettingsAttrs {
|
|
|
55
55
|
};
|
|
56
56
|
fxTradingPolicies?: FXTradingPolicies;
|
|
57
57
|
processorRouting?: ProcessorRoutingConfig;
|
|
58
|
+
requireExplicitPayinEnablement?: boolean;
|
|
58
59
|
TVFXDataProvider: {
|
|
59
60
|
[key in Fiat]: TVFXDataProvider;
|
|
60
61
|
};
|
|
@@ -149,6 +150,7 @@ interface RioSettingsDoc extends mongoose.Document {
|
|
|
149
150
|
};
|
|
150
151
|
fxTradingPolicies?: FXTradingPolicies;
|
|
151
152
|
processorRouting?: ProcessorRoutingConfig;
|
|
153
|
+
requireExplicitPayinEnablement?: boolean;
|
|
152
154
|
TVFXDataProvider: {
|
|
153
155
|
[key in Fiat]: TVFXDataProvider;
|
|
154
156
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riocrypto/common-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2892",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@google-cloud/secret-manager": "^5.6.0",
|
|
29
29
|
"@google-cloud/storage": "^7.19.0",
|
|
30
30
|
"@hyperdx/node-opentelemetry": "^0.10.3",
|
|
31
|
-
"@riocrypto/common": "1.0.
|
|
31
|
+
"@riocrypto/common": "1.0.2706",
|
|
32
32
|
"@slack/web-api": "^7.15.0",
|
|
33
33
|
"@types/express": "^4.17.25",
|
|
34
34
|
"axios": "1.18.1",
|