@revealui/services 0.0.3 → 0.2.0
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/LICENSE +22 -0
- package/LICENSE.commercial +112 -0
- package/README.md +177 -0
- package/dist/api/create-checkout-session/index.d.ts +2 -0
- package/dist/api/create-checkout-session/index.d.ts.map +1 -0
- package/dist/api/create-checkout-session/index.js +61 -0
- package/dist/api/create-checkout-session/index.js.map +1 -0
- package/dist/api/create-portal-link/index.d.ts +2 -0
- package/dist/api/create-portal-link/index.d.ts.map +1 -0
- package/dist/api/create-portal-link/index.js +37 -0
- package/dist/api/create-portal-link/index.js.map +1 -0
- package/dist/api/handlers/customer-handlers.d.ts +27 -0
- package/dist/api/handlers/customer-handlers.d.ts.map +1 -0
- package/dist/api/handlers/customer-handlers.js +86 -0
- package/dist/api/handlers/customer-handlers.js.map +1 -0
- package/dist/api/handlers/index.d.ts +18 -0
- package/dist/api/handlers/index.d.ts.map +1 -0
- package/dist/api/handlers/index.js +18 -0
- package/dist/api/handlers/index.js.map +1 -0
- package/dist/api/handlers/invoice-handlers.d.ts +9 -0
- package/dist/api/handlers/invoice-handlers.d.ts.map +1 -0
- package/dist/api/handlers/invoice-handlers.js +52 -0
- package/dist/api/handlers/invoice-handlers.js.map +1 -0
- package/dist/api/handlers/payment-handlers.d.ts +13 -0
- package/dist/api/handlers/payment-handlers.d.ts.map +1 -0
- package/dist/api/handlers/payment-handlers.js +133 -0
- package/dist/api/handlers/payment-handlers.js.map +1 -0
- package/dist/api/handlers/payment-intent.d.ts +21 -0
- package/dist/api/handlers/payment-intent.d.ts.map +1 -0
- package/dist/api/handlers/payment-intent.js +87 -0
- package/dist/api/handlers/payment-intent.js.map +1 -0
- package/dist/api/handlers/product-handlers.d.ts +11 -0
- package/dist/api/handlers/product-handlers.d.ts.map +1 -0
- package/dist/api/handlers/product-handlers.js +43 -0
- package/dist/api/handlers/product-handlers.js.map +1 -0
- package/dist/api/handlers/subscription-handlers.d.ts +17 -0
- package/dist/api/handlers/subscription-handlers.d.ts.map +1 -0
- package/dist/api/handlers/subscription-handlers.js +119 -0
- package/dist/api/handlers/subscription-handlers.js.map +1 -0
- package/dist/api/index.d.ts +8 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +8 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/types/stripe.d.ts +42 -0
- package/dist/api/types/stripe.d.ts.map +1 -0
- package/dist/api/types/stripe.js +72 -0
- package/dist/api/types/stripe.js.map +1 -0
- package/dist/api/update-price/index.d.ts +42 -0
- package/dist/api/update-price/index.d.ts.map +1 -0
- package/dist/api/update-price/index.js +78 -0
- package/dist/api/update-price/index.js.map +1 -0
- package/dist/api/update-product/index.d.ts +44 -0
- package/dist/api/update-product/index.d.ts.map +1 -0
- package/dist/api/update-product/index.js +85 -0
- package/dist/api/update-product/index.js.map +1 -0
- package/dist/api/utils.d.ts +34 -0
- package/dist/api/utils.d.ts.map +1 -0
- package/dist/api/utils.js +66 -0
- package/dist/api/utils.js.map +1 -0
- package/dist/api/webhooks/index.d.ts +12 -0
- package/dist/api/webhooks/index.d.ts.map +1 -0
- package/dist/api/webhooks/index.js +280 -0
- package/dist/api/webhooks/index.js.map +1 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +12 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.d.ts +33 -24
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -41
- package/dist/index.js.map +1 -0
- package/dist/revealcoin/__tests__/client.test.d.ts +2 -0
- package/dist/revealcoin/__tests__/client.test.d.ts.map +1 -0
- package/dist/revealcoin/__tests__/client.test.js +207 -0
- package/dist/revealcoin/__tests__/client.test.js.map +1 -0
- package/dist/revealcoin/__tests__/config.test.d.ts +2 -0
- package/dist/revealcoin/__tests__/config.test.d.ts.map +1 -0
- package/dist/revealcoin/__tests__/config.test.js +91 -0
- package/dist/revealcoin/__tests__/config.test.js.map +1 -0
- package/dist/revealcoin/__tests__/oracle.test.d.ts +2 -0
- package/dist/revealcoin/__tests__/oracle.test.d.ts.map +1 -0
- package/dist/revealcoin/__tests__/oracle.test.js +238 -0
- package/dist/revealcoin/__tests__/oracle.test.js.map +1 -0
- package/dist/revealcoin/__tests__/safeguards.test.d.ts +2 -0
- package/dist/revealcoin/__tests__/safeguards.test.d.ts.map +1 -0
- package/dist/revealcoin/__tests__/safeguards.test.js +571 -0
- package/dist/revealcoin/__tests__/safeguards.test.js.map +1 -0
- package/dist/revealcoin/client.d.ts +51 -0
- package/dist/revealcoin/client.d.ts.map +1 -0
- package/dist/revealcoin/client.js +211 -0
- package/dist/revealcoin/client.js.map +1 -0
- package/dist/revealcoin/config.d.ts +32 -0
- package/dist/revealcoin/config.d.ts.map +1 -0
- package/dist/revealcoin/config.js +54 -0
- package/dist/revealcoin/config.js.map +1 -0
- package/dist/revealcoin/index.d.ts +5 -0
- package/dist/revealcoin/index.d.ts.map +1 -0
- package/dist/revealcoin/index.js +5 -0
- package/dist/revealcoin/index.js.map +1 -0
- package/dist/revealcoin/oracle.d.ts +81 -0
- package/dist/revealcoin/oracle.d.ts.map +1 -0
- package/dist/revealcoin/oracle.js +211 -0
- package/dist/revealcoin/oracle.js.map +1 -0
- package/dist/revealcoin/safeguards.d.ts +92 -0
- package/dist/revealcoin/safeguards.d.ts.map +1 -0
- package/dist/revealcoin/safeguards.js +240 -0
- package/dist/revealcoin/safeguards.js.map +1 -0
- package/dist/stripe/db-circuit-breaker.d.ts +47 -0
- package/dist/stripe/db-circuit-breaker.d.ts.map +1 -0
- package/dist/stripe/db-circuit-breaker.js +223 -0
- package/dist/stripe/db-circuit-breaker.js.map +1 -0
- package/dist/stripe/index.d.ts +2 -0
- package/dist/stripe/index.d.ts.map +1 -0
- package/dist/stripe/index.js +2 -0
- package/dist/stripe/index.js.map +1 -0
- package/dist/stripe/stripeClient.d.ts +126 -0
- package/dist/stripe/stripeClient.d.ts.map +1 -0
- package/dist/stripe/stripeClient.js +226 -0
- package/dist/stripe/stripeClient.js.map +1 -0
- package/dist/supabase/index.d.ts +6 -0
- package/dist/supabase/index.d.ts.map +1 -0
- package/dist/supabase/index.js +5 -0
- package/dist/supabase/index.js.map +1 -0
- package/dist/supabase/resilience.d.ts +50 -0
- package/dist/supabase/resilience.d.ts.map +1 -0
- package/dist/supabase/resilience.js +166 -0
- package/dist/supabase/resilience.js.map +1 -0
- package/dist/supabase/types.d.ts +206 -0
- package/dist/supabase/types.d.ts.map +1 -0
- package/dist/supabase/types.js +19 -0
- package/dist/supabase/types.js.map +1 -0
- package/dist/supabase/utils/client.d.ts +4 -0
- package/dist/supabase/utils/client.d.ts.map +1 -0
- package/dist/supabase/utils/client.js +12 -0
- package/dist/supabase/utils/client.js.map +1 -0
- package/dist/supabase/utils/server.d.ts +10 -0
- package/dist/supabase/utils/server.d.ts.map +1 -0
- package/dist/supabase/utils/server.js +49 -0
- package/dist/supabase/utils/server.js.map +1 -0
- package/dist/supabase/utils/web.d.ts +4 -0
- package/dist/supabase/utils/web.d.ts.map +1 -0
- package/dist/supabase/utils/web.js +37 -0
- package/dist/supabase/utils/web.js.map +1 -0
- package/package.json +79 -16
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RevealCoin Anti-Manipulation Safeguards
|
|
3
|
+
*
|
|
4
|
+
* Prevents pump-and-dump schemes, arbitrage exploitation, and payment
|
|
5
|
+
* fraud through configurable rate limits, price circuit breakers, and
|
|
6
|
+
* transaction validation rules.
|
|
7
|
+
*
|
|
8
|
+
* All thresholds are parameterized per monorepo convention.
|
|
9
|
+
*/
|
|
10
|
+
export interface RevealCoinSafeguardsConfig {
|
|
11
|
+
/** TWAP window in milliseconds. Default: 3_600_000 (1 hour) */
|
|
12
|
+
twapWindowMs: number;
|
|
13
|
+
/** Price drop threshold (0–1) to trigger circuit breaker. Default: 0.30 (30%) */
|
|
14
|
+
priceCircuitBreakerThreshold: number;
|
|
15
|
+
/** Recovery check interval after circuit breaker trips. Default: 3_600_000 (1 hour) */
|
|
16
|
+
priceRecoveryCheckMs: number;
|
|
17
|
+
/** Max RVUI payments per wallet per hour. Default: 3 */
|
|
18
|
+
maxPaymentsPerWalletPerHour: number;
|
|
19
|
+
/** Max single payment in USD equivalent. Default: 500 */
|
|
20
|
+
maxSinglePaymentUsd: number;
|
|
21
|
+
/** Minimum token hold period in milliseconds. Default: 86_400_000 (24 hours) */
|
|
22
|
+
minHoldPeriodMs: number;
|
|
23
|
+
/** Max monthly discount savings per user in USD. Default: 100 */
|
|
24
|
+
maxMonthlyDiscountUsd: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function configureSafeguards(overrides: Partial<RevealCoinSafeguardsConfig>): void;
|
|
27
|
+
export declare function getSafeguardsConfig(): RevealCoinSafeguardsConfig;
|
|
28
|
+
export declare function resetSafeguardsConfig(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Calculate the time-weighted average price of RVUI in USD over the configured window.
|
|
31
|
+
*
|
|
32
|
+
* Uses price snapshots stored in the database. Returns null if insufficient
|
|
33
|
+
* data is available (fewer than 2 snapshots in the window).
|
|
34
|
+
*/
|
|
35
|
+
export declare function getTwapPrice(): Promise<number | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if the price circuit breaker should block RVUI payments.
|
|
38
|
+
*
|
|
39
|
+
* Compares the latest snapshot price to the TWAP. If the latest price
|
|
40
|
+
* has dropped more than the threshold below the TWAP, payments are blocked.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isPriceCircuitBreakerOpen(): Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Check if a wallet has exceeded the per-hour payment rate limit.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isWalletRateLimited(walletAddress: string): Promise<boolean>;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a user has exceeded their monthly discount savings cap.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isDiscountCapExceeded(userId: string): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Check if a transaction signature has already been recorded.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isDuplicateTransaction(txSignature: string): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Check if a payment amount exceeds the single-transaction USD cap.
|
|
57
|
+
*/
|
|
58
|
+
export declare function isPaymentOverMaximum(amountUsd: number): boolean;
|
|
59
|
+
export interface SafeguardCheckResult {
|
|
60
|
+
allowed: boolean;
|
|
61
|
+
reason?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Run all safeguard checks for an incoming RVUI payment.
|
|
65
|
+
*
|
|
66
|
+
* Returns `{ allowed: true }` if all checks pass, or `{ allowed: false, reason }`
|
|
67
|
+
* with the first failing check's reason.
|
|
68
|
+
*/
|
|
69
|
+
export declare function validatePayment(params: {
|
|
70
|
+
walletAddress: string;
|
|
71
|
+
userId: string;
|
|
72
|
+
txSignature: string;
|
|
73
|
+
amountUsd: number;
|
|
74
|
+
}): Promise<SafeguardCheckResult>;
|
|
75
|
+
/**
|
|
76
|
+
* Record a price snapshot for TWAP calculation.
|
|
77
|
+
* Called periodically by a cron job or price feed listener.
|
|
78
|
+
*/
|
|
79
|
+
export declare function recordPriceSnapshot(priceUsd: number, source: string): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Record a verified RVUI payment in the database.
|
|
82
|
+
*/
|
|
83
|
+
export declare function recordPayment(params: {
|
|
84
|
+
txSignature: string;
|
|
85
|
+
walletAddress: string;
|
|
86
|
+
userId: string;
|
|
87
|
+
amountRvui: string;
|
|
88
|
+
amountUsd: number;
|
|
89
|
+
discountUsd: number;
|
|
90
|
+
purpose: string;
|
|
91
|
+
}): Promise<void>;
|
|
92
|
+
//# sourceMappingURL=safeguards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safeguards.d.ts","sourceRoot":"","sources":["../../src/revealcoin/safeguards.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgBH,MAAM,WAAW,0BAA0B;IACzC,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,4BAA4B,EAAE,MAAM,CAAC;IACrC,uFAAuF;IACvF,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wDAAwD;IACxD,2BAA2B,EAAE,MAAM,CAAC;IACpC,yDAAyD;IACzD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gFAAgF;IAChF,eAAe,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAcD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,OAAO,CAAC,0BAA0B,CAAC,GAAG,IAAI,CAExF;AAED,wBAAgB,mBAAmB,IAAI,0BAA0B,CAEhE;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAMD;;;;;GAKG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiC3D;AAMD;;;;;GAKG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,OAAO,CAAC,CA4BlE;AAMD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgBjF;AAMD;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAkB5E;AAMD;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUlF;AAMD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE/D;AAMD,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAoChC;AAMD;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQzF;AAMD;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAchB"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RevealCoin Anti-Manipulation Safeguards
|
|
3
|
+
*
|
|
4
|
+
* Prevents pump-and-dump schemes, arbitrage exploitation, and payment
|
|
5
|
+
* fraud through configurable rate limits, price circuit breakers, and
|
|
6
|
+
* transaction validation rules.
|
|
7
|
+
*
|
|
8
|
+
* All thresholds are parameterized per monorepo convention.
|
|
9
|
+
*/
|
|
10
|
+
import { createLogger } from '@revealui/core/observability/logger';
|
|
11
|
+
import { getClient } from '@revealui/db';
|
|
12
|
+
import { revealcoinPayments, revealcoinPriceSnapshots, } from '@revealui/db/schema';
|
|
13
|
+
import { and, desc, eq, gte, sql } from 'drizzle-orm';
|
|
14
|
+
const logger = createLogger({ service: 'RevealCoin:Safeguards' });
|
|
15
|
+
const DEFAULT_CONFIG = {
|
|
16
|
+
twapWindowMs: 3_600_000,
|
|
17
|
+
priceCircuitBreakerThreshold: 0.30,
|
|
18
|
+
priceRecoveryCheckMs: 3_600_000,
|
|
19
|
+
maxPaymentsPerWalletPerHour: 3,
|
|
20
|
+
maxSinglePaymentUsd: 500,
|
|
21
|
+
minHoldPeriodMs: 86_400_000,
|
|
22
|
+
maxMonthlyDiscountUsd: 100,
|
|
23
|
+
};
|
|
24
|
+
let config = { ...DEFAULT_CONFIG };
|
|
25
|
+
export function configureSafeguards(overrides) {
|
|
26
|
+
config = { ...DEFAULT_CONFIG, ...overrides };
|
|
27
|
+
}
|
|
28
|
+
export function getSafeguardsConfig() {
|
|
29
|
+
return { ...config };
|
|
30
|
+
}
|
|
31
|
+
export function resetSafeguardsConfig() {
|
|
32
|
+
config = { ...DEFAULT_CONFIG };
|
|
33
|
+
}
|
|
34
|
+
// =============================================================================
|
|
35
|
+
// TWAP Price Oracle
|
|
36
|
+
// =============================================================================
|
|
37
|
+
/**
|
|
38
|
+
* Calculate the time-weighted average price of RVUI in USD over the configured window.
|
|
39
|
+
*
|
|
40
|
+
* Uses price snapshots stored in the database. Returns null if insufficient
|
|
41
|
+
* data is available (fewer than 2 snapshots in the window).
|
|
42
|
+
*/
|
|
43
|
+
export async function getTwapPrice() {
|
|
44
|
+
const db = getClient();
|
|
45
|
+
const windowStart = new Date(Date.now() - config.twapWindowMs);
|
|
46
|
+
const snapshots = await db
|
|
47
|
+
.select()
|
|
48
|
+
.from(revealcoinPriceSnapshots)
|
|
49
|
+
.where(gte(revealcoinPriceSnapshots.recordedAt, windowStart))
|
|
50
|
+
.orderBy(revealcoinPriceSnapshots.recordedAt);
|
|
51
|
+
if (snapshots.length < 2) {
|
|
52
|
+
logger.warn('Insufficient price snapshots for TWAP calculation', {
|
|
53
|
+
snapshotCount: snapshots.length,
|
|
54
|
+
windowMs: config.twapWindowMs,
|
|
55
|
+
});
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
// Time-weighted average: sum(price_i * duration_i) / total_duration
|
|
59
|
+
let weightedSum = 0;
|
|
60
|
+
let totalDuration = 0;
|
|
61
|
+
for (let i = 0; i < snapshots.length - 1; i++) {
|
|
62
|
+
const current = snapshots[i];
|
|
63
|
+
const next = snapshots[i + 1];
|
|
64
|
+
const duration = next.recordedAt.getTime() - current.recordedAt.getTime();
|
|
65
|
+
const price = Number(current.priceUsd);
|
|
66
|
+
weightedSum += price * duration;
|
|
67
|
+
totalDuration += duration;
|
|
68
|
+
}
|
|
69
|
+
if (totalDuration === 0)
|
|
70
|
+
return null;
|
|
71
|
+
return weightedSum / totalDuration;
|
|
72
|
+
}
|
|
73
|
+
// =============================================================================
|
|
74
|
+
// Price Circuit Breaker
|
|
75
|
+
// =============================================================================
|
|
76
|
+
/**
|
|
77
|
+
* Check if the price circuit breaker should block RVUI payments.
|
|
78
|
+
*
|
|
79
|
+
* Compares the latest snapshot price to the TWAP. If the latest price
|
|
80
|
+
* has dropped more than the threshold below the TWAP, payments are blocked.
|
|
81
|
+
*/
|
|
82
|
+
export async function isPriceCircuitBreakerOpen() {
|
|
83
|
+
const db = getClient();
|
|
84
|
+
const [latest] = await db
|
|
85
|
+
.select()
|
|
86
|
+
.from(revealcoinPriceSnapshots)
|
|
87
|
+
.orderBy(desc(revealcoinPriceSnapshots.recordedAt))
|
|
88
|
+
.limit(1);
|
|
89
|
+
if (!latest)
|
|
90
|
+
return true; // No price data — block payments
|
|
91
|
+
const twap = await getTwapPrice();
|
|
92
|
+
if (twap === null)
|
|
93
|
+
return true; // Insufficient data — block payments
|
|
94
|
+
const currentPrice = Number(latest.priceUsd);
|
|
95
|
+
const dropPercent = (twap - currentPrice) / twap;
|
|
96
|
+
if (dropPercent >= config.priceCircuitBreakerThreshold) {
|
|
97
|
+
logger.warn('RVUI price circuit breaker OPEN — price drop exceeds threshold', {
|
|
98
|
+
twap,
|
|
99
|
+
currentPrice,
|
|
100
|
+
dropPercent: `${(dropPercent * 100).toFixed(1)}%`,
|
|
101
|
+
threshold: `${(config.priceCircuitBreakerThreshold * 100).toFixed(0)}%`,
|
|
102
|
+
});
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
// =============================================================================
|
|
108
|
+
// Rate Limiting
|
|
109
|
+
// =============================================================================
|
|
110
|
+
/**
|
|
111
|
+
* Check if a wallet has exceeded the per-hour payment rate limit.
|
|
112
|
+
*/
|
|
113
|
+
export async function isWalletRateLimited(walletAddress) {
|
|
114
|
+
const db = getClient();
|
|
115
|
+
const oneHourAgo = new Date(Date.now() - 3_600_000);
|
|
116
|
+
const [result] = await db
|
|
117
|
+
.select({ count: sql `count(*)::int` })
|
|
118
|
+
.from(revealcoinPayments)
|
|
119
|
+
.where(and(eq(revealcoinPayments.walletAddress, walletAddress), gte(revealcoinPayments.createdAt, oneHourAgo)));
|
|
120
|
+
const count = result?.count ?? 0;
|
|
121
|
+
return count >= config.maxPaymentsPerWalletPerHour;
|
|
122
|
+
}
|
|
123
|
+
// =============================================================================
|
|
124
|
+
// Discount Abuse Prevention
|
|
125
|
+
// =============================================================================
|
|
126
|
+
/**
|
|
127
|
+
* Check if a user has exceeded their monthly discount savings cap.
|
|
128
|
+
*/
|
|
129
|
+
export async function isDiscountCapExceeded(userId) {
|
|
130
|
+
const db = getClient();
|
|
131
|
+
const monthStart = new Date();
|
|
132
|
+
monthStart.setDate(1);
|
|
133
|
+
monthStart.setHours(0, 0, 0, 0);
|
|
134
|
+
const [result] = await db
|
|
135
|
+
.select({ total: sql `coalesce(sum(${revealcoinPayments.discountUsd}), 0)::numeric` })
|
|
136
|
+
.from(revealcoinPayments)
|
|
137
|
+
.where(and(eq(revealcoinPayments.userId, userId), gte(revealcoinPayments.createdAt, monthStart)));
|
|
138
|
+
const totalDiscount = Number(result?.total ?? 0);
|
|
139
|
+
return totalDiscount >= config.maxMonthlyDiscountUsd;
|
|
140
|
+
}
|
|
141
|
+
// =============================================================================
|
|
142
|
+
// Duplicate Transaction Rejection
|
|
143
|
+
// =============================================================================
|
|
144
|
+
/**
|
|
145
|
+
* Check if a transaction signature has already been recorded.
|
|
146
|
+
*/
|
|
147
|
+
export async function isDuplicateTransaction(txSignature) {
|
|
148
|
+
const db = getClient();
|
|
149
|
+
const [existing] = await db
|
|
150
|
+
.select({ id: revealcoinPayments.id })
|
|
151
|
+
.from(revealcoinPayments)
|
|
152
|
+
.where(eq(revealcoinPayments.txSignature, txSignature))
|
|
153
|
+
.limit(1);
|
|
154
|
+
return existing !== undefined;
|
|
155
|
+
}
|
|
156
|
+
// =============================================================================
|
|
157
|
+
// Payment Amount Validation
|
|
158
|
+
// =============================================================================
|
|
159
|
+
/**
|
|
160
|
+
* Check if a payment amount exceeds the single-transaction USD cap.
|
|
161
|
+
*/
|
|
162
|
+
export function isPaymentOverMaximum(amountUsd) {
|
|
163
|
+
return amountUsd > config.maxSinglePaymentUsd;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Run all safeguard checks for an incoming RVUI payment.
|
|
167
|
+
*
|
|
168
|
+
* Returns `{ allowed: true }` if all checks pass, or `{ allowed: false, reason }`
|
|
169
|
+
* with the first failing check's reason.
|
|
170
|
+
*/
|
|
171
|
+
export async function validatePayment(params) {
|
|
172
|
+
// 1. Duplicate transaction
|
|
173
|
+
if (await isDuplicateTransaction(params.txSignature)) {
|
|
174
|
+
return { allowed: false, reason: 'Transaction signature already used' };
|
|
175
|
+
}
|
|
176
|
+
// 2. Price circuit breaker
|
|
177
|
+
if (await isPriceCircuitBreakerOpen()) {
|
|
178
|
+
return { allowed: false, reason: 'RVUI payments temporarily disabled due to price volatility' };
|
|
179
|
+
}
|
|
180
|
+
// 3. Single payment cap
|
|
181
|
+
if (isPaymentOverMaximum(params.amountUsd)) {
|
|
182
|
+
return {
|
|
183
|
+
allowed: false,
|
|
184
|
+
reason: `Payment exceeds maximum of $${config.maxSinglePaymentUsd} USD. Use fiat for larger amounts.`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// 4. Wallet rate limit
|
|
188
|
+
if (await isWalletRateLimited(params.walletAddress)) {
|
|
189
|
+
return {
|
|
190
|
+
allowed: false,
|
|
191
|
+
reason: `Wallet rate limit exceeded (max ${config.maxPaymentsPerWalletPerHour} payments/hour)`,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
// 5. Monthly discount cap
|
|
195
|
+
if (await isDiscountCapExceeded(params.userId)) {
|
|
196
|
+
return {
|
|
197
|
+
allowed: false,
|
|
198
|
+
reason: `Monthly RVUI discount cap of $${config.maxMonthlyDiscountUsd} reached`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return { allowed: true };
|
|
202
|
+
}
|
|
203
|
+
// =============================================================================
|
|
204
|
+
// Price Snapshot Recording
|
|
205
|
+
// =============================================================================
|
|
206
|
+
/**
|
|
207
|
+
* Record a price snapshot for TWAP calculation.
|
|
208
|
+
* Called periodically by a cron job or price feed listener.
|
|
209
|
+
*/
|
|
210
|
+
export async function recordPriceSnapshot(priceUsd, source) {
|
|
211
|
+
const db = getClient();
|
|
212
|
+
await db.insert(revealcoinPriceSnapshots).values({
|
|
213
|
+
id: crypto.randomUUID(),
|
|
214
|
+
priceUsd: String(priceUsd),
|
|
215
|
+
source,
|
|
216
|
+
recordedAt: new Date(),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
// =============================================================================
|
|
220
|
+
// Payment Recording
|
|
221
|
+
// =============================================================================
|
|
222
|
+
/**
|
|
223
|
+
* Record a verified RVUI payment in the database.
|
|
224
|
+
*/
|
|
225
|
+
export async function recordPayment(params) {
|
|
226
|
+
const db = getClient();
|
|
227
|
+
await db.insert(revealcoinPayments).values({
|
|
228
|
+
id: crypto.randomUUID(),
|
|
229
|
+
txSignature: params.txSignature,
|
|
230
|
+
walletAddress: params.walletAddress,
|
|
231
|
+
userId: params.userId,
|
|
232
|
+
amountRvui: params.amountRvui,
|
|
233
|
+
amountUsd: String(params.amountUsd),
|
|
234
|
+
discountUsd: String(params.discountUsd),
|
|
235
|
+
purpose: params.purpose,
|
|
236
|
+
status: 'verified',
|
|
237
|
+
createdAt: new Date(),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=safeguards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safeguards.js","sourceRoot":"","sources":["../../src/revealcoin/safeguards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DB-Backed Circuit Breaker
|
|
3
|
+
*
|
|
4
|
+
* Stores circuit state in NeonDB so all API instances share the same view.
|
|
5
|
+
*
|
|
6
|
+
* Architecture:
|
|
7
|
+
* - Local in-memory cache (5s TTL) — fast read path, no DB hit per request
|
|
8
|
+
* - DB write only on state transitions (open/closed/half-open changes)
|
|
9
|
+
* - Fail-open on DB errors: if we can't read state, we let the call through
|
|
10
|
+
* rather than blocking all traffic because the circuit state store is down
|
|
11
|
+
*/
|
|
12
|
+
export interface DbCircuitBreakerConfig {
|
|
13
|
+
/** Number of consecutive failures before tripping. Default: 5 */
|
|
14
|
+
failureThreshold: number;
|
|
15
|
+
/** Consecutive successes in half-open to close the circuit. Default: 2 */
|
|
16
|
+
successThreshold: number;
|
|
17
|
+
/** Milliseconds to wait in open state before probing. Default: 30_000 */
|
|
18
|
+
resetTimeout: number;
|
|
19
|
+
/** Local cache TTL in milliseconds. Default: 5_000 */
|
|
20
|
+
cacheTtlMs: number;
|
|
21
|
+
}
|
|
22
|
+
export declare class DbCircuitBreaker {
|
|
23
|
+
private readonly serviceName;
|
|
24
|
+
private readonly config;
|
|
25
|
+
constructor(serviceName: string, config?: Partial<DbCircuitBreakerConfig>);
|
|
26
|
+
/**
|
|
27
|
+
* Returns true if the circuit is open (requests should be blocked).
|
|
28
|
+
* Automatically transitions open→half-open when resetTimeout elapses.
|
|
29
|
+
*/
|
|
30
|
+
isOpen(): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Record a successful call. Closes the circuit if enough successes in half-open.
|
|
33
|
+
*/
|
|
34
|
+
recordSuccess(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Record a failed call. Trips the circuit when the failure threshold is reached.
|
|
37
|
+
*/
|
|
38
|
+
recordFailure(): Promise<void>;
|
|
39
|
+
/** Force-reset state and clear local cache. Primarily for testing. */
|
|
40
|
+
reset(): Promise<void>;
|
|
41
|
+
/** Clear only the local cache (forces next read to hit DB). For testing. */
|
|
42
|
+
clearLocalCache(): void;
|
|
43
|
+
private readState;
|
|
44
|
+
private readFromDb;
|
|
45
|
+
private writeState;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=db-circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/stripe/db-circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAoBH,MAAM,WAAW,sBAAsB;IACrC,iEAAiE;IACjE,gBAAgB,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,gBAAgB,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;CACpB;AAYD,qBAAa,gBAAgB;IAIzB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAH9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;gBAG7B,WAAW,EAAE,MAAM,EACpC,MAAM,GAAE,OAAO,CAAC,sBAAsB,CAAM;IAK9C;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAsBhC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BpC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBpC,sEAAsE;IAChE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,4EAA4E;IAC5E,eAAe,IAAI,IAAI;YAMT,SAAS;YAQT,UAAU;YA+CV,UAAU;CA2CzB"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DB-Backed Circuit Breaker
|
|
3
|
+
*
|
|
4
|
+
* Stores circuit state in NeonDB so all API instances share the same view.
|
|
5
|
+
*
|
|
6
|
+
* Architecture:
|
|
7
|
+
* - Local in-memory cache (5s TTL) — fast read path, no DB hit per request
|
|
8
|
+
* - DB write only on state transitions (open/closed/half-open changes)
|
|
9
|
+
* - Fail-open on DB errors: if we can't read state, we let the call through
|
|
10
|
+
* rather than blocking all traffic because the circuit state store is down
|
|
11
|
+
*/
|
|
12
|
+
import { createLogger } from '@revealui/core/observability/logger';
|
|
13
|
+
import { getClient } from '@revealui/db';
|
|
14
|
+
import { circuitBreakerState } from '@revealui/db/schema';
|
|
15
|
+
import { eq } from 'drizzle-orm';
|
|
16
|
+
const logger = createLogger({ service: 'DbCircuitBreaker' });
|
|
17
|
+
const DEFAULT_CONFIG = {
|
|
18
|
+
failureThreshold: 5,
|
|
19
|
+
successThreshold: 2,
|
|
20
|
+
resetTimeout: 30_000,
|
|
21
|
+
cacheTtlMs: 5_000,
|
|
22
|
+
};
|
|
23
|
+
// Module-level cache shared across all instances in the same process
|
|
24
|
+
const localCache = new Map();
|
|
25
|
+
export class DbCircuitBreaker {
|
|
26
|
+
serviceName;
|
|
27
|
+
config;
|
|
28
|
+
constructor(serviceName, config = {}) {
|
|
29
|
+
this.serviceName = serviceName;
|
|
30
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Returns true if the circuit is open (requests should be blocked).
|
|
34
|
+
* Automatically transitions open→half-open when resetTimeout elapses.
|
|
35
|
+
*/
|
|
36
|
+
async isOpen() {
|
|
37
|
+
const s = await this.readState();
|
|
38
|
+
if (s.state === 'closed')
|
|
39
|
+
return false;
|
|
40
|
+
if (s.state === 'half-open')
|
|
41
|
+
return false;
|
|
42
|
+
// state === 'open'
|
|
43
|
+
if (Date.now() - s.stateChangedAt >= this.config.resetTimeout) {
|
|
44
|
+
// Probe window: transition to half-open and allow one request through
|
|
45
|
+
await this.writeState({
|
|
46
|
+
...s,
|
|
47
|
+
state: 'half-open',
|
|
48
|
+
successCount: 0,
|
|
49
|
+
stateChangedAt: Date.now(),
|
|
50
|
+
});
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Record a successful call. Closes the circuit if enough successes in half-open.
|
|
57
|
+
*/
|
|
58
|
+
async recordSuccess() {
|
|
59
|
+
const s = await this.readState();
|
|
60
|
+
if (s.state === 'half-open') {
|
|
61
|
+
const newSuccesses = s.successCount + 1;
|
|
62
|
+
if (newSuccesses >= this.config.successThreshold) {
|
|
63
|
+
await this.writeState({
|
|
64
|
+
...s,
|
|
65
|
+
state: 'closed',
|
|
66
|
+
failureCount: 0,
|
|
67
|
+
successCount: 0,
|
|
68
|
+
stateChangedAt: Date.now(),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
// Update local cache only — no DB write until threshold is reached
|
|
73
|
+
localCache.set(this.serviceName, {
|
|
74
|
+
...s,
|
|
75
|
+
successCount: newSuccesses,
|
|
76
|
+
cachedAt: s.cachedAt,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (s.state === 'closed' && s.failureCount > 0) {
|
|
82
|
+
// Reset sub-threshold failure counter locally (no DB write needed)
|
|
83
|
+
localCache.set(this.serviceName, { ...s, failureCount: 0 });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Record a failed call. Trips the circuit when the failure threshold is reached.
|
|
88
|
+
*/
|
|
89
|
+
async recordFailure() {
|
|
90
|
+
const s = await this.readState();
|
|
91
|
+
const newFailures = s.failureCount + 1;
|
|
92
|
+
if (s.state === 'half-open' || newFailures >= this.config.failureThreshold) {
|
|
93
|
+
// Trip or re-trip the circuit
|
|
94
|
+
await this.writeState({
|
|
95
|
+
...s,
|
|
96
|
+
state: 'open',
|
|
97
|
+
failureCount: newFailures,
|
|
98
|
+
successCount: 0,
|
|
99
|
+
lastFailureAt: Date.now(),
|
|
100
|
+
stateChangedAt: Date.now(),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Sub-threshold: update local counter without hitting DB
|
|
105
|
+
localCache.set(this.serviceName, {
|
|
106
|
+
...s,
|
|
107
|
+
failureCount: newFailures,
|
|
108
|
+
lastFailureAt: Date.now(),
|
|
109
|
+
cachedAt: s.cachedAt,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** Force-reset state and clear local cache. Primarily for testing. */
|
|
114
|
+
async reset() {
|
|
115
|
+
const fresh = {
|
|
116
|
+
state: 'closed',
|
|
117
|
+
failureCount: 0,
|
|
118
|
+
successCount: 0,
|
|
119
|
+
lastFailureAt: 0,
|
|
120
|
+
stateChangedAt: Date.now(),
|
|
121
|
+
cachedAt: Date.now(),
|
|
122
|
+
};
|
|
123
|
+
await this.writeState(fresh);
|
|
124
|
+
}
|
|
125
|
+
/** Clear only the local cache (forces next read to hit DB). For testing. */
|
|
126
|
+
clearLocalCache() {
|
|
127
|
+
localCache.delete(this.serviceName);
|
|
128
|
+
}
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
async readState() {
|
|
131
|
+
const cached = localCache.get(this.serviceName);
|
|
132
|
+
if (cached && Date.now() - cached.cachedAt < this.config.cacheTtlMs) {
|
|
133
|
+
return cached;
|
|
134
|
+
}
|
|
135
|
+
return this.readFromDb();
|
|
136
|
+
}
|
|
137
|
+
async readFromDb() {
|
|
138
|
+
try {
|
|
139
|
+
const db = getClient();
|
|
140
|
+
const [row] = await db
|
|
141
|
+
.select()
|
|
142
|
+
.from(circuitBreakerState)
|
|
143
|
+
.where(eq(circuitBreakerState.serviceName, this.serviceName));
|
|
144
|
+
const state = row
|
|
145
|
+
? {
|
|
146
|
+
state: row.state,
|
|
147
|
+
failureCount: row.failureCount,
|
|
148
|
+
successCount: row.successCount,
|
|
149
|
+
lastFailureAt: row.lastFailureAt?.getTime() ?? 0,
|
|
150
|
+
stateChangedAt: row.stateChangedAt.getTime(),
|
|
151
|
+
cachedAt: Date.now(),
|
|
152
|
+
}
|
|
153
|
+
: {
|
|
154
|
+
state: 'closed',
|
|
155
|
+
failureCount: 0,
|
|
156
|
+
successCount: 0,
|
|
157
|
+
lastFailureAt: 0,
|
|
158
|
+
stateChangedAt: Date.now(),
|
|
159
|
+
cachedAt: Date.now(),
|
|
160
|
+
};
|
|
161
|
+
localCache.set(this.serviceName, state);
|
|
162
|
+
return state;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Fail-open: if DB is unreachable, default to closed so Stripe calls proceed.
|
|
166
|
+
// We log a warning but don't block traffic over a missing circuit state row.
|
|
167
|
+
logger.warn(`DbCircuitBreaker: failed to read state for '${this.serviceName}', defaulting to closed`);
|
|
168
|
+
const fallback = {
|
|
169
|
+
state: 'closed',
|
|
170
|
+
failureCount: 0,
|
|
171
|
+
successCount: 0,
|
|
172
|
+
lastFailureAt: 0,
|
|
173
|
+
stateChangedAt: Date.now(),
|
|
174
|
+
cachedAt: Date.now(),
|
|
175
|
+
};
|
|
176
|
+
localCache.set(this.serviceName, fallback);
|
|
177
|
+
return fallback;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async writeState(s) {
|
|
181
|
+
const now = new Date();
|
|
182
|
+
// Optimistically update local cache first so subsequent in-process
|
|
183
|
+
// calls see the new state without waiting for the DB round-trip to complete.
|
|
184
|
+
localCache.set(this.serviceName, { ...s, cachedAt: Date.now() });
|
|
185
|
+
try {
|
|
186
|
+
const db = getClient();
|
|
187
|
+
await db
|
|
188
|
+
.insert(circuitBreakerState)
|
|
189
|
+
.values({
|
|
190
|
+
serviceName: this.serviceName,
|
|
191
|
+
state: s.state,
|
|
192
|
+
failureCount: s.failureCount,
|
|
193
|
+
successCount: s.successCount,
|
|
194
|
+
lastFailureAt: s.lastFailureAt ? new Date(s.lastFailureAt) : null,
|
|
195
|
+
stateChangedAt: new Date(s.stateChangedAt),
|
|
196
|
+
updatedAt: now,
|
|
197
|
+
})
|
|
198
|
+
.onConflictDoUpdate({
|
|
199
|
+
target: circuitBreakerState.serviceName,
|
|
200
|
+
set: {
|
|
201
|
+
state: s.state,
|
|
202
|
+
failureCount: s.failureCount,
|
|
203
|
+
successCount: s.successCount,
|
|
204
|
+
lastFailureAt: s.lastFailureAt ? new Date(s.lastFailureAt) : null,
|
|
205
|
+
stateChangedAt: new Date(s.stateChangedAt),
|
|
206
|
+
updatedAt: now,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
// Non-fatal: local cache has the new state; DB will catch up on next write.
|
|
212
|
+
logger.warn(`DbCircuitBreaker: failed to persist state for '${this.serviceName}'`, {
|
|
213
|
+
error: err instanceof Error ? err.message : String(err),
|
|
214
|
+
newState: s.state,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
logger.info(`DbCircuitBreaker state transition: '${this.serviceName}' → ${s.state}`, {
|
|
218
|
+
failureCount: s.failureCount,
|
|
219
|
+
successCount: s.successCount,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=db-circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-circuit-breaker.js","sourceRoot":"","sources":["../../src/stripe/db-circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stripe/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stripe/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA"}
|