@riocrypto/common-server 1.0.2933 → 1.0.2935

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/index.d.ts CHANGED
@@ -90,6 +90,7 @@ export * from "./models/STP-deposit-CLABE";
90
90
  export * from "./models/STP-settings";
91
91
  export * from "./models/fintoc-mxn-withdrawal";
92
92
  export * from "./models/fintoc-deposit-CLABE";
93
+ export * from "./models/inbound-deposit-intake";
93
94
  export * from "./models/processor-readiness";
94
95
  export * from "./models/payment-address-verification";
95
96
  export * from "./models/alfin-virtual-cci";
package/build/index.js CHANGED
@@ -106,6 +106,7 @@ __exportStar(require("./models/STP-deposit-CLABE"), exports);
106
106
  __exportStar(require("./models/STP-settings"), exports);
107
107
  __exportStar(require("./models/fintoc-mxn-withdrawal"), exports);
108
108
  __exportStar(require("./models/fintoc-deposit-CLABE"), exports);
109
+ __exportStar(require("./models/inbound-deposit-intake"), exports);
109
110
  __exportStar(require("./models/processor-readiness"), exports);
110
111
  __exportStar(require("./models/payment-address-verification"), exports);
111
112
  __exportStar(require("./models/alfin-virtual-cci"), exports);
@@ -0,0 +1,43 @@
1
+ import { Mongoose, Model, Document, HydratedDocument } from "mongoose";
2
+ import { TreasuryProvider } from "@riocrypto/common";
3
+ export declare enum InboundDepositIntakeStatus {
4
+ /** Recorded, waiting for the worker. */
5
+ Pending = "pending",
6
+ /** A worker has taken it. Reset to pending if left here too long. */
7
+ Processing = "processing",
8
+ /** Matching ran to completion, whatever it concluded. See `outcome`. */
9
+ Done = "done",
10
+ /** Matching threw and retries are exhausted. Needs a person. */
11
+ Failed = "failed"
12
+ }
13
+ export type InboundDepositIntakeSource = "webhook" | "poller";
14
+ interface InboundDepositIntakeAttrs {
15
+ provider: TreasuryProvider;
16
+ /** The provider's id for the deposit, e.g. a Fintoc transfer id. */
17
+ externalId: string;
18
+ source: InboundDepositIntakeSource;
19
+ /** The provider's webhook event id, for tracing back to their dashboard. */
20
+ externalEventId?: string;
21
+ receivedAt: Date;
22
+ }
23
+ interface InboundDepositIntakeDoc extends Document {
24
+ provider: TreasuryProvider;
25
+ externalId: string;
26
+ source: InboundDepositIntakeSource;
27
+ externalEventId?: string;
28
+ status: InboundDepositIntakeStatus;
29
+ attempts: number;
30
+ /** The reason the provider's matcher returned, once it has run. */
31
+ outcome?: string;
32
+ /** Whether that run published a payment, as opposed to skipping or rejecting. */
33
+ processed?: boolean;
34
+ lastError?: string;
35
+ receivedAt: Date;
36
+ startedAt?: Date;
37
+ finishedAt?: Date;
38
+ }
39
+ interface InboundDepositIntakeModel extends Model<InboundDepositIntakeDoc> {
40
+ build(attrs: InboundDepositIntakeAttrs): HydratedDocument<InboundDepositIntakeDoc>;
41
+ }
42
+ declare const buildInboundDepositIntake: (mongoose: Mongoose) => InboundDepositIntakeModel;
43
+ export { buildInboundDepositIntake, InboundDepositIntakeDoc, InboundDepositIntakeAttrs, };
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildInboundDepositIntake = exports.InboundDepositIntakeStatus = void 0;
4
+ const common_1 = require("@riocrypto/common");
5
+ // One row per inbound deposit a provider has told us about, written the moment we
6
+ // learn the deposit exists and before any work is done on it. This is what lets a
7
+ // deposit survive the pod being killed mid-processing: the webhook only records
8
+ // that the deposit arrived, and a worker in the provider's service does the
9
+ // matching from here, so a restart resumes rather than forgets. It is a processing
10
+ // ledger, not an accounting record; the money itself is recorded in
11
+ // InboundBankDeposit once matching has run.
12
+ //
13
+ // Shared across providers. Each service works only the rows for its own provider,
14
+ // and what "processing" means for a row is that service's business.
15
+ var InboundDepositIntakeStatus;
16
+ (function (InboundDepositIntakeStatus) {
17
+ /** Recorded, waiting for the worker. */
18
+ InboundDepositIntakeStatus["Pending"] = "pending";
19
+ /** A worker has taken it. Reset to pending if left here too long. */
20
+ InboundDepositIntakeStatus["Processing"] = "processing";
21
+ /** Matching ran to completion, whatever it concluded. See `outcome`. */
22
+ InboundDepositIntakeStatus["Done"] = "done";
23
+ /** Matching threw and retries are exhausted. Needs a person. */
24
+ InboundDepositIntakeStatus["Failed"] = "failed";
25
+ })(InboundDepositIntakeStatus = exports.InboundDepositIntakeStatus || (exports.InboundDepositIntakeStatus = {}));
26
+ // Finished rows are kept a month for tracing a payment that went wrong, then
27
+ // dropped. Rows that never finish have no finishedAt and so never expire, which is
28
+ // the point: an unfinished deposit should stay visible until someone deals with it.
29
+ const FINISHED_ROW_TTL_SECONDS = 30 * 24 * 60 * 60;
30
+ const buildInboundDepositIntake = (mongoose) => {
31
+ if (mongoose.models.InboundDepositIntake) {
32
+ return mongoose.model("InboundDepositIntake");
33
+ }
34
+ const InboundDepositIntakeSchema = new mongoose.Schema({
35
+ provider: {
36
+ type: String,
37
+ required: true,
38
+ enum: Object.values(common_1.TreasuryProvider),
39
+ },
40
+ externalId: {
41
+ type: String,
42
+ required: true,
43
+ },
44
+ source: {
45
+ type: String,
46
+ required: true,
47
+ enum: ["webhook", "poller"],
48
+ },
49
+ externalEventId: {
50
+ type: String,
51
+ },
52
+ status: {
53
+ type: String,
54
+ required: true,
55
+ enum: Object.values(InboundDepositIntakeStatus),
56
+ default: InboundDepositIntakeStatus.Pending,
57
+ },
58
+ attempts: {
59
+ type: Number,
60
+ required: true,
61
+ default: 0,
62
+ },
63
+ outcome: {
64
+ type: String,
65
+ },
66
+ processed: {
67
+ type: Boolean,
68
+ },
69
+ lastError: {
70
+ type: String,
71
+ },
72
+ receivedAt: {
73
+ type: Date,
74
+ required: true,
75
+ },
76
+ startedAt: {
77
+ type: Date,
78
+ },
79
+ finishedAt: {
80
+ type: Date,
81
+ },
82
+ }, {
83
+ toJSON: {
84
+ transform(doc, ret) {
85
+ ret.id = ret._id.valueOf();
86
+ delete ret._id;
87
+ delete ret.__v;
88
+ },
89
+ },
90
+ });
91
+ // The dedup. A webhook delivered twice, or a deposit the poller sees after the
92
+ // webhook already recorded it, is an insert that fails rather than a second row.
93
+ // Scoped by provider so two providers' id schemes can never collide.
94
+ InboundDepositIntakeSchema.index({ provider: 1, externalId: 1 }, { unique: true });
95
+ // What each worker polls on: the oldest pending row for its provider first, and
96
+ // the processing rows whose startedAt says their owner died.
97
+ InboundDepositIntakeSchema.index({ provider: 1, status: 1, receivedAt: 1 });
98
+ InboundDepositIntakeSchema.index({ provider: 1, status: 1, startedAt: 1 });
99
+ InboundDepositIntakeSchema.index({ finishedAt: 1 }, { expireAfterSeconds: FINISHED_ROW_TTL_SECONDS });
100
+ InboundDepositIntakeSchema.statics.build = (attrs) => {
101
+ return new InboundDepositIntake(attrs);
102
+ };
103
+ const InboundDepositIntake = mongoose.model("InboundDepositIntake", InboundDepositIntakeSchema);
104
+ return InboundDepositIntake;
105
+ };
106
+ exports.buildInboundDepositIntake = buildInboundDepositIntake;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riocrypto/common-server",
3
- "version": "1.0.2933",
3
+ "version": "1.0.2935",
4
4
  "description": "",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",