@drawbridge/drawbridge-utils 0.0.29 → 0.0.31
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/ai.cjs +76 -74
- package/dist/ai.d.cts +72 -29
- package/dist/ai.d.ts +72 -29
- package/dist/ai.js +42 -14
- package/dist/chunk-6S43OFA7.js +115 -0
- package/dist/transactions.cjs +50 -98
- package/dist/transactions.d.cts +92 -133
- package/dist/transactions.d.ts +92 -133
- package/dist/transactions.js +1 -3
- package/package.json +1 -1
- package/dist/chunk-H735KDYS.js +0 -162
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// transactions.js
|
|
2
|
+
var insertTransaction = async ({
|
|
3
|
+
db,
|
|
4
|
+
user,
|
|
5
|
+
userId,
|
|
6
|
+
type,
|
|
7
|
+
category,
|
|
8
|
+
source,
|
|
9
|
+
amount,
|
|
10
|
+
_id,
|
|
11
|
+
stripeInvoiceId,
|
|
12
|
+
stripeEventId,
|
|
13
|
+
session,
|
|
14
|
+
...rest
|
|
15
|
+
}) => {
|
|
16
|
+
var _a;
|
|
17
|
+
const beforeRaw = (_a = user == null ? void 0 : user.balance) == null ? void 0 : _a[type];
|
|
18
|
+
const balance = typeof beforeRaw === "number" ? { before: beforeRaw, after: beforeRaw + amount } : void 0;
|
|
19
|
+
await db.create({
|
|
20
|
+
authenticated: user,
|
|
21
|
+
collection: "transaction",
|
|
22
|
+
data: {
|
|
23
|
+
..._id && { _id },
|
|
24
|
+
userId,
|
|
25
|
+
type,
|
|
26
|
+
category,
|
|
27
|
+
source,
|
|
28
|
+
amount,
|
|
29
|
+
...balance && { balance },
|
|
30
|
+
...rest,
|
|
31
|
+
...stripeInvoiceId && { stripeInvoiceId },
|
|
32
|
+
...stripeEventId && { stripeEventId }
|
|
33
|
+
},
|
|
34
|
+
...session && { options: { session } }
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
var credit = async ({
|
|
38
|
+
db,
|
|
39
|
+
user,
|
|
40
|
+
userId,
|
|
41
|
+
amount,
|
|
42
|
+
type,
|
|
43
|
+
category,
|
|
44
|
+
source,
|
|
45
|
+
_id,
|
|
46
|
+
stripeInvoiceId,
|
|
47
|
+
stripeEventId,
|
|
48
|
+
session,
|
|
49
|
+
...rest
|
|
50
|
+
}) => {
|
|
51
|
+
if (!Number.isInteger(amount) || amount <= 0) {
|
|
52
|
+
throw new Error(`credit() requires a positive integer amount, got ${amount}`);
|
|
53
|
+
}
|
|
54
|
+
if (!type) {
|
|
55
|
+
throw new Error('credit() requires a type (e.g., "ai")');
|
|
56
|
+
}
|
|
57
|
+
if (!source) {
|
|
58
|
+
throw new Error('credit() requires a source ("system" | "admin" | "user")');
|
|
59
|
+
}
|
|
60
|
+
await insertTransaction({
|
|
61
|
+
db,
|
|
62
|
+
user,
|
|
63
|
+
userId,
|
|
64
|
+
type,
|
|
65
|
+
category,
|
|
66
|
+
source,
|
|
67
|
+
amount,
|
|
68
|
+
_id,
|
|
69
|
+
stripeInvoiceId,
|
|
70
|
+
stripeEventId,
|
|
71
|
+
session,
|
|
72
|
+
...rest
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
var debit = async ({
|
|
76
|
+
db,
|
|
77
|
+
user,
|
|
78
|
+
userId,
|
|
79
|
+
amount,
|
|
80
|
+
type,
|
|
81
|
+
category,
|
|
82
|
+
source,
|
|
83
|
+
stripeInvoiceId,
|
|
84
|
+
stripeEventId,
|
|
85
|
+
session,
|
|
86
|
+
...rest
|
|
87
|
+
}) => {
|
|
88
|
+
if (!Number.isInteger(amount) || amount <= 0) {
|
|
89
|
+
throw new Error(`debit() requires a positive integer amount, got ${amount}`);
|
|
90
|
+
}
|
|
91
|
+
if (!type) {
|
|
92
|
+
throw new Error('debit() requires a type (e.g., "ai")');
|
|
93
|
+
}
|
|
94
|
+
if (!source) {
|
|
95
|
+
throw new Error('debit() requires a source ("system" | "admin" | "user")');
|
|
96
|
+
}
|
|
97
|
+
await insertTransaction({
|
|
98
|
+
db,
|
|
99
|
+
user,
|
|
100
|
+
userId,
|
|
101
|
+
type,
|
|
102
|
+
category,
|
|
103
|
+
source,
|
|
104
|
+
amount: -amount,
|
|
105
|
+
stripeInvoiceId,
|
|
106
|
+
stripeEventId,
|
|
107
|
+
session,
|
|
108
|
+
...rest
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
credit,
|
|
114
|
+
debit
|
|
115
|
+
};
|
package/dist/transactions.cjs
CHANGED
|
@@ -19,37 +19,39 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
// transactions.js
|
|
20
20
|
var transactions_exports = {};
|
|
21
21
|
__export(transactions_exports, {
|
|
22
|
-
InsufficientCreditsError: () => InsufficientCreditsError,
|
|
23
22
|
credit: () => credit,
|
|
24
23
|
debit: () => debit
|
|
25
24
|
});
|
|
26
25
|
module.exports = __toCommonJS(transactions_exports);
|
|
27
|
-
var
|
|
26
|
+
var insertTransaction = async ({
|
|
28
27
|
db,
|
|
29
|
-
|
|
28
|
+
user,
|
|
30
29
|
userId,
|
|
30
|
+
type,
|
|
31
31
|
category,
|
|
32
|
-
|
|
32
|
+
source,
|
|
33
33
|
amount,
|
|
34
|
-
|
|
35
|
-
provider,
|
|
34
|
+
_id,
|
|
36
35
|
stripeInvoiceId,
|
|
37
36
|
stripeEventId,
|
|
38
|
-
session
|
|
37
|
+
session,
|
|
38
|
+
...rest
|
|
39
39
|
}) => {
|
|
40
|
+
var _a;
|
|
41
|
+
const beforeRaw = (_a = user == null ? void 0 : user.balance) == null ? void 0 : _a[type];
|
|
42
|
+
const balance = typeof beforeRaw === "number" ? { before: beforeRaw, after: beforeRaw + amount } : void 0;
|
|
40
43
|
await db.create({
|
|
41
|
-
authenticated,
|
|
44
|
+
authenticated: user,
|
|
42
45
|
collection: "transaction",
|
|
43
46
|
data: {
|
|
47
|
+
..._id && { _id },
|
|
44
48
|
userId,
|
|
45
|
-
|
|
46
|
-
item: "ai"
|
|
47
|
-
},
|
|
49
|
+
type,
|
|
48
50
|
category,
|
|
49
|
-
|
|
51
|
+
source,
|
|
50
52
|
amount,
|
|
51
|
-
balance,
|
|
52
|
-
...
|
|
53
|
+
...balance && { balance },
|
|
54
|
+
...rest,
|
|
53
55
|
...stripeInvoiceId && { stripeInvoiceId },
|
|
54
56
|
...stripeEventId && { stripeEventId }
|
|
55
57
|
},
|
|
@@ -58,130 +60,80 @@ var recordTransaction = async ({
|
|
|
58
60
|
};
|
|
59
61
|
var credit = async ({
|
|
60
62
|
db,
|
|
61
|
-
|
|
63
|
+
user,
|
|
62
64
|
userId,
|
|
63
65
|
amount,
|
|
66
|
+
type,
|
|
64
67
|
category,
|
|
65
|
-
|
|
68
|
+
source,
|
|
69
|
+
_id,
|
|
66
70
|
stripeInvoiceId,
|
|
67
71
|
stripeEventId,
|
|
68
|
-
session
|
|
72
|
+
session,
|
|
73
|
+
...rest
|
|
69
74
|
}) => {
|
|
70
|
-
var _a, _b, _c;
|
|
71
75
|
if (!Number.isInteger(amount) || amount <= 0) {
|
|
72
76
|
throw new Error(`credit() requires a positive integer amount, got ${amount}`);
|
|
73
77
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
collection: "user",
|
|
77
|
-
query: {
|
|
78
|
-
id: userId
|
|
79
|
-
},
|
|
80
|
-
data: {
|
|
81
|
-
$inc: {
|
|
82
|
-
"balance.ai": amount
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
...session && { options: { session } }
|
|
86
|
-
});
|
|
87
|
-
const after = ((_b = (_a = result == null ? void 0 : result.value) == null ? void 0 : _a.balance) == null ? void 0 : _b.ai) ?? ((_c = result == null ? void 0 : result.balance) == null ? void 0 : _c.ai);
|
|
88
|
-
if (typeof after !== "number") {
|
|
89
|
-
throw new Error(`credit() could not read updated balance for user ${userId}`);
|
|
78
|
+
if (!type) {
|
|
79
|
+
throw new Error('credit() requires a type (e.g., "ai")');
|
|
90
80
|
}
|
|
91
|
-
|
|
92
|
-
|
|
81
|
+
if (!source) {
|
|
82
|
+
throw new Error('credit() requires a source ("system" | "admin" | "user")');
|
|
83
|
+
}
|
|
84
|
+
await insertTransaction({
|
|
93
85
|
db,
|
|
94
|
-
|
|
86
|
+
user,
|
|
95
87
|
userId,
|
|
88
|
+
type,
|
|
96
89
|
category,
|
|
97
|
-
|
|
90
|
+
source,
|
|
98
91
|
amount,
|
|
99
|
-
|
|
100
|
-
before,
|
|
101
|
-
after
|
|
102
|
-
},
|
|
92
|
+
_id,
|
|
103
93
|
stripeInvoiceId,
|
|
104
94
|
stripeEventId,
|
|
105
|
-
session
|
|
95
|
+
session,
|
|
96
|
+
...rest
|
|
106
97
|
});
|
|
107
|
-
return {
|
|
108
|
-
balance: {
|
|
109
|
-
before,
|
|
110
|
-
after
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
};
|
|
114
|
-
var InsufficientCreditsError = class extends Error {
|
|
115
|
-
constructor(message = "Insufficient AI credits") {
|
|
116
|
-
super(message);
|
|
117
|
-
this.name = "InsufficientCreditsError";
|
|
118
|
-
this.status = 402;
|
|
119
|
-
}
|
|
120
98
|
};
|
|
121
99
|
var debit = async ({
|
|
122
100
|
db,
|
|
123
|
-
|
|
101
|
+
user,
|
|
124
102
|
userId,
|
|
125
103
|
amount,
|
|
104
|
+
type,
|
|
126
105
|
category,
|
|
127
|
-
|
|
128
|
-
provider,
|
|
106
|
+
source,
|
|
129
107
|
stripeInvoiceId,
|
|
130
108
|
stripeEventId,
|
|
131
|
-
session
|
|
109
|
+
session,
|
|
110
|
+
...rest
|
|
132
111
|
}) => {
|
|
133
|
-
var _a, _b, _c;
|
|
134
112
|
if (!Number.isInteger(amount) || amount <= 0) {
|
|
135
113
|
throw new Error(`debit() requires a positive integer amount, got ${amount}`);
|
|
136
114
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"balance.ai": {
|
|
143
|
-
$gte: amount
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
data: {
|
|
147
|
-
$inc: {
|
|
148
|
-
"balance.ai": -amount
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
...session && { options: { session } }
|
|
152
|
-
});
|
|
153
|
-
const after = ((_b = (_a = result == null ? void 0 : result.value) == null ? void 0 : _a.balance) == null ? void 0 : _b.ai) ?? ((_c = result == null ? void 0 : result.balance) == null ? void 0 : _c.ai);
|
|
154
|
-
if (typeof after !== "number") {
|
|
155
|
-
throw new InsufficientCreditsError();
|
|
115
|
+
if (!type) {
|
|
116
|
+
throw new Error('debit() requires a type (e.g., "ai")');
|
|
117
|
+
}
|
|
118
|
+
if (!source) {
|
|
119
|
+
throw new Error('debit() requires a source ("system" | "admin" | "user")');
|
|
156
120
|
}
|
|
157
|
-
|
|
158
|
-
await recordTransaction({
|
|
121
|
+
await insertTransaction({
|
|
159
122
|
db,
|
|
160
|
-
|
|
123
|
+
user,
|
|
161
124
|
userId,
|
|
125
|
+
type,
|
|
162
126
|
category,
|
|
163
|
-
|
|
127
|
+
source,
|
|
164
128
|
amount: -amount,
|
|
165
|
-
balance: {
|
|
166
|
-
before,
|
|
167
|
-
after
|
|
168
|
-
},
|
|
169
|
-
provider,
|
|
170
129
|
stripeInvoiceId,
|
|
171
130
|
stripeEventId,
|
|
172
|
-
session
|
|
131
|
+
session,
|
|
132
|
+
...rest
|
|
173
133
|
});
|
|
174
|
-
return {
|
|
175
|
-
amount: -amount,
|
|
176
|
-
balance: {
|
|
177
|
-
before,
|
|
178
|
-
after
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
134
|
};
|
|
182
135
|
// Annotate the CommonJS export names for ESM import in node:
|
|
183
136
|
0 && (module.exports = {
|
|
184
|
-
InsufficientCreditsError,
|
|
185
137
|
credit,
|
|
186
138
|
debit
|
|
187
139
|
});
|
package/dist/transactions.d.cts
CHANGED
|
@@ -1,56 +1,63 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Ledger transaction insert helpers — no balance writes happen here.
|
|
2
|
+
//
|
|
3
|
+
// User-balance updates are the exclusive job of drawbridge-sync's
|
|
4
|
+
// stream/transaction.js change-stream listener: every insert into the
|
|
5
|
+
// `transaction` collection triggers an atomic $inc on user.balance.<type>
|
|
6
|
+
// (guarded against going negative). This decouples the API surface from
|
|
7
|
+
// the balance arithmetic and makes the `transaction` collection the single
|
|
8
|
+
// source of truth for both audit history and balance derivation.
|
|
5
9
|
//
|
|
6
10
|
// import { credit, debit } from '@drawbridge/drawbridge-utils/transactions';
|
|
7
11
|
//
|
|
8
|
-
// await credit({ db,
|
|
9
|
-
// category: 'promotional',
|
|
12
|
+
// await credit({ db, user, amount: 500,
|
|
13
|
+
// type: 'ai', category: 'promotional', source: 'system' });
|
|
10
14
|
//
|
|
11
|
-
// await debit({ db,
|
|
12
|
-
//
|
|
13
|
-
//
|
|
15
|
+
// await debit({ db, user, amount: 6,
|
|
16
|
+
// type: 'ai', category: 'usage', source: 'user',
|
|
17
|
+
// ai: { name, model, tokens, rates, totals } });
|
|
14
18
|
//
|
|
15
|
-
// `db` is the wrapper returned by @drawbridge/mongodb — credit/debit
|
|
16
|
-
// against its `.create
|
|
19
|
+
// `db` is the wrapper returned by @drawbridge/mongodb — credit/debit
|
|
20
|
+
// duck-type against its `.create` interface so this module has no hard dep.
|
|
17
21
|
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// the
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// contract.
|
|
27
|
-
const recordTransaction = async ({
|
|
22
|
+
// `user` is the user the transaction is being recorded against. The helper
|
|
23
|
+
// reads `user.balance.<type>` to stamp `balance: { before, after }` on the
|
|
24
|
+
// row as an audit snapshot of what the actor saw at action time — useful
|
|
25
|
+
// for support / dispute resolution. When `balance.<type>` isn't on the user
|
|
26
|
+
// (fresh signup, webhook-initiated row), the snapshot is omitted, which is
|
|
27
|
+
// the honest representation of "no prior view to record".
|
|
28
|
+
|
|
29
|
+
const insertTransaction = async ({
|
|
28
30
|
db,
|
|
29
|
-
|
|
31
|
+
user,
|
|
30
32
|
userId,
|
|
33
|
+
type,
|
|
31
34
|
category,
|
|
32
|
-
|
|
35
|
+
source,
|
|
33
36
|
amount,
|
|
34
|
-
|
|
35
|
-
provider,
|
|
37
|
+
_id,
|
|
36
38
|
stripeInvoiceId,
|
|
37
39
|
stripeEventId,
|
|
38
|
-
session
|
|
40
|
+
session,
|
|
41
|
+
...rest
|
|
39
42
|
}) => {
|
|
40
43
|
|
|
44
|
+
const beforeRaw = user?.balance?.[ type ];
|
|
45
|
+
const balance = typeof beforeRaw === 'number'
|
|
46
|
+
? { before : beforeRaw, after : beforeRaw + amount }
|
|
47
|
+
: undefined;
|
|
48
|
+
|
|
41
49
|
await db.create({
|
|
42
|
-
authenticated,
|
|
50
|
+
authenticated : user,
|
|
43
51
|
collection : 'transaction',
|
|
44
52
|
data : {
|
|
53
|
+
...( _id && { _id }),
|
|
45
54
|
userId,
|
|
46
|
-
|
|
47
|
-
item : 'ai'
|
|
48
|
-
},
|
|
55
|
+
type,
|
|
49
56
|
category,
|
|
50
|
-
|
|
57
|
+
source,
|
|
51
58
|
amount,
|
|
52
|
-
balance,
|
|
53
|
-
...
|
|
59
|
+
...( balance && { balance }),
|
|
60
|
+
...rest,
|
|
54
61
|
...( stripeInvoiceId && { stripeInvoiceId }),
|
|
55
62
|
...( stripeEventId && { stripeEventId })
|
|
56
63
|
},
|
|
@@ -59,20 +66,23 @@ const recordTransaction = async ({
|
|
|
59
66
|
|
|
60
67
|
};
|
|
61
68
|
|
|
62
|
-
// Additive:
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
69
|
+
// Additive: record a credit. The stream handler does the $inc separately.
|
|
70
|
+
// Optional `_id` lets callers pre-generate the transaction's ObjectId — the
|
|
71
|
+
// top-up route stamps it onto the Stripe invoice metadata so Stripe-side and
|
|
72
|
+
// Mongo-side IDs match for cross-system reconciliation.
|
|
66
73
|
const credit = async ({
|
|
67
74
|
db,
|
|
68
|
-
|
|
75
|
+
user,
|
|
69
76
|
userId,
|
|
70
77
|
amount,
|
|
78
|
+
type,
|
|
71
79
|
category,
|
|
72
|
-
|
|
80
|
+
source,
|
|
81
|
+
_id,
|
|
73
82
|
stripeInvoiceId,
|
|
74
83
|
stripeEventId,
|
|
75
|
-
session
|
|
84
|
+
session,
|
|
85
|
+
...rest
|
|
76
86
|
}) => {
|
|
77
87
|
|
|
78
88
|
if( !Number.isInteger( amount ) || amount <= 0 ){
|
|
@@ -81,83 +91,58 @@ const credit = async ({
|
|
|
81
91
|
|
|
82
92
|
}
|
|
83
93
|
|
|
84
|
-
|
|
85
|
-
authenticated,
|
|
86
|
-
collection : 'user',
|
|
87
|
-
query : {
|
|
88
|
-
id : userId
|
|
89
|
-
},
|
|
90
|
-
data : {
|
|
91
|
-
$inc : {
|
|
92
|
-
'balance.ai' : amount
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
...( session && { options : { session } })
|
|
96
|
-
});
|
|
94
|
+
if( !type ){
|
|
97
95
|
|
|
98
|
-
|
|
96
|
+
throw new Error( 'credit() requires a type (e.g., "ai")' );
|
|
99
97
|
|
|
100
|
-
|
|
98
|
+
}
|
|
101
99
|
|
|
102
|
-
|
|
100
|
+
if( !source ){
|
|
103
101
|
|
|
104
|
-
|
|
102
|
+
throw new Error( 'credit() requires a source ("system" | "admin" | "user")' );
|
|
105
103
|
|
|
106
|
-
|
|
104
|
+
}
|
|
107
105
|
|
|
108
|
-
await
|
|
106
|
+
await insertTransaction({
|
|
109
107
|
db,
|
|
110
|
-
|
|
108
|
+
user,
|
|
111
109
|
userId,
|
|
110
|
+
type,
|
|
112
111
|
category,
|
|
113
|
-
|
|
112
|
+
source,
|
|
114
113
|
amount,
|
|
115
|
-
|
|
116
|
-
before,
|
|
117
|
-
after
|
|
118
|
-
},
|
|
114
|
+
_id,
|
|
119
115
|
stripeInvoiceId,
|
|
120
116
|
stripeEventId,
|
|
121
|
-
session
|
|
117
|
+
session,
|
|
118
|
+
...rest
|
|
122
119
|
});
|
|
123
120
|
|
|
124
|
-
return {
|
|
125
|
-
balance : {
|
|
126
|
-
before,
|
|
127
|
-
after
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
|
|
131
121
|
};
|
|
132
122
|
|
|
133
|
-
// Subtractive:
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
this.status = 402;
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
123
|
+
// Subtractive: record a debit (negative amount). The stream handler's $inc
|
|
124
|
+
// is guarded against negative balance — if the resulting balance would be
|
|
125
|
+
// less than zero, the $inc is skipped and the row stays as audit-only.
|
|
126
|
+
// Callers (e.g., ai.js) pre-compute `amount` from their domain-specific
|
|
127
|
+
// pricing.
|
|
128
|
+
//
|
|
129
|
+
// Note: there is no synchronous gate here. The atomic gate-and-decrement
|
|
130
|
+
// pattern moves to the AI middleware (`balance.<type> >= MAX_REQUEST_CENTS`)
|
|
131
|
+
// plus the stream-side $inc guard. Concurrent debits with low balance can
|
|
132
|
+
// race past the middleware check but the stream guard prevents the actual
|
|
133
|
+
// balance from going negative.
|
|
150
134
|
const debit = async ({
|
|
151
135
|
db,
|
|
152
|
-
|
|
136
|
+
user,
|
|
153
137
|
userId,
|
|
154
138
|
amount,
|
|
139
|
+
type,
|
|
155
140
|
category,
|
|
156
|
-
|
|
157
|
-
provider,
|
|
141
|
+
source,
|
|
158
142
|
stripeInvoiceId,
|
|
159
143
|
stripeEventId,
|
|
160
|
-
session
|
|
144
|
+
session,
|
|
145
|
+
...rest
|
|
161
146
|
}) => {
|
|
162
147
|
|
|
163
148
|
if( !Number.isInteger( amount ) || amount <= 0 ){
|
|
@@ -166,58 +151,32 @@ const debit = async ({
|
|
|
166
151
|
|
|
167
152
|
}
|
|
168
153
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
query : {
|
|
173
|
-
id : userId,
|
|
174
|
-
'balance.ai' : {
|
|
175
|
-
$gte : amount
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
data : {
|
|
179
|
-
$inc : {
|
|
180
|
-
'balance.ai' : -amount
|
|
181
|
-
}
|
|
182
|
-
},
|
|
183
|
-
...( session && { options : { session } })
|
|
184
|
-
});
|
|
154
|
+
if( !type ){
|
|
155
|
+
|
|
156
|
+
throw new Error( 'debit() requires a type (e.g., "ai")' );
|
|
185
157
|
|
|
186
|
-
|
|
158
|
+
}
|
|
187
159
|
|
|
188
|
-
if(
|
|
160
|
+
if( !source ){
|
|
189
161
|
|
|
190
|
-
throw new
|
|
162
|
+
throw new Error( 'debit() requires a source ("system" | "admin" | "user")' );
|
|
191
163
|
|
|
192
164
|
}
|
|
193
165
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
await recordTransaction({
|
|
166
|
+
await insertTransaction({
|
|
197
167
|
db,
|
|
198
|
-
|
|
168
|
+
user,
|
|
199
169
|
userId,
|
|
170
|
+
type,
|
|
200
171
|
category,
|
|
201
|
-
|
|
172
|
+
source,
|
|
202
173
|
amount : -amount,
|
|
203
|
-
balance : {
|
|
204
|
-
before,
|
|
205
|
-
after
|
|
206
|
-
},
|
|
207
|
-
provider,
|
|
208
174
|
stripeInvoiceId,
|
|
209
175
|
stripeEventId,
|
|
210
|
-
session
|
|
176
|
+
session,
|
|
177
|
+
...rest
|
|
211
178
|
});
|
|
212
179
|
|
|
213
|
-
return {
|
|
214
|
-
amount : -amount,
|
|
215
|
-
balance : {
|
|
216
|
-
before,
|
|
217
|
-
after
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
|
|
221
180
|
};
|
|
222
181
|
|
|
223
|
-
export {
|
|
182
|
+
export { credit, debit };
|