ojamoto_models 1.0.9 → 1.0.11
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/migrations/2026000047-add-cac-verified-to-user-kyc.js +39 -0
- package/migrations/2026000048-update-platform-fee-tnx-pin-limit.js +87 -0
- package/models/Escrow.js +18 -0
- package/models/KycVerification.js +9 -0
- package/models/SellerProfile.js +6 -0
- package/models/User.js +21 -1
- package/models/WalletTransaction.js +10 -0
- package/package.json +1 -1
- package/services/constants.js +23 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
async up(queryInterface, Sequelize) {
|
|
5
|
+
await queryInterface.addColumn("users", "cac_verified", {
|
|
6
|
+
type: Sequelize.BOOLEAN,
|
|
7
|
+
allowNull: false,
|
|
8
|
+
defaultValue: false,
|
|
9
|
+
after: "nin_verified",
|
|
10
|
+
comment: "Business sellers only",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
await queryInterface.addColumn("kyc_verifications", "cac_data_json", {
|
|
14
|
+
type: Sequelize.JSON,
|
|
15
|
+
allowNull: true,
|
|
16
|
+
after: "bank_data_json",
|
|
17
|
+
comment: "Raw CAC API response from provider",
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
await queryInterface.addColumn(
|
|
21
|
+
"kyc_verifications",
|
|
22
|
+
"verification_attempts",
|
|
23
|
+
{
|
|
24
|
+
type: Sequelize.INTEGER,
|
|
25
|
+
allowNull: false,
|
|
26
|
+
defaultValue: 0,
|
|
27
|
+
after: "cac_data_json",
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
async down(queryInterface) {
|
|
32
|
+
await queryInterface.removeColumn("users", "cac_verified");
|
|
33
|
+
await queryInterface.removeColumn("kyc_verifications", "cac_data_json");
|
|
34
|
+
await queryInterface.removeColumn(
|
|
35
|
+
"kyc_verifications",
|
|
36
|
+
"verification_attempts",
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { SELLER_TIER } = require("../services/constants");
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
async up(queryInterface, Sequelize) {
|
|
7
|
+
// seller tiers
|
|
8
|
+
await queryInterface.addColumn("seller_profiles", "tier", {
|
|
9
|
+
type: Sequelize.ENUM(...Object.values(SELLER_TIER)),
|
|
10
|
+
allowNull: true,
|
|
11
|
+
defaultValue: SELLER_TIER.TIER_1,
|
|
12
|
+
after: "verification_status",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// escrow units + fee snapshot
|
|
16
|
+
await queryInterface.addColumn("escrows", "units", {
|
|
17
|
+
type: Sequelize.INTEGER,
|
|
18
|
+
allowNull: true,
|
|
19
|
+
defaultValue: 1,
|
|
20
|
+
after: "currency",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
await queryInterface.addColumn("escrows", "unit_price", {
|
|
24
|
+
type: Sequelize.DECIMAL(15, 2),
|
|
25
|
+
allowNull: true,
|
|
26
|
+
defaultValue: 0.0,
|
|
27
|
+
after: "units",
|
|
28
|
+
comment: "Price per unit at time of order",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await queryInterface.addColumn("escrows", "fee_percentage_applied", {
|
|
32
|
+
type: Sequelize.DECIMAL(5, 4),
|
|
33
|
+
allowNull: true,
|
|
34
|
+
defaultValue: 0.05,
|
|
35
|
+
after: "unit_price",
|
|
36
|
+
comment: "Seller's tier fee rate frozen at order time",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// funding fee — what the user was actually charged vs what we credit
|
|
40
|
+
await queryInterface.addColumn("wallet_transactions", "charged_amount", {
|
|
41
|
+
type: Sequelize.DECIMAL(15, 2),
|
|
42
|
+
allowNull: true,
|
|
43
|
+
after: "currency",
|
|
44
|
+
comment:
|
|
45
|
+
"Gross amount charged at the gateway (amount + funding fee) - deposits only",
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await queryInterface.addColumn("wallet_transactions", "funding_fee", {
|
|
49
|
+
type: Sequelize.DECIMAL(15, 2),
|
|
50
|
+
allowNull: true,
|
|
51
|
+
after: "charged_amount",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// PIN lockout
|
|
55
|
+
await queryInterface.addColumn("users", "pin_failed_attempts", {
|
|
56
|
+
type: Sequelize.INTEGER,
|
|
57
|
+
allowNull: true,
|
|
58
|
+
defaultValue: 0,
|
|
59
|
+
after: "transaction_pin_hash",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await queryInterface.addColumn("users", "pin_locked_until", {
|
|
63
|
+
type: Sequelize.DATE,
|
|
64
|
+
allowNull: true,
|
|
65
|
+
after: "pin_failed_attempts",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
await queryInterface.addColumn("users", "pin_locked_permanently", {
|
|
69
|
+
type: Sequelize.BOOLEAN,
|
|
70
|
+
allowNull: true,
|
|
71
|
+
defaultValue: false,
|
|
72
|
+
after: "pin_locked_until",
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
async down(queryInterface) {
|
|
77
|
+
await queryInterface.removeColumn("seller_profiles", "tier");
|
|
78
|
+
await queryInterface.removeColumn("escrows", "units");
|
|
79
|
+
await queryInterface.removeColumn("escrows", "unit_price");
|
|
80
|
+
await queryInterface.removeColumn("escrows", "fee_percentage_applied");
|
|
81
|
+
await queryInterface.removeColumn("wallet_transactions", "charged_amount");
|
|
82
|
+
await queryInterface.removeColumn("wallet_transactions", "funding_fee");
|
|
83
|
+
await queryInterface.removeColumn("users", "pin_failed_attempts");
|
|
84
|
+
await queryInterface.removeColumn("users", "pin_locked_until");
|
|
85
|
+
await queryInterface.removeColumn("users", "pin_locked_permanently");
|
|
86
|
+
},
|
|
87
|
+
};
|
package/models/Escrow.js
CHANGED
|
@@ -84,6 +84,24 @@ module.exports = (sequelize) => {
|
|
|
84
84
|
allowNull: false,
|
|
85
85
|
defaultValue: "NGN",
|
|
86
86
|
},
|
|
87
|
+
units: {
|
|
88
|
+
type: DataTypes.INTEGER,
|
|
89
|
+
allowNull: false,
|
|
90
|
+
defaultValue: 1,
|
|
91
|
+
},
|
|
92
|
+
unit_price: {
|
|
93
|
+
type: DataTypes.DECIMAL(15, 2),
|
|
94
|
+
allowNull: false,
|
|
95
|
+
defaultValue: 0.0,
|
|
96
|
+
comment: "Price per unit at time of order. Amount = unit_price * units",
|
|
97
|
+
},
|
|
98
|
+
fee_percentage_applied: {
|
|
99
|
+
type: DataTypes.DECIMAL(15, 2),
|
|
100
|
+
allowNull: false,
|
|
101
|
+
defaultValue: 0.05,
|
|
102
|
+
comment:
|
|
103
|
+
"Fee rate determined by seller's tier. 5% - T1, 4.5% - T2, 4% - T3",
|
|
104
|
+
},
|
|
87
105
|
status: {
|
|
88
106
|
type: DataTypes.ENUM(...Object.values(ESCROW_STATUS)),
|
|
89
107
|
allowNull: false,
|
|
@@ -37,6 +37,15 @@ module.exports = (sequelize) => {
|
|
|
37
37
|
allowNull: true,
|
|
38
38
|
comment: "Resolved account name and bank details",
|
|
39
39
|
},
|
|
40
|
+
cac_data_json: {
|
|
41
|
+
type: DataTypes.JSON,
|
|
42
|
+
allowNull: true,
|
|
43
|
+
},
|
|
44
|
+
verification_attempts: {
|
|
45
|
+
type: DataTypes.INTEGER,
|
|
46
|
+
allowNull: false,
|
|
47
|
+
defaultValue: 0,
|
|
48
|
+
},
|
|
40
49
|
verification_status: {
|
|
41
50
|
type: DataTypes.ENUM(...Object.values(KYC_STATUS)),
|
|
42
51
|
allowNull: false,
|
package/models/SellerProfile.js
CHANGED
|
@@ -4,6 +4,7 @@ const { Model, DataTypes } = require("sequelize");
|
|
|
4
4
|
const {
|
|
5
5
|
SELLER_VERIFICATION_STATUS,
|
|
6
6
|
SELLER_TYPE,
|
|
7
|
+
SELLER_TIER,
|
|
7
8
|
} = require("../services/constants");
|
|
8
9
|
|
|
9
10
|
module.exports = (sequelize) => {
|
|
@@ -76,6 +77,11 @@ module.exports = (sequelize) => {
|
|
|
76
77
|
allowNull: false,
|
|
77
78
|
defaultValue: SELLER_VERIFICATION_STATUS.UNVERIFIED,
|
|
78
79
|
},
|
|
80
|
+
tier: {
|
|
81
|
+
type: DataTypes.ENUM(...Object.values(SELLER_TIER)),
|
|
82
|
+
allowNull: false,
|
|
83
|
+
defaultValue: SELLER_TIER.TIER_1,
|
|
84
|
+
},
|
|
79
85
|
},
|
|
80
86
|
{
|
|
81
87
|
sequelize,
|
package/models/User.js
CHANGED
|
@@ -160,6 +160,20 @@ module.exports = (sequelize) => {
|
|
|
160
160
|
type: DataTypes.STRING(255),
|
|
161
161
|
allowNull: true,
|
|
162
162
|
},
|
|
163
|
+
pin_failed_attempts: {
|
|
164
|
+
type: DataTypes.INTEGER,
|
|
165
|
+
allowNull: false,
|
|
166
|
+
defaultValue: 0,
|
|
167
|
+
},
|
|
168
|
+
pin_locked_until: {
|
|
169
|
+
type: DataTypes.DATE,
|
|
170
|
+
allowNull: true,
|
|
171
|
+
},
|
|
172
|
+
pin_locked_permanently: {
|
|
173
|
+
type: DataTypes.BOOLEAN,
|
|
174
|
+
allowNull: false,
|
|
175
|
+
defaultValue: false,
|
|
176
|
+
},
|
|
163
177
|
is_transaction_pin_set: {
|
|
164
178
|
type: DataTypes.BOOLEAN,
|
|
165
179
|
defaultValue: false,
|
|
@@ -184,7 +198,13 @@ module.exports = (sequelize) => {
|
|
|
184
198
|
nin_verified: {
|
|
185
199
|
type: DataTypes.BOOLEAN,
|
|
186
200
|
defaultValue: false,
|
|
187
|
-
comment: "Sellers
|
|
201
|
+
comment: "Buyers and Individual Sellers",
|
|
202
|
+
},
|
|
203
|
+
cac_verified: {
|
|
204
|
+
type: DataTypes.BOOLEAN,
|
|
205
|
+
allowNull: false,
|
|
206
|
+
defaultValue: false,
|
|
207
|
+
comment: "Business sellers only",
|
|
188
208
|
},
|
|
189
209
|
bank_verified: {
|
|
190
210
|
type: DataTypes.BOOLEAN,
|
|
@@ -58,6 +58,16 @@ module.exports = (sequelize) => {
|
|
|
58
58
|
allowNull: false,
|
|
59
59
|
defaultValue: "NGN",
|
|
60
60
|
},
|
|
61
|
+
charged_amount: {
|
|
62
|
+
type: DataTypes.DECIMAL(15, 2),
|
|
63
|
+
allowNull: true,
|
|
64
|
+
comment:
|
|
65
|
+
"Gross amount charged at the gateway (amount + funding fee) - deposits only",
|
|
66
|
+
},
|
|
67
|
+
funding_fee: {
|
|
68
|
+
type: DataTypes.DECIMAL(15, 2),
|
|
69
|
+
allowNull: true,
|
|
70
|
+
},
|
|
61
71
|
reference: {
|
|
62
72
|
type: DataTypes.STRING(100),
|
|
63
73
|
allowNull: false,
|
package/package.json
CHANGED
package/services/constants.js
CHANGED
|
@@ -32,6 +32,8 @@ module.exports = {
|
|
|
32
32
|
SELLER_BADGE: {
|
|
33
33
|
TOP_RATED: "TOP_RATED",
|
|
34
34
|
TRUSTED_SELLER: "TRUSTED_SELLER",
|
|
35
|
+
VERIFIED_SELLER: "VERIFIED_SELLER",
|
|
36
|
+
PREMIUM_SELLER: "PREMIUM_SELLER",
|
|
35
37
|
},
|
|
36
38
|
|
|
37
39
|
// - addresses.label ───────────────────────────────────────────────────────────
|
|
@@ -76,6 +78,27 @@ module.exports = {
|
|
|
76
78
|
SUSPENDED: "SUSPENDED",
|
|
77
79
|
},
|
|
78
80
|
|
|
81
|
+
// Wallet funding fee - passed on to the user at top-up time 0.8%
|
|
82
|
+
FUNDING_FEE_PERCENTAGE: 0.008,
|
|
83
|
+
|
|
84
|
+
// - seller_profiles.tier ─────────────────────────────────────────────────
|
|
85
|
+
SELLER_TIER: {
|
|
86
|
+
TIER_1: "TIER_1",
|
|
87
|
+
TIER_2: "TIER_2",
|
|
88
|
+
TIER_3: "TIER_3",
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
SELLER_TIER_CONFIG: {
|
|
92
|
+
TIER_1: { fee_percentage: 0.05, listing_limit: 10, requires_kyc: false },
|
|
93
|
+
TIER_2: { fee_percentage: 0.045, listing_limit: 100, requires_kyc: true },
|
|
94
|
+
TIER_3: { fee_percentage: 0.04, listing_limit: null, requires_kyc: true },
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
PIN_LOCK: {
|
|
98
|
+
MAX_ATTEMPTS_BEFORE_LOCK: 3,
|
|
99
|
+
LOCK_DURATION_HOURS: 24,
|
|
100
|
+
},
|
|
101
|
+
|
|
79
102
|
// - products.condition ───────────────────────────────────────────────────
|
|
80
103
|
PRODUCT_CONDITION: {
|
|
81
104
|
NEW: "NEW",
|