vr-models 1.0.56 → 1.0.58
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/dist/models/contact.models.d.ts +71 -0
- package/dist/models/{phoneContact.models.js → contact.models.js} +93 -29
- package/dist/models/devicePaymentPlan.models.d.ts +3 -0
- package/dist/models/devicePaymentPlan.models.js +7 -0
- package/dist/models/index.d.ts +3 -3
- package/dist/models/index.js +2 -2
- package/dist/models/payment.models.d.ts +37 -18
- package/dist/models/payment.models.js +158 -44
- package/dist/models/paymentEventLog.models.d.ts +69 -0
- package/dist/models/paymentEventLog.models.js +204 -0
- package/dist/models/types.d.ts +2 -2
- package/dist/models/user.models.d.ts +13 -12
- package/dist/models/user.models.js +36 -26
- package/package.json +1 -1
- package/dist/models/phoneContact.models.d.ts +0 -71
- package/dist/models/transaction.models.d.ts +0 -44
- package/dist/models/transaction.models.js +0 -97
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Payment = exports.PAYMENT_STATUS = exports.PAYMENT_PROVIDER = void 0;
|
|
4
|
+
// src/models/payment.models.ts
|
|
4
5
|
const vr_migrations_1 = require("vr-migrations");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
exports.PAYMENT_PROVIDER = [
|
|
7
|
+
"MTN_MOMO",
|
|
8
|
+
"AIRTEL_MONEY",
|
|
9
|
+
"DPO",
|
|
10
|
+
"FLUTTERWAVE",
|
|
11
|
+
];
|
|
12
|
+
exports.PAYMENT_STATUS = [
|
|
13
|
+
"PENDING",
|
|
14
|
+
"SUCCESSFUL",
|
|
15
|
+
"FAILED",
|
|
16
|
+
"REFUNDED",
|
|
17
|
+
];
|
|
8
18
|
class Payment extends vr_migrations_1.Model {
|
|
9
19
|
// Static initialization method
|
|
10
20
|
static initialize(sequelize) {
|
|
@@ -14,43 +24,115 @@ class Payment extends vr_migrations_1.Model {
|
|
|
14
24
|
defaultValue: vr_migrations_1.DataTypes.UUIDV4,
|
|
15
25
|
primaryKey: true,
|
|
16
26
|
},
|
|
17
|
-
userId: {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
27
|
+
userId: {
|
|
28
|
+
type: vr_migrations_1.DataTypes.UUID,
|
|
29
|
+
allowNull: false,
|
|
30
|
+
},
|
|
31
|
+
devicePaymentPlanId: {
|
|
32
|
+
type: vr_migrations_1.DataTypes.UUID,
|
|
33
|
+
allowNull: true,
|
|
34
|
+
},
|
|
35
|
+
installmentId: {
|
|
36
|
+
type: vr_migrations_1.DataTypes.UUID,
|
|
37
|
+
allowNull: true,
|
|
38
|
+
},
|
|
39
|
+
idempotencyKeyId: {
|
|
40
|
+
type: vr_migrations_1.DataTypes.UUID,
|
|
41
|
+
allowNull: true,
|
|
42
|
+
},
|
|
21
43
|
amount: {
|
|
22
|
-
type: vr_migrations_1.DataTypes.
|
|
44
|
+
type: vr_migrations_1.DataTypes.DECIMAL(10, 2),
|
|
23
45
|
allowNull: false,
|
|
24
46
|
validate: { min: 1 },
|
|
25
47
|
},
|
|
48
|
+
currency: {
|
|
49
|
+
type: vr_migrations_1.DataTypes.STRING(3),
|
|
50
|
+
allowNull: false,
|
|
51
|
+
defaultValue: "RWF",
|
|
52
|
+
},
|
|
26
53
|
provider: {
|
|
27
|
-
type: vr_migrations_1.DataTypes.ENUM("
|
|
54
|
+
type: vr_migrations_1.DataTypes.ENUM("MTN_MOMO", "AIRTEL_MONEY", "DPO", "FLUTTERWAVE"),
|
|
28
55
|
allowNull: false,
|
|
29
56
|
},
|
|
30
|
-
providerReference: {
|
|
57
|
+
providerReference: {
|
|
58
|
+
type: vr_migrations_1.DataTypes.STRING(255),
|
|
59
|
+
allowNull: true,
|
|
60
|
+
},
|
|
31
61
|
status: {
|
|
32
|
-
type: vr_migrations_1.DataTypes.ENUM("PENDING", "SUCCESSFUL", "FAILED"),
|
|
62
|
+
type: vr_migrations_1.DataTypes.ENUM("PENDING", "SUCCESSFUL", "FAILED", "REFUNDED"),
|
|
33
63
|
allowNull: false,
|
|
34
64
|
defaultValue: "PENDING",
|
|
35
65
|
},
|
|
66
|
+
customerPhone: {
|
|
67
|
+
type: vr_migrations_1.DataTypes.STRING(20),
|
|
68
|
+
allowNull: false,
|
|
69
|
+
},
|
|
70
|
+
customerName: {
|
|
71
|
+
type: vr_migrations_1.DataTypes.STRING(100),
|
|
72
|
+
allowNull: true,
|
|
73
|
+
},
|
|
74
|
+
failureReason: {
|
|
75
|
+
type: vr_migrations_1.DataTypes.TEXT,
|
|
76
|
+
allowNull: true,
|
|
77
|
+
},
|
|
36
78
|
metadata: {
|
|
37
79
|
type: vr_migrations_1.DataTypes.JSONB,
|
|
38
80
|
allowNull: false,
|
|
39
81
|
defaultValue: {},
|
|
40
82
|
},
|
|
41
|
-
|
|
42
|
-
|
|
83
|
+
settledAt: {
|
|
84
|
+
type: vr_migrations_1.DataTypes.DATE,
|
|
85
|
+
allowNull: true,
|
|
86
|
+
},
|
|
87
|
+
createdAt: {
|
|
88
|
+
type: vr_migrations_1.DataTypes.DATE,
|
|
89
|
+
defaultValue: vr_migrations_1.DataTypes.NOW,
|
|
90
|
+
},
|
|
91
|
+
updatedAt: {
|
|
92
|
+
type: vr_migrations_1.DataTypes.DATE,
|
|
93
|
+
defaultValue: vr_migrations_1.DataTypes.NOW,
|
|
94
|
+
},
|
|
43
95
|
}, {
|
|
44
96
|
sequelize,
|
|
45
|
-
tableName: "payments",
|
|
46
97
|
modelName: "Payment",
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
98
|
+
tableName: "payments",
|
|
99
|
+
timestamps: true,
|
|
100
|
+
freezeTableName: true,
|
|
101
|
+
validate: {
|
|
102
|
+
exactlyOneTarget() {
|
|
103
|
+
const hasPlan = !!this.devicePaymentPlanId;
|
|
104
|
+
const hasInstallment = !!this.installmentId;
|
|
105
|
+
if ((hasPlan && hasInstallment) || (!hasPlan && !hasInstallment)) {
|
|
106
|
+
throw new Error("Payment must belong to exactly one: devicePaymentPlanId OR installmentId");
|
|
51
107
|
}
|
|
52
108
|
},
|
|
53
109
|
},
|
|
110
|
+
indexes: [
|
|
111
|
+
{
|
|
112
|
+
fields: ["userId"],
|
|
113
|
+
name: "payment_user_idx",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
fields: ["devicePaymentPlanId"],
|
|
117
|
+
name: "payment_plan_idx",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
fields: ["installmentId"],
|
|
121
|
+
name: "payment_installment_idx",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
fields: ["status"],
|
|
125
|
+
name: "payment_status_idx",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
fields: ["providerReference"],
|
|
129
|
+
name: "payment_provider_ref_idx",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
fields: ["createdAt"],
|
|
133
|
+
name: "payment_created_at_idx",
|
|
134
|
+
},
|
|
135
|
+
],
|
|
54
136
|
});
|
|
55
137
|
}
|
|
56
138
|
// Static association method
|
|
@@ -64,12 +146,12 @@ class Payment extends vr_migrations_1.Model {
|
|
|
64
146
|
this.belongsTo(models.DevicePaymentPlan, {
|
|
65
147
|
foreignKey: "devicePaymentPlanId",
|
|
66
148
|
as: "devicePaymentPlan",
|
|
67
|
-
onDelete: "
|
|
149
|
+
onDelete: "SET NULL",
|
|
68
150
|
onUpdate: "CASCADE",
|
|
69
151
|
});
|
|
70
|
-
this.belongsTo(models.
|
|
71
|
-
foreignKey: "
|
|
72
|
-
as: "
|
|
152
|
+
this.belongsTo(models.Installment, {
|
|
153
|
+
foreignKey: "installmentId",
|
|
154
|
+
as: "installment",
|
|
73
155
|
onDelete: "SET NULL",
|
|
74
156
|
onUpdate: "CASCADE",
|
|
75
157
|
});
|
|
@@ -79,33 +161,49 @@ class Payment extends vr_migrations_1.Model {
|
|
|
79
161
|
onDelete: "SET NULL",
|
|
80
162
|
onUpdate: "CASCADE",
|
|
81
163
|
});
|
|
164
|
+
this.hasMany(models.PaymentEventLog, {
|
|
165
|
+
foreignKey: "paymentId",
|
|
166
|
+
as: "eventLogs", // Payment.getEventLogs()
|
|
167
|
+
onDelete: "SET NULL",
|
|
168
|
+
});
|
|
82
169
|
}
|
|
83
170
|
// Custom instance methods
|
|
84
|
-
async
|
|
171
|
+
async markAsSuccessful(providerReference, metadata) {
|
|
85
172
|
this.status = "SUCCESSFUL";
|
|
86
173
|
this.providerReference = providerReference;
|
|
87
174
|
if (metadata) {
|
|
88
175
|
this.metadata = { ...this.metadata, ...metadata };
|
|
89
176
|
}
|
|
90
177
|
await this.save();
|
|
178
|
+
// If this payment is for an installment, mark it as paid
|
|
179
|
+
if (this.installmentId) {
|
|
180
|
+
const Installment = this.constructor.sequelize.models
|
|
181
|
+
.Installment;
|
|
182
|
+
const installment = await Installment.findByPk(this.installmentId);
|
|
183
|
+
if (installment && installment.status !== "PAID") {
|
|
184
|
+
await installment.markAsPaid(this.id);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
91
187
|
}
|
|
92
188
|
async markAsFailed(reason, metadata) {
|
|
93
189
|
this.status = "FAILED";
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
};
|
|
99
|
-
this.metadata = { ...this.metadata, ...failureMetadata };
|
|
190
|
+
this.failureReason = reason;
|
|
191
|
+
if (metadata) {
|
|
192
|
+
this.metadata = { ...this.metadata, ...metadata };
|
|
193
|
+
}
|
|
100
194
|
await this.save();
|
|
101
195
|
}
|
|
102
|
-
async
|
|
103
|
-
this.
|
|
104
|
-
if (
|
|
105
|
-
this.
|
|
196
|
+
async markAsRefunded(metadata) {
|
|
197
|
+
this.status = "REFUNDED";
|
|
198
|
+
if (metadata) {
|
|
199
|
+
this.metadata = { ...this.metadata, ...metadata };
|
|
106
200
|
}
|
|
107
201
|
await this.save();
|
|
108
202
|
}
|
|
203
|
+
async markAsSettled(settledAt = new Date()) {
|
|
204
|
+
this.settledAt = settledAt;
|
|
205
|
+
await this.save();
|
|
206
|
+
}
|
|
109
207
|
isSuccessful() {
|
|
110
208
|
return this.status === "SUCCESSFUL";
|
|
111
209
|
}
|
|
@@ -115,26 +213,42 @@ class Payment extends vr_migrations_1.Model {
|
|
|
115
213
|
isFailed() {
|
|
116
214
|
return this.status === "FAILED";
|
|
117
215
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
216
|
+
isRefunded() {
|
|
217
|
+
return this.status === "REFUNDED";
|
|
218
|
+
}
|
|
219
|
+
getFormattedAmount() {
|
|
220
|
+
return `${this.currency} ${this.amount.toLocaleString()}`;
|
|
221
|
+
}
|
|
222
|
+
getTargetType() {
|
|
223
|
+
return this.devicePaymentPlanId ? "payment_plan" : "installment";
|
|
127
224
|
}
|
|
225
|
+
getTargetId() {
|
|
226
|
+
return (this.devicePaymentPlanId || this.installmentId);
|
|
227
|
+
}
|
|
228
|
+
// Static methods
|
|
128
229
|
static async getUserPayments(userId, limit = 50) {
|
|
129
230
|
return await this.findAll({
|
|
130
231
|
where: { userId },
|
|
131
232
|
order: [["createdAt", "DESC"]],
|
|
132
233
|
limit,
|
|
234
|
+
include: ["devicePaymentPlan", "installment"],
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
static async getPlanPayments(planId) {
|
|
238
|
+
return await this.findAll({
|
|
239
|
+
where: { devicePaymentPlanId: planId },
|
|
240
|
+
order: [["createdAt", "ASC"]],
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
static async getInstallmentPayments(installmentId) {
|
|
244
|
+
return await this.findAll({
|
|
245
|
+
where: { installmentId },
|
|
246
|
+
order: [["createdAt", "ASC"]],
|
|
133
247
|
});
|
|
134
248
|
}
|
|
135
|
-
static async
|
|
249
|
+
static async getPendingPayments() {
|
|
136
250
|
return await this.findAll({
|
|
137
|
-
where: {
|
|
251
|
+
where: { status: "PENDING" },
|
|
138
252
|
order: [["createdAt", "ASC"]],
|
|
139
253
|
});
|
|
140
254
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Model, InferAttributes, InferCreationAttributes, CreationOptional, ModelStatic, NonAttribute } from "vr-migrations";
|
|
2
|
+
import type { Payment } from "./payment.models";
|
|
3
|
+
export type EventDirection = "OUTGOING_REQUEST" | "INCOMING_WEBHOOK";
|
|
4
|
+
export type EventStatus = "PENDING" | "SUCCESS" | "FAILED" | "RETRIED";
|
|
5
|
+
export declare const EVENT_DIRECTION: readonly ["OUTGOING_REQUEST", "INCOMING_WEBHOOK"];
|
|
6
|
+
export declare const EVENT_STATUS: readonly ["PENDING", "SUCCESS", "FAILED", "RETRIED"];
|
|
7
|
+
export interface PaymentEventLogAttributes {
|
|
8
|
+
id: string;
|
|
9
|
+
paymentId: string | null;
|
|
10
|
+
provider: string;
|
|
11
|
+
direction: EventDirection;
|
|
12
|
+
action: string;
|
|
13
|
+
externalId: string | null;
|
|
14
|
+
requestPayload: Record<string, any> | null;
|
|
15
|
+
responsePayload: Record<string, any> | null;
|
|
16
|
+
statusCode: number | null;
|
|
17
|
+
status: EventStatus;
|
|
18
|
+
error: string | null;
|
|
19
|
+
durationMs: number | null;
|
|
20
|
+
retryCount: number;
|
|
21
|
+
nextRetryAt: Date | null;
|
|
22
|
+
processedAt: Date | null;
|
|
23
|
+
createdAt: Date;
|
|
24
|
+
updatedAt: Date;
|
|
25
|
+
}
|
|
26
|
+
export interface PaymentEventLogCreationAttributes extends Omit<PaymentEventLogAttributes, "id" | "createdAt" | "updatedAt" | "status" | "error" | "retryCount" | "nextRetryAt" | "processedAt" | "durationMs"> {
|
|
27
|
+
id?: string;
|
|
28
|
+
status?: EventStatus;
|
|
29
|
+
error?: string | null;
|
|
30
|
+
retryCount?: number;
|
|
31
|
+
nextRetryAt?: Date | null;
|
|
32
|
+
processedAt?: Date | null;
|
|
33
|
+
durationMs?: number | null;
|
|
34
|
+
payment?: Payment;
|
|
35
|
+
}
|
|
36
|
+
export declare class PaymentEventLog extends Model<InferAttributes<PaymentEventLog>, InferCreationAttributes<PaymentEventLog>> implements PaymentEventLogAttributes {
|
|
37
|
+
id: CreationOptional<string>;
|
|
38
|
+
paymentId: CreationOptional<string | null>;
|
|
39
|
+
provider: string;
|
|
40
|
+
direction: EventDirection;
|
|
41
|
+
action: string;
|
|
42
|
+
externalId: CreationOptional<string | null>;
|
|
43
|
+
requestPayload: CreationOptional<Record<string, any> | null>;
|
|
44
|
+
responsePayload: CreationOptional<Record<string, any> | null>;
|
|
45
|
+
statusCode: CreationOptional<number | null>;
|
|
46
|
+
status: EventStatus;
|
|
47
|
+
error: CreationOptional<string | null>;
|
|
48
|
+
durationMs: CreationOptional<number | null>;
|
|
49
|
+
retryCount: CreationOptional<number>;
|
|
50
|
+
nextRetryAt: CreationOptional<Date | null>;
|
|
51
|
+
processedAt: CreationOptional<Date | null>;
|
|
52
|
+
payment?: NonAttribute<Payment>;
|
|
53
|
+
readonly createdAt: CreationOptional<Date>;
|
|
54
|
+
readonly updatedAt: CreationOptional<Date>;
|
|
55
|
+
static initialize(sequelize: any): void;
|
|
56
|
+
static associate(models: Record<string, ModelStatic<Model>>): void;
|
|
57
|
+
markAsSuccess(durationMs?: number): Promise<void>;
|
|
58
|
+
markAsFailed(error: string, durationMs?: number): Promise<void>;
|
|
59
|
+
scheduleRetry(retryAfterSeconds: number): Promise<void>;
|
|
60
|
+
isPending(): boolean;
|
|
61
|
+
isSuccess(): boolean;
|
|
62
|
+
isFailed(): boolean;
|
|
63
|
+
isRetryable(): boolean;
|
|
64
|
+
static getUnprocessedWebhooks(limit?: number): Promise<PaymentEventLog[]>;
|
|
65
|
+
static getPendingRetries(): Promise<PaymentEventLog[]>;
|
|
66
|
+
static getPaymentEvents(paymentId: string): Promise<PaymentEventLog[]>;
|
|
67
|
+
static getProviderEvents(provider: string, externalId: string): Promise<PaymentEventLog[]>;
|
|
68
|
+
}
|
|
69
|
+
export type PaymentEventLogModel = typeof PaymentEventLog;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PaymentEventLog = exports.EVENT_STATUS = exports.EVENT_DIRECTION = void 0;
|
|
4
|
+
// src/models/paymentEventLog.models.ts
|
|
5
|
+
const vr_migrations_1 = require("vr-migrations");
|
|
6
|
+
exports.EVENT_DIRECTION = [
|
|
7
|
+
"OUTGOING_REQUEST",
|
|
8
|
+
"INCOMING_WEBHOOK",
|
|
9
|
+
];
|
|
10
|
+
exports.EVENT_STATUS = [
|
|
11
|
+
"PENDING",
|
|
12
|
+
"SUCCESS",
|
|
13
|
+
"FAILED",
|
|
14
|
+
"RETRIED",
|
|
15
|
+
];
|
|
16
|
+
class PaymentEventLog extends vr_migrations_1.Model {
|
|
17
|
+
// Static initialization method
|
|
18
|
+
static initialize(sequelize) {
|
|
19
|
+
this.init({
|
|
20
|
+
id: {
|
|
21
|
+
type: vr_migrations_1.DataTypes.UUID,
|
|
22
|
+
defaultValue: vr_migrations_1.DataTypes.UUIDV4,
|
|
23
|
+
primaryKey: true,
|
|
24
|
+
},
|
|
25
|
+
paymentId: {
|
|
26
|
+
type: vr_migrations_1.DataTypes.UUID,
|
|
27
|
+
allowNull: true,
|
|
28
|
+
references: {
|
|
29
|
+
model: "payments",
|
|
30
|
+
key: "id",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
provider: {
|
|
34
|
+
type: vr_migrations_1.DataTypes.STRING(50),
|
|
35
|
+
allowNull: false,
|
|
36
|
+
},
|
|
37
|
+
direction: {
|
|
38
|
+
type: vr_migrations_1.DataTypes.ENUM("OUTGOING_REQUEST", "INCOMING_WEBHOOK"),
|
|
39
|
+
allowNull: false,
|
|
40
|
+
},
|
|
41
|
+
action: {
|
|
42
|
+
type: vr_migrations_1.DataTypes.STRING(50),
|
|
43
|
+
allowNull: false,
|
|
44
|
+
},
|
|
45
|
+
externalId: {
|
|
46
|
+
type: vr_migrations_1.DataTypes.STRING(255),
|
|
47
|
+
allowNull: true,
|
|
48
|
+
},
|
|
49
|
+
requestPayload: {
|
|
50
|
+
type: vr_migrations_1.DataTypes.JSONB,
|
|
51
|
+
allowNull: true,
|
|
52
|
+
},
|
|
53
|
+
responsePayload: {
|
|
54
|
+
type: vr_migrations_1.DataTypes.JSONB,
|
|
55
|
+
allowNull: true,
|
|
56
|
+
},
|
|
57
|
+
statusCode: {
|
|
58
|
+
type: vr_migrations_1.DataTypes.INTEGER,
|
|
59
|
+
allowNull: true,
|
|
60
|
+
},
|
|
61
|
+
status: {
|
|
62
|
+
type: vr_migrations_1.DataTypes.ENUM("PENDING", "SUCCESS", "FAILED", "RETRIED"),
|
|
63
|
+
allowNull: false,
|
|
64
|
+
defaultValue: "PENDING",
|
|
65
|
+
},
|
|
66
|
+
error: {
|
|
67
|
+
type: vr_migrations_1.DataTypes.TEXT,
|
|
68
|
+
allowNull: true,
|
|
69
|
+
},
|
|
70
|
+
durationMs: {
|
|
71
|
+
type: vr_migrations_1.DataTypes.INTEGER,
|
|
72
|
+
allowNull: true,
|
|
73
|
+
},
|
|
74
|
+
retryCount: {
|
|
75
|
+
type: vr_migrations_1.DataTypes.INTEGER,
|
|
76
|
+
allowNull: false,
|
|
77
|
+
defaultValue: 0,
|
|
78
|
+
},
|
|
79
|
+
nextRetryAt: {
|
|
80
|
+
type: vr_migrations_1.DataTypes.DATE,
|
|
81
|
+
allowNull: true,
|
|
82
|
+
},
|
|
83
|
+
processedAt: {
|
|
84
|
+
type: vr_migrations_1.DataTypes.DATE,
|
|
85
|
+
allowNull: true,
|
|
86
|
+
},
|
|
87
|
+
createdAt: {
|
|
88
|
+
type: vr_migrations_1.DataTypes.DATE,
|
|
89
|
+
defaultValue: vr_migrations_1.DataTypes.NOW,
|
|
90
|
+
},
|
|
91
|
+
updatedAt: {
|
|
92
|
+
type: vr_migrations_1.DataTypes.DATE,
|
|
93
|
+
defaultValue: vr_migrations_1.DataTypes.NOW,
|
|
94
|
+
},
|
|
95
|
+
}, {
|
|
96
|
+
sequelize,
|
|
97
|
+
modelName: "PaymentEventLog",
|
|
98
|
+
tableName: "payment_event_logs",
|
|
99
|
+
timestamps: true,
|
|
100
|
+
freezeTableName: true,
|
|
101
|
+
indexes: [
|
|
102
|
+
{
|
|
103
|
+
fields: ["paymentId"],
|
|
104
|
+
name: "payment_event_payment_idx",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
fields: ["provider", "externalId"],
|
|
108
|
+
name: "payment_event_provider_external_idx",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
fields: ["status", "nextRetryAt"],
|
|
112
|
+
name: "payment_event_retry_idx",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
fields: ["createdAt"],
|
|
116
|
+
name: "payment_event_created_at_idx",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
fields: ["direction", "action"],
|
|
120
|
+
name: "payment_event_direction_action_idx",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// Static association method
|
|
126
|
+
static associate(models) {
|
|
127
|
+
this.belongsTo(models.Payment, {
|
|
128
|
+
foreignKey: "paymentId",
|
|
129
|
+
as: "payment",
|
|
130
|
+
onDelete: "SET NULL",
|
|
131
|
+
onUpdate: "CASCADE",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// Custom instance methods
|
|
135
|
+
async markAsSuccess(durationMs) {
|
|
136
|
+
this.status = "SUCCESS";
|
|
137
|
+
this.processedAt = new Date();
|
|
138
|
+
if (durationMs !== undefined) {
|
|
139
|
+
this.durationMs = durationMs;
|
|
140
|
+
}
|
|
141
|
+
await this.save();
|
|
142
|
+
}
|
|
143
|
+
async markAsFailed(error, durationMs) {
|
|
144
|
+
this.status = "FAILED";
|
|
145
|
+
this.error = error;
|
|
146
|
+
this.processedAt = new Date();
|
|
147
|
+
if (durationMs !== undefined) {
|
|
148
|
+
this.durationMs = durationMs;
|
|
149
|
+
}
|
|
150
|
+
await this.save();
|
|
151
|
+
}
|
|
152
|
+
async scheduleRetry(retryAfterSeconds) {
|
|
153
|
+
this.status = "RETRIED";
|
|
154
|
+
this.retryCount += 1;
|
|
155
|
+
this.nextRetryAt = new Date(Date.now() + retryAfterSeconds * 1000);
|
|
156
|
+
await this.save();
|
|
157
|
+
}
|
|
158
|
+
isPending() {
|
|
159
|
+
return this.status === "PENDING";
|
|
160
|
+
}
|
|
161
|
+
isSuccess() {
|
|
162
|
+
return this.status === "SUCCESS";
|
|
163
|
+
}
|
|
164
|
+
isFailed() {
|
|
165
|
+
return this.status === "FAILED";
|
|
166
|
+
}
|
|
167
|
+
isRetryable() {
|
|
168
|
+
return this.status === "FAILED" && this.retryCount < 5;
|
|
169
|
+
}
|
|
170
|
+
// Static methods
|
|
171
|
+
static async getUnprocessedWebhooks(limit = 100) {
|
|
172
|
+
return await this.findAll({
|
|
173
|
+
where: {
|
|
174
|
+
direction: "INCOMING_WEBHOOK",
|
|
175
|
+
status: "PENDING",
|
|
176
|
+
processedAt: null,
|
|
177
|
+
},
|
|
178
|
+
limit,
|
|
179
|
+
order: [["createdAt", "ASC"]],
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
static async getPendingRetries() {
|
|
183
|
+
return await this.findAll({
|
|
184
|
+
where: {
|
|
185
|
+
status: "RETRIED",
|
|
186
|
+
nextRetryAt: { [vr_migrations_1.Op.lte]: new Date() },
|
|
187
|
+
},
|
|
188
|
+
order: [["nextRetryAt", "ASC"]],
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
static async getPaymentEvents(paymentId) {
|
|
192
|
+
return await this.findAll({
|
|
193
|
+
where: { paymentId },
|
|
194
|
+
order: [["createdAt", "ASC"]],
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
static async getProviderEvents(provider, externalId) {
|
|
198
|
+
return await this.findAll({
|
|
199
|
+
where: { provider, externalId },
|
|
200
|
+
order: [["createdAt", "ASC"]],
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
exports.PaymentEventLog = PaymentEventLog;
|
package/dist/models/types.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export type { UserModel } from "./user.models";
|
|
2
|
-
export type { TransactionModel } from "./transaction.models";
|
|
3
2
|
export type { SecurityClearanceModel } from "./securityClearance.models";
|
|
4
3
|
export type { ProductModel } from "./product.models";
|
|
5
4
|
export type { PricingModel } from "./pricing.models";
|
|
@@ -12,4 +11,5 @@ export type { InstallmentModel } from "./installment.models";
|
|
|
12
11
|
export type { BanModel } from "./ban.models";
|
|
13
12
|
export type { SuspensionModel } from "./suspension.models";
|
|
14
13
|
export type { AppSpecsModel } from "./appSpecs.models";
|
|
15
|
-
export type {
|
|
14
|
+
export type { ContactModel } from "./contact.models";
|
|
15
|
+
export type { PaymentEventLogModel } from "./paymentEventLog.models";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Model, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, ModelStatic } from "vr-migrations";
|
|
2
|
+
import type { Device } from "./device.models";
|
|
2
3
|
import type { Payment } from "./payment.models";
|
|
3
4
|
import type { SecurityClearance } from "./securityClearance.models";
|
|
4
|
-
import type {
|
|
5
|
+
import type { Contact } from "./contact.models";
|
|
5
6
|
import { Ban } from "./ban.models";
|
|
6
7
|
import { Suspension } from "./suspension.models";
|
|
7
8
|
import { DevicePaymentPlan } from "./devicePaymentPlan.models";
|
|
@@ -18,7 +19,6 @@ export interface UserAttributes {
|
|
|
18
19
|
firstName: string;
|
|
19
20
|
lastName: string;
|
|
20
21
|
jacketId?: string | null;
|
|
21
|
-
email?: string | null;
|
|
22
22
|
password?: string | null;
|
|
23
23
|
securityClearanceId: string;
|
|
24
24
|
plateNumber?: string | null;
|
|
@@ -26,7 +26,7 @@ export interface UserAttributes {
|
|
|
26
26
|
birthDate?: number | null;
|
|
27
27
|
birthMonth?: number | null;
|
|
28
28
|
birthYear?: number | null;
|
|
29
|
-
|
|
29
|
+
primaryContactId: string | null;
|
|
30
30
|
isActive: boolean;
|
|
31
31
|
forgotPassword: boolean;
|
|
32
32
|
isDeactivated: boolean;
|
|
@@ -37,25 +37,25 @@ export interface UserAttributes {
|
|
|
37
37
|
createdAt: Date;
|
|
38
38
|
payments?: Payment[];
|
|
39
39
|
securityClearance?: SecurityClearance;
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
primaryContact?: Contact;
|
|
41
|
+
contacts?: Contact[];
|
|
42
42
|
bans?: Ban[];
|
|
43
43
|
suspensions?: Suspension[];
|
|
44
|
+
devices?: Device[];
|
|
44
45
|
}
|
|
45
|
-
export interface UserCreationAttributes extends Omit<UserAttributes, "id" | "createdAt" | "isActive" | "forgotPassword" | "isDeactivated" | "tokenVersion" | "
|
|
46
|
+
export interface UserCreationAttributes extends Omit<UserAttributes, "id" | "createdAt" | "isActive" | "forgotPassword" | "isDeactivated" | "tokenVersion" | "primaryContactId"> {
|
|
46
47
|
id?: string;
|
|
47
48
|
isActive?: boolean;
|
|
48
49
|
forgotPassword?: boolean;
|
|
49
50
|
isDeactivated?: boolean;
|
|
50
51
|
tokenVersion?: number;
|
|
51
|
-
|
|
52
|
+
primaryContactId?: string | null;
|
|
52
53
|
}
|
|
53
54
|
export declare class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> implements UserAttributes {
|
|
54
55
|
id: CreationOptional<string>;
|
|
55
56
|
firstName: string;
|
|
56
57
|
lastName: string;
|
|
57
58
|
jacketId: CreationOptional<string | null>;
|
|
58
|
-
email: CreationOptional<string | null>;
|
|
59
59
|
password: CreationOptional<string | null>;
|
|
60
60
|
securityClearanceId: string;
|
|
61
61
|
plateNumber: CreationOptional<string | null>;
|
|
@@ -63,7 +63,7 @@ export declare class User extends Model<InferAttributes<User>, InferCreationAttr
|
|
|
63
63
|
birthDate: CreationOptional<number | null>;
|
|
64
64
|
birthMonth: CreationOptional<number | null>;
|
|
65
65
|
birthYear: CreationOptional<number | null>;
|
|
66
|
-
|
|
66
|
+
primaryContactId: CreationOptional<string | null>;
|
|
67
67
|
isActive: CreationOptional<boolean>;
|
|
68
68
|
forgotPassword: CreationOptional<boolean>;
|
|
69
69
|
isDeactivated: CreationOptional<boolean>;
|
|
@@ -75,8 +75,8 @@ export declare class User extends Model<InferAttributes<User>, InferCreationAttr
|
|
|
75
75
|
payments?: NonAttribute<Payment[]>;
|
|
76
76
|
paymentPlans?: NonAttribute<DevicePaymentPlan[]>;
|
|
77
77
|
securityClearance?: NonAttribute<SecurityClearance>;
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
primaryContact?: NonAttribute<Contact>;
|
|
79
|
+
contacts?: NonAttribute<Contact[]>;
|
|
80
80
|
bans?: NonAttribute<Ban[]>;
|
|
81
81
|
suspensions?: NonAttribute<Suspension[]>;
|
|
82
82
|
static initialize(sequelize: any): void;
|
|
@@ -85,7 +85,8 @@ export declare class User extends Model<InferAttributes<User>, InferCreationAttr
|
|
|
85
85
|
updateLastLogin(): Promise<void>;
|
|
86
86
|
deactivate(): Promise<void>;
|
|
87
87
|
activate(): Promise<void>;
|
|
88
|
-
|
|
88
|
+
getPrimaryVerifiedContact(): Promise<Contact | null>;
|
|
89
|
+
hasVerifiedContactType(type: "EMAIL" | "PHONE" | "WHATSAPP"): Promise<boolean>;
|
|
89
90
|
getBirthDate(): Date | null;
|
|
90
91
|
getAge(): number | null;
|
|
91
92
|
}
|