@riocrypto/common-server 1.0.2880 → 1.0.2882
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/payin-destinations.d.ts +24 -1
- package/build/helpers/payin-destinations.js +87 -18
- package/build/index.d.ts +2 -0
- package/build/index.js +2 -0
- package/build/models/alfin-payin-notification.d.ts +2 -0
- package/build/models/alfin-payin-notification.js +17 -0
- package/build/models/alfin-payin-origin.d.ts +39 -0
- package/build/models/alfin-payin-origin.js +65 -0
- package/build/models/alfin-statement-credit.d.ts +55 -0
- package/build/models/alfin-statement-credit.js +91 -0
- package/package.json +2 -2
|
@@ -11,14 +11,37 @@ export interface PayinBankDetails {
|
|
|
11
11
|
RFC?: string;
|
|
12
12
|
bankAddress?: string;
|
|
13
13
|
NIT?: string;
|
|
14
|
+
largeTransferCCI?: string;
|
|
14
15
|
}
|
|
15
16
|
export declare const PER_USER_PAYIN_PROCESSORS: Processor[];
|
|
16
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Per-operation ceiling of the CCE's immediate transfer service, set by the BCRP.
|
|
19
|
+
*
|
|
20
|
+
* A virtual CCI accepts nothing but immediate transfers, so a payment above this
|
|
21
|
+
* cannot be sent to one at all: the payer's bank has to use deferred CCE or LBTR,
|
|
22
|
+
* and both are refused by a CCIV. Those payments go to the principal account
|
|
23
|
+
* instead, which is why the ceiling decides which CCI a customer is shown.
|
|
24
|
+
*/
|
|
25
|
+
export declare const CCE_IMMEDIATE_CEILING_BY_FIAT: Partial<Record<Fiat, number>>;
|
|
26
|
+
export declare const normalizeCci: (cci: string) => string;
|
|
27
|
+
/**
|
|
28
|
+
* Whether a CCI is one of Rio's own Peru accounts.
|
|
29
|
+
*
|
|
30
|
+
* Rio funds Alfin from its Interbank accounts, so those transfers appear on the
|
|
31
|
+
* Alfin statement as ordinary interbank credits carrying Rio's own account as
|
|
32
|
+
* the payer. Anything reading the statement to attribute customer payments has
|
|
33
|
+
* to exclude them, or a treasury top-up would be credited to whichever customer
|
|
34
|
+
* order happened to be awaiting the same amount.
|
|
35
|
+
*/
|
|
36
|
+
export declare const isRioOwnPeruAccountCci: (cci?: string) => boolean;
|
|
37
|
+
export declare const getPayinBankDetails: ({ mongoose, processor, fiat, country, user, amount, requireSharedAlfinAccount, }: {
|
|
17
38
|
mongoose: Mongoose;
|
|
18
39
|
processor: Processor;
|
|
19
40
|
fiat: Fiat;
|
|
20
41
|
country: Country;
|
|
21
42
|
user: User;
|
|
43
|
+
amount?: number | undefined;
|
|
44
|
+
requireSharedAlfinAccount?: boolean | undefined;
|
|
22
45
|
}) => Promise<PayinBankDetails>;
|
|
23
46
|
export interface PayinDestination {
|
|
24
47
|
processor: Processor;
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.getPayinDestinations = exports.getPayinBankDetails = exports.PER_USER_PAYIN_PROCESSORS = void 0;
|
|
12
|
+
exports.getPayinDestinations = exports.getPayinBankDetails = exports.isRioOwnPeruAccountCci = exports.normalizeCci = exports.CCE_IMMEDIATE_CEILING_BY_FIAT = exports.PER_USER_PAYIN_PROCESSORS = void 0;
|
|
13
13
|
const common_1 = require("@riocrypto/common");
|
|
14
14
|
const rio_bank_account_1 = require("../models/rio-bank-account");
|
|
15
15
|
const STP_deposit_CLABE_1 = require("../models/STP-deposit-CLABE");
|
|
@@ -33,13 +33,60 @@ const PERU_COMPANY_ADDRESS = "Calle General de la Fuente, Edificio 393, dep. 102
|
|
|
33
33
|
const PERU_RUC = "20610425713";
|
|
34
34
|
// Rio's Alfin Cuenta Vista per currency. A CCIV is reachable only through the
|
|
35
35
|
// interbank clearing house, so a customer who banks at Alfin cannot pay one from
|
|
36
|
-
// inside Alfin and needs the underlying account number.
|
|
37
|
-
//
|
|
38
|
-
//
|
|
36
|
+
// inside Alfin and needs the underlying account number. A payment sent here
|
|
37
|
+
// carries no CCIV, so Alfin raises no payin notification for it: it is picked up
|
|
38
|
+
// by the treasury statement import and attributed from there.
|
|
39
39
|
const ALFIN_ACCOUNT_NUMBER_BY_FIAT = {
|
|
40
40
|
[common_1.Fiat.PEN]: "01818233900001",
|
|
41
41
|
[common_1.Fiat.USD]: "01818233900002",
|
|
42
42
|
};
|
|
43
|
+
// The same accounts as CCIs, for payers at another bank. An account number is
|
|
44
|
+
// only addressable from inside Alfin, so this is what a customer needs when the
|
|
45
|
+
// payment cannot go to their CCIV.
|
|
46
|
+
const ALFIN_PRINCIPAL_CCI_BY_FIAT = {
|
|
47
|
+
[common_1.Fiat.PEN]: "05810001818233900196",
|
|
48
|
+
[common_1.Fiat.USD]: "05810001818233900294",
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Per-operation ceiling of the CCE's immediate transfer service, set by the BCRP.
|
|
52
|
+
*
|
|
53
|
+
* A virtual CCI accepts nothing but immediate transfers, so a payment above this
|
|
54
|
+
* cannot be sent to one at all: the payer's bank has to use deferred CCE or LBTR,
|
|
55
|
+
* and both are refused by a CCIV. Those payments go to the principal account
|
|
56
|
+
* instead, which is why the ceiling decides which CCI a customer is shown.
|
|
57
|
+
*/
|
|
58
|
+
exports.CCE_IMMEDIATE_CEILING_BY_FIAT = {
|
|
59
|
+
[common_1.Fiat.PEN]: 30000,
|
|
60
|
+
[common_1.Fiat.USD]: 10000,
|
|
61
|
+
};
|
|
62
|
+
const INTERBANK_PERU_CCI_BY_FIAT = {
|
|
63
|
+
[common_1.Fiat.PEN]: "003-200-003004741070-36",
|
|
64
|
+
[common_1.Fiat.USD]: "003-200-003004741088-38",
|
|
65
|
+
};
|
|
66
|
+
// Digits-only form of a Peruvian CCI. Banks present them either grouped with
|
|
67
|
+
// dashes or bare, and Alfin reports the bare form inside a statement line's
|
|
68
|
+
// referencia, so both have to reduce to the same string to be comparable.
|
|
69
|
+
const normalizeCci = (cci) => cci.replace(/\D/g, "");
|
|
70
|
+
exports.normalizeCci = normalizeCci;
|
|
71
|
+
const RIO_PERU_OWN_CCIS = Object.values(INTERBANK_PERU_CCI_BY_FIAT)
|
|
72
|
+
.filter((cci) => Boolean(cci))
|
|
73
|
+
.map(exports.normalizeCci);
|
|
74
|
+
/**
|
|
75
|
+
* Whether a CCI is one of Rio's own Peru accounts.
|
|
76
|
+
*
|
|
77
|
+
* Rio funds Alfin from its Interbank accounts, so those transfers appear on the
|
|
78
|
+
* Alfin statement as ordinary interbank credits carrying Rio's own account as
|
|
79
|
+
* the payer. Anything reading the statement to attribute customer payments has
|
|
80
|
+
* to exclude them, or a treasury top-up would be credited to whichever customer
|
|
81
|
+
* order happened to be awaiting the same amount.
|
|
82
|
+
*/
|
|
83
|
+
const isRioOwnPeruAccountCci = (cci) => {
|
|
84
|
+
if (!cci) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return RIO_PERU_OWN_CCIS.includes((0, exports.normalizeCci)(cci));
|
|
88
|
+
};
|
|
89
|
+
exports.isRioOwnPeruAccountCci = isRioOwnPeruAccountCci;
|
|
43
90
|
// Shared house accounts. Kept here rather than in the order-payment path so the
|
|
44
91
|
// customer-facing whitelisting list and the payment instructions on an order can
|
|
45
92
|
// never drift apart.
|
|
@@ -78,7 +125,7 @@ const getStaticPayinBankDetails = (processor, fiat) => {
|
|
|
78
125
|
companyName: PERU_COMPANY_NAME,
|
|
79
126
|
companyAddress: PERU_COMPANY_ADDRESS,
|
|
80
127
|
accountNumber: "200-3004741070",
|
|
81
|
-
CCI:
|
|
128
|
+
CCI: INTERBANK_PERU_CCI_BY_FIAT[common_1.Fiat.PEN],
|
|
82
129
|
RUC: PERU_RUC,
|
|
83
130
|
};
|
|
84
131
|
}
|
|
@@ -88,7 +135,7 @@ const getStaticPayinBankDetails = (processor, fiat) => {
|
|
|
88
135
|
companyName: PERU_COMPANY_NAME,
|
|
89
136
|
companyAddress: PERU_COMPANY_ADDRESS,
|
|
90
137
|
accountNumber: "200-3004741088",
|
|
91
|
-
CCI:
|
|
138
|
+
CCI: INTERBANK_PERU_CCI_BY_FIAT[common_1.Fiat.USD],
|
|
92
139
|
RUC: PERU_RUC,
|
|
93
140
|
};
|
|
94
141
|
}
|
|
@@ -116,8 +163,10 @@ const getStaticPayinBankDetails = (processor, fiat) => {
|
|
|
116
163
|
// Looks up (and by default creates) the user's own destination on a per-user
|
|
117
164
|
// rail. With `provision` false it reports what exists without calling out to the
|
|
118
165
|
// provider, which is what the whitelisting list needs so that merely viewing the
|
|
119
|
-
// page does not allocate account numbers at Fintoc or Alfin.
|
|
120
|
-
|
|
166
|
+
// page does not allocate account numbers at Fintoc or Alfin. `amount` is the sum
|
|
167
|
+
// the customer will send in one transfer, which on Alfin decides whether their
|
|
168
|
+
// virtual CCI can receive it at all.
|
|
169
|
+
const getPerUserPayinBankDetails = ({ mongoose, processor, fiat, userId, provision, amount, requireSharedAlfinAccount, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
121
170
|
if (processor === common_1.Processor.SPEI_STP) {
|
|
122
171
|
const STPDepositCLABE = (0, STP_deposit_CLABE_1.buildSTPDepositCLABE)(mongoose);
|
|
123
172
|
const existing = yield STPDepositCLABE.findOne({ userId });
|
|
@@ -163,22 +212,34 @@ const getPerUserPayinBankDetails = ({ mongoose, processor, fiat, userId, provisi
|
|
|
163
212
|
};
|
|
164
213
|
}
|
|
165
214
|
if (processor === common_1.Processor.Alfin) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
215
|
+
// Above the immediate-transfer ceiling the customer is given the principal
|
|
216
|
+
// account's CCI rather than their own virtual one. Showing the CCIV there
|
|
217
|
+
// would be showing them the one destination their bank cannot pay: the
|
|
218
|
+
// transfer has to go out on a rail a CCIV refuses, so it would never arrive.
|
|
219
|
+
const ceiling = exports.CCE_IMMEDIATE_CEILING_BY_FIAT[fiat];
|
|
220
|
+
const exceedsCcivCeiling = amount !== undefined && ceiling !== undefined && amount > ceiling;
|
|
221
|
+
const usePrincipalAccount = Boolean(requireSharedAlfinAccount) || exceedsCcivCeiling;
|
|
222
|
+
let CCI = usePrincipalAccount
|
|
223
|
+
? ALFIN_PRINCIPAL_CCI_BY_FIAT[fiat]
|
|
224
|
+
: undefined;
|
|
225
|
+
if (!usePrincipalAccount) {
|
|
226
|
+
const AlfinVirtualCci = (0, alfin_virtual_cci_1.buildAlfinVirtualCci)(mongoose);
|
|
227
|
+
const existing = yield AlfinVirtualCci.findOne({ userId, fiat });
|
|
228
|
+
CCI = existing === null || existing === void 0 ? void 0 : existing.CCIV;
|
|
229
|
+
if (!CCI) {
|
|
230
|
+
if (!provision) {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
const clusterClient = yield (0, cluster_client_1.buildClusterClient)();
|
|
234
|
+
CCI = (yield clusterClient.generateAlfinVirtualCci(userId, fiat)).CCIV;
|
|
172
235
|
}
|
|
173
|
-
const clusterClient = yield (0, cluster_client_1.buildClusterClient)();
|
|
174
|
-
CCIV = (yield clusterClient.generateAlfinVirtualCci(userId, fiat)).CCIV;
|
|
175
236
|
}
|
|
176
237
|
return {
|
|
177
238
|
bankName: "Alfin Banco",
|
|
178
239
|
companyName: PERU_COMPANY_NAME,
|
|
179
240
|
companyAddress: PERU_COMPANY_ADDRESS,
|
|
180
241
|
accountNumber: ALFIN_ACCOUNT_NUMBER_BY_FIAT[fiat],
|
|
181
|
-
CCI
|
|
242
|
+
CCI,
|
|
182
243
|
RUC: PERU_RUC,
|
|
183
244
|
};
|
|
184
245
|
}
|
|
@@ -187,7 +248,7 @@ const getPerUserPayinBankDetails = ({ mongoose, processor, fiat, userId, provisi
|
|
|
187
248
|
// The account a customer must pay into to fund a buy order on a given rail.
|
|
188
249
|
// A custom Rio bank account replaces the rail's own destination outright, which
|
|
189
250
|
// is why routing has to know that account's processor - see resolveProcessor.
|
|
190
|
-
const getPayinBankDetails = ({ mongoose, processor, fiat, country, user, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
251
|
+
const getPayinBankDetails = ({ mongoose, processor, fiat, country, user, amount, requireSharedAlfinAccount, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
191
252
|
var _a, _b, _c;
|
|
192
253
|
const rioBankAccountId = (_c = (_b = (_a = user.rioBankAccount) === null || _a === void 0 ? void 0 : _a[country]) === null || _b === void 0 ? void 0 : _b[fiat]) === null || _c === void 0 ? void 0 : _c.id;
|
|
193
254
|
if (rioBankAccountId) {
|
|
@@ -215,6 +276,8 @@ const getPayinBankDetails = ({ mongoose, processor, fiat, country, user, }) => _
|
|
|
215
276
|
fiat,
|
|
216
277
|
userId: user.id,
|
|
217
278
|
provision: true,
|
|
279
|
+
amount,
|
|
280
|
+
requireSharedAlfinAccount,
|
|
218
281
|
});
|
|
219
282
|
return details || {};
|
|
220
283
|
}
|
|
@@ -252,6 +315,12 @@ const getPayinDestinations = ({ mongoose, user, country, fiat, processors, provi
|
|
|
252
315
|
userId: user.id,
|
|
253
316
|
provision,
|
|
254
317
|
});
|
|
318
|
+
// A customer who may only send to accounts their bank has on file has to
|
|
319
|
+
// have both Alfin CCIs on it before they place an order, since which one
|
|
320
|
+
// they are given depends on how much that order is for.
|
|
321
|
+
if (processor === common_1.Processor.Alfin && bankDetails) {
|
|
322
|
+
bankDetails.largeTransferCCI = ALFIN_PRINCIPAL_CCI_BY_FIAT[fiat];
|
|
323
|
+
}
|
|
255
324
|
}
|
|
256
325
|
else {
|
|
257
326
|
bankDetails = getStaticPayinBankDetails(processor, fiat);
|
package/build/index.d.ts
CHANGED
|
@@ -95,6 +95,8 @@ export * from "./models/alfin-virtual-cci";
|
|
|
95
95
|
export * from "./models/alfin-settings";
|
|
96
96
|
export * from "./models/alfin-withdrawal";
|
|
97
97
|
export * from "./models/alfin-payin-notification";
|
|
98
|
+
export * from "./models/alfin-payin-origin";
|
|
99
|
+
export * from "./models/alfin-statement-credit";
|
|
98
100
|
export * from "./models/alfin-webhook-token";
|
|
99
101
|
export * from "./models/dashboard-push-notification-subscription";
|
|
100
102
|
export * from "./models/webauthn-credential";
|
package/build/index.js
CHANGED
|
@@ -111,6 +111,8 @@ __exportStar(require("./models/alfin-virtual-cci"), exports);
|
|
|
111
111
|
__exportStar(require("./models/alfin-settings"), exports);
|
|
112
112
|
__exportStar(require("./models/alfin-withdrawal"), exports);
|
|
113
113
|
__exportStar(require("./models/alfin-payin-notification"), exports);
|
|
114
|
+
__exportStar(require("./models/alfin-payin-origin"), exports);
|
|
115
|
+
__exportStar(require("./models/alfin-statement-credit"), exports);
|
|
114
116
|
__exportStar(require("./models/alfin-webhook-token"), exports);
|
|
115
117
|
__exportStar(require("./models/dashboard-push-notification-subscription"), exports);
|
|
116
118
|
__exportStar(require("./models/webauthn-credential"), exports);
|
|
@@ -19,6 +19,8 @@ interface AlfinPayinNotificationDoc extends Document {
|
|
|
19
19
|
amount: number;
|
|
20
20
|
processedAt: Date;
|
|
21
21
|
payload: Record<string, any>;
|
|
22
|
+
processingCompletedAt?: Date | null;
|
|
23
|
+
reconciledStatementCreditKey?: string;
|
|
22
24
|
}
|
|
23
25
|
interface AlfinPayinNotificationModel extends Model<AlfinPayinNotificationDoc> {
|
|
24
26
|
build(attrs: AlfinPayinNotificationAttrs): HydratedDocument<AlfinPayinNotificationDoc>;
|
|
@@ -37,6 +37,17 @@ const buildAlfinPayinNotification = (mongoose) => {
|
|
|
37
37
|
payload: {
|
|
38
38
|
type: mongoose.Schema.Types.Mixed,
|
|
39
39
|
},
|
|
40
|
+
// Defaulted so every new notification carries the field explicitly, which
|
|
41
|
+
// is what separates one that has not finished from one written before this
|
|
42
|
+
// existed. A query that cannot tell those apart either credits a payin
|
|
43
|
+
// twice or not at all.
|
|
44
|
+
processingCompletedAt: {
|
|
45
|
+
type: mongoose.Schema.Types.Date,
|
|
46
|
+
default: null,
|
|
47
|
+
},
|
|
48
|
+
reconciledStatementCreditKey: {
|
|
49
|
+
type: String,
|
|
50
|
+
},
|
|
40
51
|
}, {
|
|
41
52
|
toJSON: {
|
|
42
53
|
transform(doc, ret) {
|
|
@@ -46,6 +57,12 @@ const buildAlfinPayinNotification = (mongoose) => {
|
|
|
46
57
|
},
|
|
47
58
|
},
|
|
48
59
|
});
|
|
60
|
+
// Supports the claim lookup, which searches unclaimed notifications by
|
|
61
|
+
// currency and amount within a few days of a statement line.
|
|
62
|
+
AlfinPayinNotificationSchema.index({ fiat: 1, amount: 1, processedAt: 1 });
|
|
63
|
+
// One statement line can only ever be the same money as one notification.
|
|
64
|
+
// Sparse so the unclaimed majority are not indexed as duplicate nulls.
|
|
65
|
+
AlfinPayinNotificationSchema.index({ reconciledStatementCreditKey: 1 }, { unique: true, sparse: true });
|
|
49
66
|
AlfinPayinNotificationSchema.statics.build = (attrs) => {
|
|
50
67
|
return new AlfinPayinNotification(attrs);
|
|
51
68
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Mongoose, Model, Document, HydratedDocument } from "mongoose";
|
|
2
|
+
type AlfinPayinOriginSource = "payinWebhook" | "payinVerification";
|
|
3
|
+
interface AlfinPayinOriginAttrs {
|
|
4
|
+
CCI: string;
|
|
5
|
+
userId: string;
|
|
6
|
+
source: AlfinPayinOriginSource;
|
|
7
|
+
accountHolderName?: string;
|
|
8
|
+
firstSeenAt: Date;
|
|
9
|
+
lastSeenAt: Date;
|
|
10
|
+
}
|
|
11
|
+
interface AlfinPayinOriginDoc extends Document {
|
|
12
|
+
CCI: string;
|
|
13
|
+
userId: string;
|
|
14
|
+
source: AlfinPayinOriginSource;
|
|
15
|
+
accountHolderName?: string;
|
|
16
|
+
firstSeenAt: Date;
|
|
17
|
+
lastSeenAt: Date;
|
|
18
|
+
}
|
|
19
|
+
interface AlfinPayinOriginModel extends Model<AlfinPayinOriginDoc> {
|
|
20
|
+
build(attrs: AlfinPayinOriginAttrs): HydratedDocument<AlfinPayinOriginDoc>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Accounts customers have paid us from at Alfin, learned as they pay.
|
|
24
|
+
*
|
|
25
|
+
* A payment that lands on a user's virtual CCI identifies its owner outright, so
|
|
26
|
+
* nothing here is needed to process it. It exists for payments that cannot use a
|
|
27
|
+
* virtual CCI at all - anything over the CCE immediate-transfer ceiling, which
|
|
28
|
+
* the CCIV rejects - because those land on the principal account, raise no
|
|
29
|
+
* notification, and are only visible as an account-statement credit. The
|
|
30
|
+
* statement carries the payer's CCI, so a customer's first small payment through
|
|
31
|
+
* their CCIV is what teaches us where their large ones will come from.
|
|
32
|
+
*
|
|
33
|
+
* One document per account/user pair rather than one per account: a shared
|
|
34
|
+
* corporate account can legitimately belong to more than one user, and a
|
|
35
|
+
* statement credit from an account claimed by two users cannot be attributed to
|
|
36
|
+
* either, which the matcher detects by finding more than one document.
|
|
37
|
+
*/
|
|
38
|
+
declare const buildAlfinPayinOrigin: (mongoose: Mongoose) => AlfinPayinOriginModel;
|
|
39
|
+
export { buildAlfinPayinOrigin, AlfinPayinOriginDoc, AlfinPayinOriginAttrs, AlfinPayinOriginSource, };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAlfinPayinOrigin = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Accounts customers have paid us from at Alfin, learned as they pay.
|
|
6
|
+
*
|
|
7
|
+
* A payment that lands on a user's virtual CCI identifies its owner outright, so
|
|
8
|
+
* nothing here is needed to process it. It exists for payments that cannot use a
|
|
9
|
+
* virtual CCI at all - anything over the CCE immediate-transfer ceiling, which
|
|
10
|
+
* the CCIV rejects - because those land on the principal account, raise no
|
|
11
|
+
* notification, and are only visible as an account-statement credit. The
|
|
12
|
+
* statement carries the payer's CCI, so a customer's first small payment through
|
|
13
|
+
* their CCIV is what teaches us where their large ones will come from.
|
|
14
|
+
*
|
|
15
|
+
* One document per account/user pair rather than one per account: a shared
|
|
16
|
+
* corporate account can legitimately belong to more than one user, and a
|
|
17
|
+
* statement credit from an account claimed by two users cannot be attributed to
|
|
18
|
+
* either, which the matcher detects by finding more than one document.
|
|
19
|
+
*/
|
|
20
|
+
const buildAlfinPayinOrigin = (mongoose) => {
|
|
21
|
+
if (mongoose.models.AlfinPayinOrigin) {
|
|
22
|
+
return mongoose.model("AlfinPayinOrigin");
|
|
23
|
+
}
|
|
24
|
+
const AlfinPayinOriginSchema = new mongoose.Schema({
|
|
25
|
+
CCI: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
userId: {
|
|
30
|
+
type: String,
|
|
31
|
+
required: true,
|
|
32
|
+
index: true,
|
|
33
|
+
},
|
|
34
|
+
source: {
|
|
35
|
+
type: String,
|
|
36
|
+
required: true,
|
|
37
|
+
},
|
|
38
|
+
accountHolderName: {
|
|
39
|
+
type: String,
|
|
40
|
+
},
|
|
41
|
+
firstSeenAt: {
|
|
42
|
+
type: mongoose.Schema.Types.Date,
|
|
43
|
+
required: true,
|
|
44
|
+
},
|
|
45
|
+
lastSeenAt: {
|
|
46
|
+
type: mongoose.Schema.Types.Date,
|
|
47
|
+
required: true,
|
|
48
|
+
},
|
|
49
|
+
}, {
|
|
50
|
+
toJSON: {
|
|
51
|
+
transform(doc, ret) {
|
|
52
|
+
ret.id = ret._id.valueOf();
|
|
53
|
+
delete ret._id;
|
|
54
|
+
delete ret.__v;
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
AlfinPayinOriginSchema.index({ CCI: 1, userId: 1 }, { unique: true });
|
|
59
|
+
AlfinPayinOriginSchema.statics.build = (attrs) => {
|
|
60
|
+
return new AlfinPayinOrigin(attrs);
|
|
61
|
+
};
|
|
62
|
+
const AlfinPayinOrigin = mongoose.model("AlfinPayinOrigin", AlfinPayinOriginSchema);
|
|
63
|
+
return AlfinPayinOrigin;
|
|
64
|
+
};
|
|
65
|
+
exports.buildAlfinPayinOrigin = buildAlfinPayinOrigin;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Fiat } from "@riocrypto/common";
|
|
2
|
+
import { Mongoose, Model, Document, HydratedDocument } from "mongoose";
|
|
3
|
+
type AlfinStatementCreditStatus = "matched" | "attributedToWebhook" | "ignored" | "awaitingApproval" | "unmatched";
|
|
4
|
+
interface AlfinStatementCreditAttrs {
|
|
5
|
+
uniqueCompositeKey: string;
|
|
6
|
+
movimientoUId: string;
|
|
7
|
+
ordinal?: number;
|
|
8
|
+
fiat: Fiat;
|
|
9
|
+
amount: number;
|
|
10
|
+
postedAt: Date;
|
|
11
|
+
concepto?: string;
|
|
12
|
+
referencia?: string;
|
|
13
|
+
originCCI?: string;
|
|
14
|
+
originName?: string;
|
|
15
|
+
status: AlfinStatementCreditStatus;
|
|
16
|
+
reason?: string;
|
|
17
|
+
userId?: string;
|
|
18
|
+
orderId?: string;
|
|
19
|
+
bulkBankPaymentId?: string;
|
|
20
|
+
evaluatedAt: Date;
|
|
21
|
+
}
|
|
22
|
+
interface AlfinStatementCreditDoc extends Document {
|
|
23
|
+
uniqueCompositeKey: string;
|
|
24
|
+
movimientoUId: string;
|
|
25
|
+
ordinal?: number;
|
|
26
|
+
fiat: Fiat;
|
|
27
|
+
amount: number;
|
|
28
|
+
postedAt: Date;
|
|
29
|
+
concepto?: string;
|
|
30
|
+
referencia?: string;
|
|
31
|
+
originCCI?: string;
|
|
32
|
+
originName?: string;
|
|
33
|
+
status: AlfinStatementCreditStatus;
|
|
34
|
+
reason?: string;
|
|
35
|
+
userId?: string;
|
|
36
|
+
orderId?: string;
|
|
37
|
+
bulkBankPaymentId?: string;
|
|
38
|
+
evaluatedAt: Date;
|
|
39
|
+
}
|
|
40
|
+
interface AlfinStatementCreditModel extends Model<AlfinStatementCreditDoc> {
|
|
41
|
+
build(attrs: AlfinStatementCreditAttrs): HydratedDocument<AlfinStatementCreditDoc>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* One record per account-statement credit we have evaluated for payment
|
|
45
|
+
* matching, and the decision we reached.
|
|
46
|
+
*
|
|
47
|
+
* The statement is re-read on every poll over a 30-day window, so this is what
|
|
48
|
+
* stops a credit from being attributed twice: the key is inserted before any
|
|
49
|
+
* matching happens, and a duplicate-key error means another run already owns
|
|
50
|
+
* this line. It doubles as the audit trail for why a credit was or was not
|
|
51
|
+
* attributed, which is the only record of that reasoning - the credit itself
|
|
52
|
+
* arrives with no notification behind it.
|
|
53
|
+
*/
|
|
54
|
+
declare const buildAlfinStatementCredit: (mongoose: Mongoose) => AlfinStatementCreditModel;
|
|
55
|
+
export { buildAlfinStatementCredit, AlfinStatementCreditDoc, AlfinStatementCreditAttrs, AlfinStatementCreditStatus, };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildAlfinStatementCredit = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* One record per account-statement credit we have evaluated for payment
|
|
6
|
+
* matching, and the decision we reached.
|
|
7
|
+
*
|
|
8
|
+
* The statement is re-read on every poll over a 30-day window, so this is what
|
|
9
|
+
* stops a credit from being attributed twice: the key is inserted before any
|
|
10
|
+
* matching happens, and a duplicate-key error means another run already owns
|
|
11
|
+
* this line. It doubles as the audit trail for why a credit was or was not
|
|
12
|
+
* attributed, which is the only record of that reasoning - the credit itself
|
|
13
|
+
* arrives with no notification behind it.
|
|
14
|
+
*/
|
|
15
|
+
const buildAlfinStatementCredit = (mongoose) => {
|
|
16
|
+
if (mongoose.models.AlfinStatementCredit) {
|
|
17
|
+
return mongoose.model("AlfinStatementCredit");
|
|
18
|
+
}
|
|
19
|
+
const AlfinStatementCreditSchema = new mongoose.Schema({
|
|
20
|
+
uniqueCompositeKey: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: true,
|
|
23
|
+
unique: true,
|
|
24
|
+
},
|
|
25
|
+
movimientoUId: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: true,
|
|
28
|
+
},
|
|
29
|
+
ordinal: {
|
|
30
|
+
type: Number,
|
|
31
|
+
},
|
|
32
|
+
fiat: {
|
|
33
|
+
type: String,
|
|
34
|
+
required: true,
|
|
35
|
+
},
|
|
36
|
+
amount: {
|
|
37
|
+
type: Number,
|
|
38
|
+
required: true,
|
|
39
|
+
},
|
|
40
|
+
postedAt: {
|
|
41
|
+
type: mongoose.Schema.Types.Date,
|
|
42
|
+
required: true,
|
|
43
|
+
},
|
|
44
|
+
concepto: {
|
|
45
|
+
type: String,
|
|
46
|
+
},
|
|
47
|
+
referencia: {
|
|
48
|
+
type: String,
|
|
49
|
+
},
|
|
50
|
+
originCCI: {
|
|
51
|
+
type: String,
|
|
52
|
+
},
|
|
53
|
+
originName: {
|
|
54
|
+
type: String,
|
|
55
|
+
},
|
|
56
|
+
status: {
|
|
57
|
+
type: String,
|
|
58
|
+
required: true,
|
|
59
|
+
},
|
|
60
|
+
reason: {
|
|
61
|
+
type: String,
|
|
62
|
+
},
|
|
63
|
+
userId: {
|
|
64
|
+
type: String,
|
|
65
|
+
},
|
|
66
|
+
orderId: {
|
|
67
|
+
type: String,
|
|
68
|
+
},
|
|
69
|
+
bulkBankPaymentId: {
|
|
70
|
+
type: String,
|
|
71
|
+
},
|
|
72
|
+
evaluatedAt: {
|
|
73
|
+
type: mongoose.Schema.Types.Date,
|
|
74
|
+
required: true,
|
|
75
|
+
},
|
|
76
|
+
}, {
|
|
77
|
+
toJSON: {
|
|
78
|
+
transform(doc, ret) {
|
|
79
|
+
ret.id = ret._id.valueOf();
|
|
80
|
+
delete ret._id;
|
|
81
|
+
delete ret.__v;
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
AlfinStatementCreditSchema.statics.build = (attrs) => {
|
|
86
|
+
return new AlfinStatementCredit(attrs);
|
|
87
|
+
};
|
|
88
|
+
const AlfinStatementCredit = mongoose.model("AlfinStatementCredit", AlfinStatementCreditSchema);
|
|
89
|
+
return AlfinStatementCredit;
|
|
90
|
+
};
|
|
91
|
+
exports.buildAlfinStatementCredit = buildAlfinStatementCredit;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riocrypto/common-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2882",
|
|
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.2702",
|
|
32
32
|
"@slack/web-api": "^7.15.0",
|
|
33
33
|
"@types/express": "^4.17.25",
|
|
34
34
|
"axios": "1.18.1",
|