@pot-sdk2/pay 0.9.2 → 0.9.3
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/index.cjs +55 -0
- package/dist/index.d.cts +34 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +54 -0
- package/package.json +12 -3
- package/src/index.ts +2 -0
- package/src/transaction-policy.test.ts +56 -0
- package/src/transaction-policy.ts +88 -0
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
TransactionPolicy: () => TransactionPolicy,
|
|
23
24
|
VERIFIER_PROFILES: () => VERIFIER_PROFILES,
|
|
24
25
|
buildAttestationHeaders: () => buildAttestationHeaders,
|
|
25
26
|
getProfile: () => getProfile,
|
|
@@ -295,8 +296,62 @@ function wrapClient(client, options) {
|
|
|
295
296
|
};
|
|
296
297
|
return wrapped;
|
|
297
298
|
}
|
|
299
|
+
|
|
300
|
+
// src/transaction-policy.ts
|
|
301
|
+
var TransactionPolicy = class {
|
|
302
|
+
config;
|
|
303
|
+
dailySpend = /* @__PURE__ */ new Map();
|
|
304
|
+
constructor(config = {}) {
|
|
305
|
+
this.config = {
|
|
306
|
+
requireVerificationAbove: 50,
|
|
307
|
+
...config
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
check(tx) {
|
|
311
|
+
const { to, amount } = tx;
|
|
312
|
+
const threshold = this.config.requireVerificationAbove ?? 50;
|
|
313
|
+
const requiresVerification = amount >= threshold;
|
|
314
|
+
if (this.config.blockedAddresses?.length) {
|
|
315
|
+
const toLower = to.toLowerCase();
|
|
316
|
+
if (this.config.blockedAddresses.some((a) => a.toLowerCase() === toLower)) {
|
|
317
|
+
return { allowed: false, reason: `Address ${to} is blocked`, requiresVerification };
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (this.config.allowedAddresses?.length) {
|
|
321
|
+
const toLower = to.toLowerCase();
|
|
322
|
+
if (!this.config.allowedAddresses.some((a) => a.toLowerCase() === toLower)) {
|
|
323
|
+
return { allowed: false, reason: `Address ${to} is not in allowedAddresses`, requiresVerification };
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (this.config.maxPerTransaction !== void 0 && amount > this.config.maxPerTransaction) {
|
|
327
|
+
return {
|
|
328
|
+
allowed: false,
|
|
329
|
+
reason: `Amount $${amount} exceeds maxPerTransaction ($${this.config.maxPerTransaction})`,
|
|
330
|
+
requiresVerification
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
if (this.config.dailyCap !== void 0) {
|
|
334
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
335
|
+
const spent = this.dailySpend.get(today) ?? 0;
|
|
336
|
+
if (spent + amount > this.config.dailyCap) {
|
|
337
|
+
return {
|
|
338
|
+
allowed: false,
|
|
339
|
+
reason: `Daily cap ($${this.config.dailyCap}) would be exceeded. Already spent: $${spent}`,
|
|
340
|
+
requiresVerification
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
this.dailySpend.set(today, spent + amount);
|
|
344
|
+
}
|
|
345
|
+
return { allowed: true, requiresVerification };
|
|
346
|
+
}
|
|
347
|
+
/** Reset daily spend tracking (useful for testing) */
|
|
348
|
+
resetDailySpend() {
|
|
349
|
+
this.dailySpend.clear();
|
|
350
|
+
}
|
|
351
|
+
};
|
|
298
352
|
// Annotate the CommonJS export names for ESM import in node:
|
|
299
353
|
0 && (module.exports = {
|
|
354
|
+
TransactionPolicy,
|
|
300
355
|
VERIFIER_PROFILES,
|
|
301
356
|
buildAttestationHeaders,
|
|
302
357
|
getProfile,
|
package/dist/index.d.cts
CHANGED
|
@@ -101,6 +101,39 @@ interface PolicyResult {
|
|
|
101
101
|
}
|
|
102
102
|
declare function resolvePolicy(amount: number, policy?: 'tiered' | 'always' | 'skip'): PolicyResult;
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* TransactionPolicy — spending limits, address allowlists, verification thresholds
|
|
106
|
+
* @pot-sdk2/pay v0.9.3
|
|
107
|
+
*/
|
|
108
|
+
interface TransactionPolicyConfig {
|
|
109
|
+
/** Max USD per single transaction */
|
|
110
|
+
maxPerTransaction?: number;
|
|
111
|
+
/** Max USD spent per calendar day */
|
|
112
|
+
dailyCap?: number;
|
|
113
|
+
/** If set, only these addresses are allowed (case-insensitive) */
|
|
114
|
+
allowedAddresses?: string[];
|
|
115
|
+
/** Always blocked addresses (case-insensitive) */
|
|
116
|
+
blockedAddresses?: string[];
|
|
117
|
+
/** Require reasoning verification above this USD amount (default: 50) */
|
|
118
|
+
requireVerificationAbove?: number;
|
|
119
|
+
}
|
|
120
|
+
interface PolicyCheckResult {
|
|
121
|
+
allowed: boolean;
|
|
122
|
+
reason?: string;
|
|
123
|
+
requiresVerification: boolean;
|
|
124
|
+
}
|
|
125
|
+
declare class TransactionPolicy {
|
|
126
|
+
private config;
|
|
127
|
+
private dailySpend;
|
|
128
|
+
constructor(config?: TransactionPolicyConfig);
|
|
129
|
+
check(tx: {
|
|
130
|
+
to: string;
|
|
131
|
+
amount: number;
|
|
132
|
+
}): PolicyCheckResult;
|
|
133
|
+
/** Reset daily spend tracking (useful for testing) */
|
|
134
|
+
resetDailySpend(): void;
|
|
135
|
+
}
|
|
136
|
+
|
|
104
137
|
/**
|
|
105
138
|
* Generates X-402-Attestation-* headers from a verify result.
|
|
106
139
|
* These headers can be attached to x402 payment requests.
|
|
@@ -172,4 +205,4 @@ declare function warnIfNoHighPerformanceVerifier(modelIds: string[]): string | n
|
|
|
172
205
|
*/
|
|
173
206
|
declare function getWeight(modelId: string): number;
|
|
174
207
|
|
|
175
|
-
export { type PayVerifyOptions, type PayVerifyResult, type PayWrapOptions, type PaymentIntent, VERIFIER_PROFILES, type VerifierProfile, buildAttestationHeaders, getProfile, getRecommendedVerifiers, getWeight, resolvePolicy, verifyPayment, warnIfNoHighPerformanceVerifier, wrapClient };
|
|
208
|
+
export { type PayVerifyOptions, type PayVerifyResult, type PayWrapOptions, type PaymentIntent, type PolicyCheckResult, TransactionPolicy, type TransactionPolicyConfig, VERIFIER_PROFILES, type VerifierProfile, buildAttestationHeaders, getProfile, getRecommendedVerifiers, getWeight, resolvePolicy, verifyPayment, warnIfNoHighPerformanceVerifier, wrapClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -101,6 +101,39 @@ interface PolicyResult {
|
|
|
101
101
|
}
|
|
102
102
|
declare function resolvePolicy(amount: number, policy?: 'tiered' | 'always' | 'skip'): PolicyResult;
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* TransactionPolicy — spending limits, address allowlists, verification thresholds
|
|
106
|
+
* @pot-sdk2/pay v0.9.3
|
|
107
|
+
*/
|
|
108
|
+
interface TransactionPolicyConfig {
|
|
109
|
+
/** Max USD per single transaction */
|
|
110
|
+
maxPerTransaction?: number;
|
|
111
|
+
/** Max USD spent per calendar day */
|
|
112
|
+
dailyCap?: number;
|
|
113
|
+
/** If set, only these addresses are allowed (case-insensitive) */
|
|
114
|
+
allowedAddresses?: string[];
|
|
115
|
+
/** Always blocked addresses (case-insensitive) */
|
|
116
|
+
blockedAddresses?: string[];
|
|
117
|
+
/** Require reasoning verification above this USD amount (default: 50) */
|
|
118
|
+
requireVerificationAbove?: number;
|
|
119
|
+
}
|
|
120
|
+
interface PolicyCheckResult {
|
|
121
|
+
allowed: boolean;
|
|
122
|
+
reason?: string;
|
|
123
|
+
requiresVerification: boolean;
|
|
124
|
+
}
|
|
125
|
+
declare class TransactionPolicy {
|
|
126
|
+
private config;
|
|
127
|
+
private dailySpend;
|
|
128
|
+
constructor(config?: TransactionPolicyConfig);
|
|
129
|
+
check(tx: {
|
|
130
|
+
to: string;
|
|
131
|
+
amount: number;
|
|
132
|
+
}): PolicyCheckResult;
|
|
133
|
+
/** Reset daily spend tracking (useful for testing) */
|
|
134
|
+
resetDailySpend(): void;
|
|
135
|
+
}
|
|
136
|
+
|
|
104
137
|
/**
|
|
105
138
|
* Generates X-402-Attestation-* headers from a verify result.
|
|
106
139
|
* These headers can be attached to x402 payment requests.
|
|
@@ -172,4 +205,4 @@ declare function warnIfNoHighPerformanceVerifier(modelIds: string[]): string | n
|
|
|
172
205
|
*/
|
|
173
206
|
declare function getWeight(modelId: string): number;
|
|
174
207
|
|
|
175
|
-
export { type PayVerifyOptions, type PayVerifyResult, type PayWrapOptions, type PaymentIntent, VERIFIER_PROFILES, type VerifierProfile, buildAttestationHeaders, getProfile, getRecommendedVerifiers, getWeight, resolvePolicy, verifyPayment, warnIfNoHighPerformanceVerifier, wrapClient };
|
|
208
|
+
export { type PayVerifyOptions, type PayVerifyResult, type PayWrapOptions, type PaymentIntent, type PolicyCheckResult, TransactionPolicy, type TransactionPolicyConfig, VERIFIER_PROFILES, type VerifierProfile, buildAttestationHeaders, getProfile, getRecommendedVerifiers, getWeight, resolvePolicy, verifyPayment, warnIfNoHighPerformanceVerifier, wrapClient };
|
package/dist/index.js
CHANGED
|
@@ -261,7 +261,61 @@ function wrapClient(client, options) {
|
|
|
261
261
|
};
|
|
262
262
|
return wrapped;
|
|
263
263
|
}
|
|
264
|
+
|
|
265
|
+
// src/transaction-policy.ts
|
|
266
|
+
var TransactionPolicy = class {
|
|
267
|
+
config;
|
|
268
|
+
dailySpend = /* @__PURE__ */ new Map();
|
|
269
|
+
constructor(config = {}) {
|
|
270
|
+
this.config = {
|
|
271
|
+
requireVerificationAbove: 50,
|
|
272
|
+
...config
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
check(tx) {
|
|
276
|
+
const { to, amount } = tx;
|
|
277
|
+
const threshold = this.config.requireVerificationAbove ?? 50;
|
|
278
|
+
const requiresVerification = amount >= threshold;
|
|
279
|
+
if (this.config.blockedAddresses?.length) {
|
|
280
|
+
const toLower = to.toLowerCase();
|
|
281
|
+
if (this.config.blockedAddresses.some((a) => a.toLowerCase() === toLower)) {
|
|
282
|
+
return { allowed: false, reason: `Address ${to} is blocked`, requiresVerification };
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (this.config.allowedAddresses?.length) {
|
|
286
|
+
const toLower = to.toLowerCase();
|
|
287
|
+
if (!this.config.allowedAddresses.some((a) => a.toLowerCase() === toLower)) {
|
|
288
|
+
return { allowed: false, reason: `Address ${to} is not in allowedAddresses`, requiresVerification };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (this.config.maxPerTransaction !== void 0 && amount > this.config.maxPerTransaction) {
|
|
292
|
+
return {
|
|
293
|
+
allowed: false,
|
|
294
|
+
reason: `Amount $${amount} exceeds maxPerTransaction ($${this.config.maxPerTransaction})`,
|
|
295
|
+
requiresVerification
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
if (this.config.dailyCap !== void 0) {
|
|
299
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
300
|
+
const spent = this.dailySpend.get(today) ?? 0;
|
|
301
|
+
if (spent + amount > this.config.dailyCap) {
|
|
302
|
+
return {
|
|
303
|
+
allowed: false,
|
|
304
|
+
reason: `Daily cap ($${this.config.dailyCap}) would be exceeded. Already spent: $${spent}`,
|
|
305
|
+
requiresVerification
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
this.dailySpend.set(today, spent + amount);
|
|
309
|
+
}
|
|
310
|
+
return { allowed: true, requiresVerification };
|
|
311
|
+
}
|
|
312
|
+
/** Reset daily spend tracking (useful for testing) */
|
|
313
|
+
resetDailySpend() {
|
|
314
|
+
this.dailySpend.clear();
|
|
315
|
+
}
|
|
316
|
+
};
|
|
264
317
|
export {
|
|
318
|
+
TransactionPolicy,
|
|
265
319
|
VERIFIER_PROFILES,
|
|
266
320
|
buildAttestationHeaders,
|
|
267
321
|
getProfile,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pot-sdk2/pay",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "Payment reasoning verification for pot-sdk — x402 attestation layer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"types": "./dist/index.d.ts"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
-
"engines": {
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=22.5.0"
|
|
18
|
+
},
|
|
17
19
|
"scripts": {
|
|
18
20
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
19
21
|
"test": "node --experimental-sqlite --import tsx/esm tests/pay.test.ts",
|
|
@@ -29,7 +31,14 @@
|
|
|
29
31
|
"typescript": "^5.0.0"
|
|
30
32
|
},
|
|
31
33
|
"license": "MIT",
|
|
32
|
-
"keywords": [
|
|
34
|
+
"keywords": [
|
|
35
|
+
"thoughtproof",
|
|
36
|
+
"pot-sdk",
|
|
37
|
+
"x402",
|
|
38
|
+
"agent-payments",
|
|
39
|
+
"verification",
|
|
40
|
+
"attestation"
|
|
41
|
+
],
|
|
33
42
|
"homepage": "https://thoughtproof.ai",
|
|
34
43
|
"repository": {
|
|
35
44
|
"type": "git",
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
export { verifyPayment } from './verify-payment.js';
|
|
30
30
|
export { wrapClient } from './middleware.js';
|
|
31
31
|
export { resolvePolicy } from './policy.js';
|
|
32
|
+
export { TransactionPolicy } from './transaction-policy.js';
|
|
33
|
+
export type { TransactionPolicyConfig, PolicyCheckResult } from './transaction-policy.js';
|
|
32
34
|
export { buildAttestationHeaders } from './headers.js';
|
|
33
35
|
export {
|
|
34
36
|
VERIFIER_PROFILES,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { TransactionPolicy } from './transaction-policy.js';
|
|
3
|
+
|
|
4
|
+
describe('TransactionPolicy', () => {
|
|
5
|
+
const ATTACKER = '0xdead000000000000000000000000000000001337';
|
|
6
|
+
const SAFE = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
|
|
7
|
+
|
|
8
|
+
it('blocks tx above maxPerTransaction', () => {
|
|
9
|
+
const policy = new TransactionPolicy({ maxPerTransaction: 100 });
|
|
10
|
+
const result = policy.check({ to: SAFE, amount: 101 });
|
|
11
|
+
expect(result.allowed).toBe(false);
|
|
12
|
+
expect(result.reason).toContain('maxPerTransaction');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('allows tx within maxPerTransaction', () => {
|
|
16
|
+
const policy = new TransactionPolicy({ maxPerTransaction: 100 });
|
|
17
|
+
const result = policy.check({ to: SAFE, amount: 99 });
|
|
18
|
+
expect(result.allowed).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('blocks unknown address when allowedAddresses is set', () => {
|
|
22
|
+
const policy = new TransactionPolicy({ allowedAddresses: [SAFE] });
|
|
23
|
+
const result = policy.check({ to: ATTACKER, amount: 10 });
|
|
24
|
+
expect(result.allowed).toBe(false);
|
|
25
|
+
expect(result.reason).toContain('allowedAddresses');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('allows known address when allowedAddresses is set', () => {
|
|
29
|
+
const policy = new TransactionPolicy({ allowedAddresses: [SAFE] });
|
|
30
|
+
const result = policy.check({ to: SAFE, amount: 10 });
|
|
31
|
+
expect(result.allowed).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('blocks when dailyCap exceeded', () => {
|
|
35
|
+
const policy = new TransactionPolicy({ dailyCap: 100 });
|
|
36
|
+
policy.check({ to: SAFE, amount: 80 }); // first tx — OK, records spend
|
|
37
|
+
const result = policy.check({ to: SAFE, amount: 30 }); // 80+30=110 > 100
|
|
38
|
+
expect(result.allowed).toBe(false);
|
|
39
|
+
expect(result.reason).toContain('Daily cap');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('sets requiresVerification=true above threshold', () => {
|
|
43
|
+
const policy = new TransactionPolicy({ requireVerificationAbove: 50 });
|
|
44
|
+
const below = policy.check({ to: SAFE, amount: 49 });
|
|
45
|
+
const above = policy.check({ to: SAFE, amount: 51 });
|
|
46
|
+
expect(below.requiresVerification).toBe(false);
|
|
47
|
+
expect(above.requiresVerification).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('blocks blockedAddresses', () => {
|
|
51
|
+
const policy = new TransactionPolicy({ blockedAddresses: [ATTACKER] });
|
|
52
|
+
const result = policy.check({ to: ATTACKER, amount: 1 });
|
|
53
|
+
expect(result.allowed).toBe(false);
|
|
54
|
+
expect(result.reason).toContain('blocked');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TransactionPolicy — spending limits, address allowlists, verification thresholds
|
|
3
|
+
* @pot-sdk2/pay v0.9.3
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface TransactionPolicyConfig {
|
|
7
|
+
/** Max USD per single transaction */
|
|
8
|
+
maxPerTransaction?: number;
|
|
9
|
+
/** Max USD spent per calendar day */
|
|
10
|
+
dailyCap?: number;
|
|
11
|
+
/** If set, only these addresses are allowed (case-insensitive) */
|
|
12
|
+
allowedAddresses?: string[];
|
|
13
|
+
/** Always blocked addresses (case-insensitive) */
|
|
14
|
+
blockedAddresses?: string[];
|
|
15
|
+
/** Require reasoning verification above this USD amount (default: 50) */
|
|
16
|
+
requireVerificationAbove?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PolicyCheckResult {
|
|
20
|
+
allowed: boolean;
|
|
21
|
+
reason?: string;
|
|
22
|
+
requiresVerification: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class TransactionPolicy {
|
|
26
|
+
private config: TransactionPolicyConfig;
|
|
27
|
+
private dailySpend: Map<string, number> = new Map();
|
|
28
|
+
|
|
29
|
+
constructor(config: TransactionPolicyConfig = {}) {
|
|
30
|
+
this.config = {
|
|
31
|
+
requireVerificationAbove: 50,
|
|
32
|
+
...config,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
check(tx: { to: string; amount: number }): PolicyCheckResult {
|
|
37
|
+
const { to, amount } = tx;
|
|
38
|
+
const threshold = this.config.requireVerificationAbove ?? 50;
|
|
39
|
+
const requiresVerification = amount >= threshold;
|
|
40
|
+
|
|
41
|
+
// 1. Blocked addresses
|
|
42
|
+
if (this.config.blockedAddresses?.length) {
|
|
43
|
+
const toLower = to.toLowerCase();
|
|
44
|
+
if (this.config.blockedAddresses.some(a => a.toLowerCase() === toLower)) {
|
|
45
|
+
return { allowed: false, reason: `Address ${to} is blocked`, requiresVerification };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 2. Allowlist check
|
|
50
|
+
if (this.config.allowedAddresses?.length) {
|
|
51
|
+
const toLower = to.toLowerCase();
|
|
52
|
+
if (!this.config.allowedAddresses.some(a => a.toLowerCase() === toLower)) {
|
|
53
|
+
return { allowed: false, reason: `Address ${to} is not in allowedAddresses`, requiresVerification };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 3. Per-transaction limit
|
|
58
|
+
if (this.config.maxPerTransaction !== undefined && amount > this.config.maxPerTransaction) {
|
|
59
|
+
return {
|
|
60
|
+
allowed: false,
|
|
61
|
+
reason: `Amount $${amount} exceeds maxPerTransaction ($${this.config.maxPerTransaction})`,
|
|
62
|
+
requiresVerification,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 4. Daily cap
|
|
67
|
+
if (this.config.dailyCap !== undefined) {
|
|
68
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
69
|
+
const spent = this.dailySpend.get(today) ?? 0;
|
|
70
|
+
if (spent + amount > this.config.dailyCap) {
|
|
71
|
+
return {
|
|
72
|
+
allowed: false,
|
|
73
|
+
reason: `Daily cap ($${this.config.dailyCap}) would be exceeded. Already spent: $${spent}`,
|
|
74
|
+
requiresVerification,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Record spend
|
|
78
|
+
this.dailySpend.set(today, spent + amount);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { allowed: true, requiresVerification };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Reset daily spend tracking (useful for testing) */
|
|
85
|
+
resetDailySpend(): void {
|
|
86
|
+
this.dailySpend.clear();
|
|
87
|
+
}
|
|
88
|
+
}
|