@carrierllc/mcp 0.2.1 → 0.2.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.d.ts +0 -13
- package/dist/index.js +9374 -77
- package/dist/index.js.map +1 -1
- package/package.json +11 -10
- package/dist/apps/app-state.d.ts +0 -19
- package/dist/apps/app-state.d.ts.map +0 -1
- package/dist/apps/app-state.js +0 -28
- package/dist/apps/app-state.js.map +0 -1
- package/dist/apps/balance-topup.d.ts +0 -19
- package/dist/apps/balance-topup.d.ts.map +0 -1
- package/dist/apps/balance-topup.js +0 -121
- package/dist/apps/balance-topup.js.map +0 -1
- package/dist/apps/fleet-health-app.d.ts +0 -29
- package/dist/apps/fleet-health-app.d.ts.map +0 -1
- package/dist/apps/fleet-health-app.js +0 -152
- package/dist/apps/fleet-health-app.js.map +0 -1
- package/dist/apps/index.d.ts +0 -37
- package/dist/apps/index.d.ts.map +0 -1
- package/dist/apps/index.js +0 -42
- package/dist/apps/index.js.map +0 -1
- package/dist/apps/provisioning-wizard.d.ts +0 -11
- package/dist/apps/provisioning-wizard.d.ts.map +0 -1
- package/dist/apps/provisioning-wizard.js +0 -345
- package/dist/apps/provisioning-wizard.js.map +0 -1
- package/dist/audit.d.ts +0 -31
- package/dist/audit.d.ts.map +0 -1
- package/dist/audit.js +0 -42
- package/dist/audit.js.map +0 -1
- package/dist/billing-thresholds.d.ts +0 -71
- package/dist/billing-thresholds.d.ts.map +0 -1
- package/dist/billing-thresholds.js +0 -238
- package/dist/billing-thresholds.js.map +0 -1
- package/dist/billing.d.ts +0 -95
- package/dist/billing.d.ts.map +0 -1
- package/dist/billing.js +0 -239
- package/dist/billing.js.map +0 -1
- package/dist/clerk.d.ts +0 -125
- package/dist/clerk.d.ts.map +0 -1
- package/dist/clerk.js +0 -389
- package/dist/clerk.js.map +0 -1
- package/dist/client.d.ts +0 -35
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -85
- package/dist/client.js.map +0 -1
- package/dist/credits.d.ts +0 -165
- package/dist/credits.d.ts.map +0 -1
- package/dist/credits.js +0 -483
- package/dist/credits.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/intelligence.d.ts +0 -4
- package/dist/intelligence.d.ts.map +0 -1
- package/dist/intelligence.js +0 -977
- package/dist/intelligence.js.map +0 -1
- package/dist/list-recent-ocs-events.d.ts +0 -48
- package/dist/list-recent-ocs-events.d.ts.map +0 -1
- package/dist/list-recent-ocs-events.js +0 -160
- package/dist/list-recent-ocs-events.js.map +0 -1
- package/dist/manus-client.d.ts +0 -429
- package/dist/manus-client.d.ts.map +0 -1
- package/dist/manus-client.js +0 -401
- package/dist/manus-client.js.map +0 -1
- package/dist/manus-common.d.ts +0 -5
- package/dist/manus-common.d.ts.map +0 -1
- package/dist/manus-common.js +0 -5
- package/dist/manus-common.js.map +0 -1
- package/dist/manus-schedule.d.ts +0 -74
- package/dist/manus-schedule.d.ts.map +0 -1
- package/dist/manus-schedule.js +0 -213
- package/dist/manus-schedule.js.map +0 -1
- package/dist/manus-usage.d.ts +0 -32
- package/dist/manus-usage.d.ts.map +0 -1
- package/dist/manus-usage.js +0 -120
- package/dist/manus-usage.js.map +0 -1
- package/dist/manus-webhook.d.ts +0 -60
- package/dist/manus-webhook.d.ts.map +0 -1
- package/dist/manus-webhook.js +0 -361
- package/dist/manus-webhook.js.map +0 -1
- package/dist/pricing-tools.d.ts +0 -26
- package/dist/pricing-tools.d.ts.map +0 -1
- package/dist/pricing-tools.js +0 -428
- package/dist/pricing-tools.js.map +0 -1
- package/dist/projects-tools.d.ts +0 -39
- package/dist/projects-tools.d.ts.map +0 -1
- package/dist/projects-tools.js +0 -545
- package/dist/projects-tools.js.map +0 -1
- package/dist/prompts.d.ts +0 -7
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js +0 -151
- package/dist/prompts.js.map +0 -1
- package/dist/stripe-connect-tools.d.ts +0 -33
- package/dist/stripe-connect-tools.d.ts.map +0 -1
- package/dist/stripe-connect-tools.js +0 -504
- package/dist/stripe-connect-tools.js.map +0 -1
- package/dist/timing-safe-equal.d.ts +0 -5
- package/dist/timing-safe-equal.d.ts.map +0 -1
- package/dist/timing-safe-equal.js +0 -13
- package/dist/timing-safe-equal.js.map +0 -1
- package/dist/tools-backlog.d.ts +0 -37
- package/dist/tools-backlog.d.ts.map +0 -1
- package/dist/tools-backlog.js +0 -410
- package/dist/tools-backlog.js.map +0 -1
- package/dist/tools-carrier-ask.d.ts +0 -138
- package/dist/tools-carrier-ask.d.ts.map +0 -1
- package/dist/tools-carrier-ask.js +0 -913
- package/dist/tools-carrier-ask.js.map +0 -1
- package/dist/tools-pricing.d.ts +0 -18
- package/dist/tools-pricing.d.ts.map +0 -1
- package/dist/tools-pricing.js +0 -172
- package/dist/tools-pricing.js.map +0 -1
- package/dist/tools-ui-agent-ask.d.ts +0 -29
- package/dist/tools-ui-agent-ask.d.ts.map +0 -1
- package/dist/tools-ui-agent-ask.js +0 -272
- package/dist/tools-ui-agent-ask.js.map +0 -1
- package/dist/tools-ui-agent-schedule.d.ts +0 -29
- package/dist/tools-ui-agent-schedule.d.ts.map +0 -1
- package/dist/tools-ui-agent-schedule.js +0 -517
- package/dist/tools-ui-agent-schedule.js.map +0 -1
- package/dist/tools-ui-agent.d.ts +0 -40
- package/dist/tools-ui-agent.d.ts.map +0 -1
- package/dist/tools-ui-agent.js +0 -508
- package/dist/tools-ui-agent.js.map +0 -1
- package/dist/tools.d.ts +0 -60
- package/dist/tools.d.ts.map +0 -1
- package/dist/tools.js +0 -1384
- package/dist/tools.js.map +0 -1
- package/dist/types.d.ts +0 -298
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -5
- package/dist/types.js.map +0 -1
package/dist/client.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,MAAM,OAAO,WAAY,SAAQ,KAAK;IAElB;IAEA;IAHlB,YACkB,IAAY,EAC5B,OAAe,EACC,MAAc;QAE9B,KAAK,CAAC,IAAI,MAAM,eAAe,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QAJnC,SAAI,GAAJ,IAAI,CAAQ;QAEZ,WAAM,GAAN,MAAM,CAAQ;QAG9B,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,OAAO,SAAS;IACH,OAAO,CAAS;IAChB,KAAK,CAAS;IAE/B,YAAY,OAAe,EAAE,KAAa;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,MAAc,EACd,SAAoD,EAAE;QAEtD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,aAAa,IAAI,CAAC,KAAK,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAElD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI;SACL,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC,CAAC;QACpF,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAmB,CAAC;QAElD,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,MAAM,CAAC,CAAC;QAC9F,CAAC;QAED,kFAAkF;QAClF,IAAI,MAAM,KAAK,mBAAmB,IAAI,IAAI,CAAC,gBAAgB,CAAC,KAAK,SAAS,EAAE,CAAC;YAC3E,OAAO,IAAI,CAAC,gBAAgB,CAAM,CAAC;QACrC,CAAC;QAED,yFAAyF;QACzF,IAAI,MAAM,KAAK,+BAA+B,EAAE,CAAC;YAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAkB,CAAC;YAC/C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,IAAI,IAAI,CAAC,oBAAoB,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC7C,OAAO,IAAI,CAAC,oBAAoB,CAAM,CAAC;YACzC,CAAC;QACH,CAAC;QAED,qCAAqC;QACrC,OAAQ,IAAI,CAAC,MAAM,CAAO,IAAK,IAAqB,CAAC;IACvD,CAAC;CACF;AAED,IAAI,OAAO,GAAqB,IAAI,CAAC;AAErC,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;QAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAC9C,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,2EAA2E;gBACzE,0CAA0C,CAC7C,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,IAAI,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAkB,iBAAiB,EAAE,EAAE,CAAC,CAAC;IAC5E,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC;IACpB,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
package/dist/credits.d.ts
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Carrier MCP — Credits System v2.0 (Pricing Evolution)
|
|
3
|
-
*
|
|
4
|
-
* Implements credit-based billing inspired by Stripe's Pricing Model Evolution Guide:
|
|
5
|
-
* - Monthly credit allotments per tier (Lovable pattern)
|
|
6
|
-
* - Auto-billed overages when base credits exhausted (Warp pattern)
|
|
7
|
-
* - Credit rollovers for irregular usage (Lovable pattern)
|
|
8
|
-
* - Daily free credits for acquisition (Browserbase/Lovable pattern)
|
|
9
|
-
* - Billing thresholds to prevent bill shock (Hex pattern)
|
|
10
|
-
* - Volume discounts for high-usage customers
|
|
11
|
-
*
|
|
12
|
-
* Storage layout (CARRIER_USERS KV):
|
|
13
|
-
* key: credits:<sub>:<yyyymm> value: CreditLedger
|
|
14
|
-
* key: credits:daily:<sub>:<yyyymmdd> value: DailyFreeCredits
|
|
15
|
-
* key: credits:config:<sub> value: CreditConfig (overrides, thresholds)
|
|
16
|
-
* key: credits:rollover:<sub> value: RolloverBalance
|
|
17
|
-
*
|
|
18
|
-
* Credit economy:
|
|
19
|
-
* 1 tool call = 1 credit (read scope)
|
|
20
|
-
* 1 tool call = 2 credits (write scope)
|
|
21
|
-
* 1 tool call = 5 credits (admin scope)
|
|
22
|
-
* Intelligence composites = 3 credits each
|
|
23
|
-
*
|
|
24
|
-
* Stripe integration:
|
|
25
|
-
* - Overages reported via Stripe Metered Billing (usage_records API)
|
|
26
|
-
* - Billing thresholds trigger Stripe invoice finalization
|
|
27
|
-
* - Credit grants created via Stripe Customer Balance Transactions
|
|
28
|
-
*/
|
|
29
|
-
import type { Env } from "./types.js";
|
|
30
|
-
import type { Tier, ScopeToken } from "./billing.js";
|
|
31
|
-
/** Monthly credit allotments per tier */
|
|
32
|
-
export declare const TIER_CREDIT_ALLOTMENTS: Record<Tier, number>;
|
|
33
|
-
/** Daily free credits for all users (acquisition driver) */
|
|
34
|
-
export declare const DAILY_FREE_CREDITS = 50;
|
|
35
|
-
/** Maximum rollover credits (cap at 2x monthly allotment) */
|
|
36
|
-
export declare const ROLLOVER_CAP_MULTIPLIER = 2;
|
|
37
|
-
/** Credit cost per scope */
|
|
38
|
-
export declare const SCOPE_CREDIT_COSTS: Record<ScopeToken | "intelligence", number>;
|
|
39
|
-
/** Volume discount tiers (cumulative monthly usage) */
|
|
40
|
-
export declare const VOLUME_DISCOUNTS: VolumeDiscountTier[];
|
|
41
|
-
/** Default billing threshold (in cents) — triggers invoice at this spend level */
|
|
42
|
-
export declare const DEFAULT_BILLING_THRESHOLD_CENTS = 10000;
|
|
43
|
-
/** Overage price per credit (in cents) — Pro tier */
|
|
44
|
-
export declare const OVERAGE_PRICE_PER_CREDIT_CENTS = 0.1;
|
|
45
|
-
export interface VolumeDiscountTier {
|
|
46
|
-
threshold: number;
|
|
47
|
-
discountPct: number;
|
|
48
|
-
}
|
|
49
|
-
export interface CreditLedger {
|
|
50
|
-
/** Monthly allotment (base + rollover) */
|
|
51
|
-
allotment: number;
|
|
52
|
-
/** Credits consumed this period */
|
|
53
|
-
consumed: number;
|
|
54
|
-
/** Credits from rollovers applied this month */
|
|
55
|
-
rollover_applied: number;
|
|
56
|
-
/** Overage credits consumed beyond allotment */
|
|
57
|
-
overage: number;
|
|
58
|
-
/** Overage amount billed (cents) */
|
|
59
|
-
overage_billed_cents: number;
|
|
60
|
-
/** Whether billing threshold was hit this period */
|
|
61
|
-
threshold_triggered: boolean;
|
|
62
|
-
/** ISO 8601 last updated */
|
|
63
|
-
updated_at: string;
|
|
64
|
-
}
|
|
65
|
-
export interface DailyFreeCredits {
|
|
66
|
-
/** Credits granted today */
|
|
67
|
-
granted: number;
|
|
68
|
-
/** Credits consumed from daily free pool */
|
|
69
|
-
consumed: number;
|
|
70
|
-
/** ISO date (YYYY-MM-DD) */
|
|
71
|
-
date: string;
|
|
72
|
-
}
|
|
73
|
-
export interface CreditConfig {
|
|
74
|
-
/** Custom billing threshold override (cents) */
|
|
75
|
-
billing_threshold_cents: number;
|
|
76
|
-
/** Whether overages are enabled (Pro+) */
|
|
77
|
-
overages_enabled: boolean;
|
|
78
|
-
/** Whether rollovers are enabled (Pro+) */
|
|
79
|
-
rollovers_enabled: boolean;
|
|
80
|
-
/** Custom overage rate override (cents per credit) */
|
|
81
|
-
overage_rate_cents: number;
|
|
82
|
-
/** Volume discount tier override */
|
|
83
|
-
volume_discount_pct: number;
|
|
84
|
-
/** Notification preferences */
|
|
85
|
-
notify_at_pct: number[];
|
|
86
|
-
updated_at: string;
|
|
87
|
-
}
|
|
88
|
-
export interface RolloverBalance {
|
|
89
|
-
/** Unused credits from previous month eligible for rollover */
|
|
90
|
-
credits: number;
|
|
91
|
-
/** Source month (YYYYMM) */
|
|
92
|
-
source_month: string;
|
|
93
|
-
/** Expiry — rollovers expire after 1 month */
|
|
94
|
-
expires_at: string;
|
|
95
|
-
}
|
|
96
|
-
export interface CreditCheckResult {
|
|
97
|
-
allowed: boolean;
|
|
98
|
-
credits_remaining: number;
|
|
99
|
-
daily_free_remaining: number;
|
|
100
|
-
overage_active: boolean;
|
|
101
|
-
overage_amount_cents: number;
|
|
102
|
-
threshold_pct: number;
|
|
103
|
-
tier: Tier;
|
|
104
|
-
volume_discount_pct: number;
|
|
105
|
-
reset_at: string;
|
|
106
|
-
}
|
|
107
|
-
export interface CreditDeductionResult {
|
|
108
|
-
success: boolean;
|
|
109
|
-
credits_deducted: number;
|
|
110
|
-
source: "allotment" | "daily_free" | "overage";
|
|
111
|
-
new_balance: number;
|
|
112
|
-
overage_triggered: boolean;
|
|
113
|
-
threshold_triggered: boolean;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Check if a user has sufficient credits for a tool call.
|
|
117
|
-
* Order of credit consumption:
|
|
118
|
-
* 1. Daily free credits (if available)
|
|
119
|
-
* 2. Monthly allotment (base + rollover)
|
|
120
|
-
* 3. Overage (if enabled for tier)
|
|
121
|
-
*/
|
|
122
|
-
export declare function checkCredits(env: Env, sub: string, tier: Tier, scope: ScopeToken | "intelligence"): Promise<CreditCheckResult>;
|
|
123
|
-
/**
|
|
124
|
-
* Deduct credits for a tool call. Fire-and-forget in the hot path.
|
|
125
|
-
*
|
|
126
|
-
* Consumption order:
|
|
127
|
-
* 1. Daily free credits first (lowest cost to user)
|
|
128
|
-
* 2. Monthly allotment
|
|
129
|
-
* 3. Overage (auto-billed, Pro+ only)
|
|
130
|
-
*/
|
|
131
|
-
export declare function deductCredits(env: Env, sub: string, tier: Tier, scope: ScopeToken | "intelligence"): void;
|
|
132
|
-
/**
|
|
133
|
-
* Calculate and apply rollover credits from previous month.
|
|
134
|
-
* Called by the monthly cron job (scheduled handler).
|
|
135
|
-
*
|
|
136
|
-
* Rules:
|
|
137
|
-
* - Only Pro+ tiers get rollovers
|
|
138
|
-
* - Rollover = min(unused credits, allotment * ROLLOVER_CAP_MULTIPLIER)
|
|
139
|
-
* - Rollovers expire after 1 month (use-it-or-lose-it next cycle)
|
|
140
|
-
*/
|
|
141
|
-
export declare function applyMonthlyRollover(env: Env, sub: string, tier: Tier): Promise<RolloverBalance | null>;
|
|
142
|
-
/**
|
|
143
|
-
* Resolve the applicable volume discount percentage based on cumulative usage.
|
|
144
|
-
*/
|
|
145
|
-
export declare function resolveVolumeDiscount(consumed: number): number;
|
|
146
|
-
/**
|
|
147
|
-
* Update a user's credit configuration (admin or self-service).
|
|
148
|
-
*/
|
|
149
|
-
export declare function updateCreditConfig(env: Env, sub: string, updates: Partial<CreditConfig>): Promise<CreditConfig>;
|
|
150
|
-
/**
|
|
151
|
-
* Get credit usage summary for a user (used by billing UI).
|
|
152
|
-
*/
|
|
153
|
-
export declare function getCreditSummary(env: Env, sub: string, tier: Tier): Promise<{
|
|
154
|
-
ledger: CreditLedger;
|
|
155
|
-
daily_free: DailyFreeCredits;
|
|
156
|
-
config: CreditConfig;
|
|
157
|
-
rollover: RolloverBalance | null;
|
|
158
|
-
volume_discount_pct: number;
|
|
159
|
-
}>;
|
|
160
|
-
/**
|
|
161
|
-
* Issue a credit grant to a customer via Stripe Customer Balance Transactions.
|
|
162
|
-
* Used for promotional credits, referral bonuses, and compensation.
|
|
163
|
-
*/
|
|
164
|
-
export declare function issueStripeCredits(env: Env, sub: string, amountCents: number, description: string): Promise<boolean>;
|
|
165
|
-
//# sourceMappingURL=credits.d.ts.map
|
package/dist/credits.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credits.d.ts","sourceRoot":"","sources":["../src/credits.ts"],"names":[],"mappings":"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"}
|
package/dist/credits.js
DELETED
|
@@ -1,483 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Carrier MCP — Credits System v2.0 (Pricing Evolution)
|
|
3
|
-
*
|
|
4
|
-
* Implements credit-based billing inspired by Stripe's Pricing Model Evolution Guide:
|
|
5
|
-
* - Monthly credit allotments per tier (Lovable pattern)
|
|
6
|
-
* - Auto-billed overages when base credits exhausted (Warp pattern)
|
|
7
|
-
* - Credit rollovers for irregular usage (Lovable pattern)
|
|
8
|
-
* - Daily free credits for acquisition (Browserbase/Lovable pattern)
|
|
9
|
-
* - Billing thresholds to prevent bill shock (Hex pattern)
|
|
10
|
-
* - Volume discounts for high-usage customers
|
|
11
|
-
*
|
|
12
|
-
* Storage layout (CARRIER_USERS KV):
|
|
13
|
-
* key: credits:<sub>:<yyyymm> value: CreditLedger
|
|
14
|
-
* key: credits:daily:<sub>:<yyyymmdd> value: DailyFreeCredits
|
|
15
|
-
* key: credits:config:<sub> value: CreditConfig (overrides, thresholds)
|
|
16
|
-
* key: credits:rollover:<sub> value: RolloverBalance
|
|
17
|
-
*
|
|
18
|
-
* Credit economy:
|
|
19
|
-
* 1 tool call = 1 credit (read scope)
|
|
20
|
-
* 1 tool call = 2 credits (write scope)
|
|
21
|
-
* 1 tool call = 5 credits (admin scope)
|
|
22
|
-
* Intelligence composites = 3 credits each
|
|
23
|
-
*
|
|
24
|
-
* Stripe integration:
|
|
25
|
-
* - Overages reported via Stripe Metered Billing (usage_records API)
|
|
26
|
-
* - Billing thresholds trigger Stripe invoice finalization
|
|
27
|
-
* - Credit grants created via Stripe Customer Balance Transactions
|
|
28
|
-
*/
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
// Credit Constants
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
/** Monthly credit allotments per tier */
|
|
33
|
-
export const TIER_CREDIT_ALLOTMENTS = {
|
|
34
|
-
free: 5_000,
|
|
35
|
-
pro: 100_000,
|
|
36
|
-
enterprise: Infinity,
|
|
37
|
-
};
|
|
38
|
-
/** Daily free credits for all users (acquisition driver) */
|
|
39
|
-
export const DAILY_FREE_CREDITS = 50;
|
|
40
|
-
/** Maximum rollover credits (cap at 2x monthly allotment) */
|
|
41
|
-
export const ROLLOVER_CAP_MULTIPLIER = 2;
|
|
42
|
-
/** Credit cost per scope */
|
|
43
|
-
export const SCOPE_CREDIT_COSTS = {
|
|
44
|
-
read: 1,
|
|
45
|
-
write: 2,
|
|
46
|
-
admin: 5,
|
|
47
|
-
intelligence: 3,
|
|
48
|
-
};
|
|
49
|
-
/** Volume discount tiers (cumulative monthly usage) */
|
|
50
|
-
export const VOLUME_DISCOUNTS = [
|
|
51
|
-
{ threshold: 50_000, discountPct: 0 },
|
|
52
|
-
{ threshold: 100_000, discountPct: 10 },
|
|
53
|
-
{ threshold: 250_000, discountPct: 15 },
|
|
54
|
-
{ threshold: 500_000, discountPct: 20 },
|
|
55
|
-
{ threshold: 1_000_000, discountPct: 25 },
|
|
56
|
-
];
|
|
57
|
-
/** Default billing threshold (in cents) — triggers invoice at this spend level */
|
|
58
|
-
export const DEFAULT_BILLING_THRESHOLD_CENTS = 10_000; // $100
|
|
59
|
-
/** Overage price per credit (in cents) — Pro tier */
|
|
60
|
-
export const OVERAGE_PRICE_PER_CREDIT_CENTS = 0.1; // $0.001 per credit
|
|
61
|
-
// ---------------------------------------------------------------------------
|
|
62
|
-
// Credit Check
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
|
-
/**
|
|
65
|
-
* Check if a user has sufficient credits for a tool call.
|
|
66
|
-
* Order of credit consumption:
|
|
67
|
-
* 1. Daily free credits (if available)
|
|
68
|
-
* 2. Monthly allotment (base + rollover)
|
|
69
|
-
* 3. Overage (if enabled for tier)
|
|
70
|
-
*/
|
|
71
|
-
export async function checkCredits(env, sub, tier, scope) {
|
|
72
|
-
const cost = SCOPE_CREDIT_COSTS[scope];
|
|
73
|
-
const month = currentMonth();
|
|
74
|
-
const today = currentDay();
|
|
75
|
-
const resetAt = firstDayNextMonth();
|
|
76
|
-
// Enterprise always allowed
|
|
77
|
-
if (tier === "enterprise") {
|
|
78
|
-
return {
|
|
79
|
-
allowed: true,
|
|
80
|
-
credits_remaining: Infinity,
|
|
81
|
-
daily_free_remaining: Infinity,
|
|
82
|
-
overage_active: false,
|
|
83
|
-
overage_amount_cents: 0,
|
|
84
|
-
threshold_pct: 0,
|
|
85
|
-
tier,
|
|
86
|
-
volume_discount_pct: 0,
|
|
87
|
-
reset_at: resetAt,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
// Load ledger
|
|
91
|
-
const ledger = await getLedger(env, sub, month, tier);
|
|
92
|
-
const dailyFree = await getDailyFreeCredits(env, sub, today);
|
|
93
|
-
const config = await getCreditConfig(env, sub, tier);
|
|
94
|
-
// Calculate remaining
|
|
95
|
-
const monthlyRemaining = Math.max(0, ledger.allotment - ledger.consumed);
|
|
96
|
-
const dailyFreeRemaining = Math.max(0, dailyFree.granted - dailyFree.consumed);
|
|
97
|
-
const totalRemaining = dailyFreeRemaining + monthlyRemaining;
|
|
98
|
-
// Check if allowed
|
|
99
|
-
const overageEnabled = config.overages_enabled && tier !== "free";
|
|
100
|
-
const allowed = totalRemaining >= cost || overageEnabled;
|
|
101
|
-
// Volume discount
|
|
102
|
-
const volumeDiscount = resolveVolumeDiscount(ledger.consumed);
|
|
103
|
-
// Threshold percentage
|
|
104
|
-
const thresholdPct = config.billing_threshold_cents > 0
|
|
105
|
-
? Math.round((ledger.overage_billed_cents / config.billing_threshold_cents) * 100)
|
|
106
|
-
: 0;
|
|
107
|
-
return {
|
|
108
|
-
allowed,
|
|
109
|
-
credits_remaining: monthlyRemaining,
|
|
110
|
-
daily_free_remaining: dailyFreeRemaining,
|
|
111
|
-
overage_active: monthlyRemaining < cost && dailyFreeRemaining < cost && overageEnabled,
|
|
112
|
-
overage_amount_cents: ledger.overage_billed_cents,
|
|
113
|
-
threshold_pct: thresholdPct,
|
|
114
|
-
tier,
|
|
115
|
-
volume_discount_pct: volumeDiscount,
|
|
116
|
-
reset_at: resetAt,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
// ---------------------------------------------------------------------------
|
|
120
|
-
// Credit Deduction
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
/**
|
|
123
|
-
* Deduct credits for a tool call. Fire-and-forget in the hot path.
|
|
124
|
-
*
|
|
125
|
-
* Consumption order:
|
|
126
|
-
* 1. Daily free credits first (lowest cost to user)
|
|
127
|
-
* 2. Monthly allotment
|
|
128
|
-
* 3. Overage (auto-billed, Pro+ only)
|
|
129
|
-
*/
|
|
130
|
-
export function deductCredits(env, sub, tier, scope) {
|
|
131
|
-
void (async () => {
|
|
132
|
-
const cost = SCOPE_CREDIT_COSTS[scope];
|
|
133
|
-
const month = currentMonth();
|
|
134
|
-
const today = currentDay();
|
|
135
|
-
// Enterprise — no deduction needed
|
|
136
|
-
if (tier === "enterprise")
|
|
137
|
-
return;
|
|
138
|
-
// Try daily free credits first
|
|
139
|
-
const dailyFree = await getDailyFreeCredits(env, sub, today);
|
|
140
|
-
const dailyRemaining = dailyFree.granted - dailyFree.consumed;
|
|
141
|
-
if (dailyRemaining >= cost) {
|
|
142
|
-
// Deduct from daily free pool
|
|
143
|
-
await putDailyFreeCredits(env, sub, today, {
|
|
144
|
-
...dailyFree,
|
|
145
|
-
consumed: dailyFree.consumed + cost,
|
|
146
|
-
});
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
// Deduct from monthly allotment
|
|
150
|
-
const ledger = await getLedger(env, sub, month, tier);
|
|
151
|
-
const monthlyRemaining = ledger.allotment - ledger.consumed;
|
|
152
|
-
if (monthlyRemaining >= cost) {
|
|
153
|
-
await putLedger(env, sub, month, {
|
|
154
|
-
...ledger,
|
|
155
|
-
consumed: ledger.consumed + cost,
|
|
156
|
-
updated_at: new Date().toISOString(),
|
|
157
|
-
});
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
// Overage path (Pro+ only)
|
|
161
|
-
const config = await getCreditConfig(env, sub, tier);
|
|
162
|
-
if (!config.overages_enabled || tier === "free")
|
|
163
|
-
return;
|
|
164
|
-
const volumeDiscount = resolveVolumeDiscount(ledger.consumed);
|
|
165
|
-
const effectiveRate = OVERAGE_PRICE_PER_CREDIT_CENTS * (1 - volumeDiscount / 100);
|
|
166
|
-
const overageCostCents = Math.round(cost * effectiveRate * 100) / 100;
|
|
167
|
-
const updatedLedger = {
|
|
168
|
-
...ledger,
|
|
169
|
-
consumed: ledger.consumed + cost,
|
|
170
|
-
overage: ledger.overage + cost,
|
|
171
|
-
overage_billed_cents: ledger.overage_billed_cents + overageCostCents,
|
|
172
|
-
updated_at: new Date().toISOString(),
|
|
173
|
-
};
|
|
174
|
-
// Check billing threshold
|
|
175
|
-
if (!ledger.threshold_triggered &&
|
|
176
|
-
updatedLedger.overage_billed_cents >= config.billing_threshold_cents) {
|
|
177
|
-
updatedLedger.threshold_triggered = true;
|
|
178
|
-
// Trigger Stripe invoice finalization (async, non-blocking)
|
|
179
|
-
void triggerBillingThreshold(env, sub, updatedLedger.overage_billed_cents);
|
|
180
|
-
}
|
|
181
|
-
await putLedger(env, sub, month, updatedLedger);
|
|
182
|
-
// Push overage to Stripe at every 100-credit boundary
|
|
183
|
-
if (updatedLedger.overage % 100 === 0) {
|
|
184
|
-
void pushOverageToStripe(env, sub, 100, effectiveRate).catch(() => { });
|
|
185
|
-
}
|
|
186
|
-
})();
|
|
187
|
-
}
|
|
188
|
-
// ---------------------------------------------------------------------------
|
|
189
|
-
// Credit Grant (Rollover)
|
|
190
|
-
// ---------------------------------------------------------------------------
|
|
191
|
-
/**
|
|
192
|
-
* Calculate and apply rollover credits from previous month.
|
|
193
|
-
* Called by the monthly cron job (scheduled handler).
|
|
194
|
-
*
|
|
195
|
-
* Rules:
|
|
196
|
-
* - Only Pro+ tiers get rollovers
|
|
197
|
-
* - Rollover = min(unused credits, allotment * ROLLOVER_CAP_MULTIPLIER)
|
|
198
|
-
* - Rollovers expire after 1 month (use-it-or-lose-it next cycle)
|
|
199
|
-
*/
|
|
200
|
-
export async function applyMonthlyRollover(env, sub, tier) {
|
|
201
|
-
if (tier === "free")
|
|
202
|
-
return null;
|
|
203
|
-
const prevMonth = previousMonth();
|
|
204
|
-
const prevLedger = await getLedger(env, sub, prevMonth, tier);
|
|
205
|
-
const unused = Math.max(0, prevLedger.allotment - prevLedger.consumed);
|
|
206
|
-
if (unused === 0)
|
|
207
|
-
return null;
|
|
208
|
-
const maxRollover = TIER_CREDIT_ALLOTMENTS[tier] * ROLLOVER_CAP_MULTIPLIER;
|
|
209
|
-
const rolloverAmount = Math.min(unused, maxRollover);
|
|
210
|
-
const rollover = {
|
|
211
|
-
credits: rolloverAmount,
|
|
212
|
-
source_month: prevMonth,
|
|
213
|
-
expires_at: firstDayMonthAfterNext(),
|
|
214
|
-
};
|
|
215
|
-
await env.CARRIER_USERS.put(`credits:rollover:${sub}`, JSON.stringify(rollover), { expirationTtl: 62 * 24 * 60 * 60 });
|
|
216
|
-
// Apply to current month's ledger
|
|
217
|
-
const currentMo = currentMonth();
|
|
218
|
-
const currentLedger = await getLedger(env, sub, currentMo, tier);
|
|
219
|
-
await putLedger(env, sub, currentMo, {
|
|
220
|
-
...currentLedger,
|
|
221
|
-
allotment: currentLedger.allotment + rolloverAmount,
|
|
222
|
-
rollover_applied: rolloverAmount,
|
|
223
|
-
updated_at: new Date().toISOString(),
|
|
224
|
-
});
|
|
225
|
-
return rollover;
|
|
226
|
-
}
|
|
227
|
-
// ---------------------------------------------------------------------------
|
|
228
|
-
// Volume Discounts
|
|
229
|
-
// ---------------------------------------------------------------------------
|
|
230
|
-
/**
|
|
231
|
-
* Resolve the applicable volume discount percentage based on cumulative usage.
|
|
232
|
-
*/
|
|
233
|
-
export function resolveVolumeDiscount(consumed) {
|
|
234
|
-
let discount = 0;
|
|
235
|
-
for (const tier of VOLUME_DISCOUNTS) {
|
|
236
|
-
if (consumed >= tier.threshold) {
|
|
237
|
-
discount = tier.discountPct;
|
|
238
|
-
}
|
|
239
|
-
else {
|
|
240
|
-
break;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return discount;
|
|
244
|
-
}
|
|
245
|
-
// ---------------------------------------------------------------------------
|
|
246
|
-
// Billing Threshold
|
|
247
|
-
// ---------------------------------------------------------------------------
|
|
248
|
-
/**
|
|
249
|
-
* Trigger billing threshold — creates a Stripe invoice for accumulated overages.
|
|
250
|
-
* Prevents bill shock by invoicing incrementally (Hex pattern).
|
|
251
|
-
*/
|
|
252
|
-
async function triggerBillingThreshold(env, sub, amountCents) {
|
|
253
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
254
|
-
if (!stripeKey)
|
|
255
|
-
return;
|
|
256
|
-
const customerId = await env.CARRIER_USERS.get(`stripe_customer_id:${sub}`);
|
|
257
|
-
if (!customerId)
|
|
258
|
-
return;
|
|
259
|
-
// Create an invoice item for the threshold amount
|
|
260
|
-
const body = new URLSearchParams({
|
|
261
|
-
customer: customerId,
|
|
262
|
-
amount: String(Math.round(amountCents)),
|
|
263
|
-
currency: "usd",
|
|
264
|
-
description: `Carrier MCP overage credits (threshold reached)`,
|
|
265
|
-
});
|
|
266
|
-
const resp = await fetch("https://api.stripe.com/v1/invoiceitems", {
|
|
267
|
-
method: "POST",
|
|
268
|
-
headers: {
|
|
269
|
-
Authorization: `Bearer ${stripeKey}`,
|
|
270
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
271
|
-
},
|
|
272
|
-
body: body.toString(),
|
|
273
|
-
});
|
|
274
|
-
if (!resp.ok)
|
|
275
|
-
return;
|
|
276
|
-
// Create and finalize the invoice
|
|
277
|
-
const invoiceBody = new URLSearchParams({
|
|
278
|
-
customer: customerId,
|
|
279
|
-
auto_advance: "true", // Auto-finalize and attempt payment
|
|
280
|
-
"collection_method": "charge_automatically",
|
|
281
|
-
description: "Carrier MCP — Overage billing threshold invoice",
|
|
282
|
-
});
|
|
283
|
-
await fetch("https://api.stripe.com/v1/invoices", {
|
|
284
|
-
method: "POST",
|
|
285
|
-
headers: {
|
|
286
|
-
Authorization: `Bearer ${stripeKey}`,
|
|
287
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
288
|
-
},
|
|
289
|
-
body: invoiceBody.toString(),
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Push overage usage records to Stripe Metered Billing.
|
|
294
|
-
*/
|
|
295
|
-
async function pushOverageToStripe(env, sub, quantity, ratePerCredit) {
|
|
296
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
297
|
-
if (!stripeKey)
|
|
298
|
-
return;
|
|
299
|
-
const subItemId = await env.CARRIER_USERS.get(`stripe_sub_item_id:${sub}`);
|
|
300
|
-
if (!subItemId)
|
|
301
|
-
return;
|
|
302
|
-
const body = new URLSearchParams({
|
|
303
|
-
quantity: String(quantity),
|
|
304
|
-
timestamp: String(Math.floor(Date.now() / 1000)),
|
|
305
|
-
action: "increment",
|
|
306
|
-
});
|
|
307
|
-
await fetch(`https://api.stripe.com/v1/subscription_items/${subItemId}/usage_records`, {
|
|
308
|
-
method: "POST",
|
|
309
|
-
headers: {
|
|
310
|
-
Authorization: `Bearer ${stripeKey}`,
|
|
311
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
312
|
-
},
|
|
313
|
-
body: body.toString(),
|
|
314
|
-
});
|
|
315
|
-
// Store rate for reconciliation
|
|
316
|
-
await env.CARRIER_USERS.put(`overage_rate:${sub}`, JSON.stringify({ rate_cents: ratePerCredit, updated_at: new Date().toISOString() }), { expirationTtl: 35 * 24 * 60 * 60 });
|
|
317
|
-
}
|
|
318
|
-
// ---------------------------------------------------------------------------
|
|
319
|
-
// KV Helpers
|
|
320
|
-
// ---------------------------------------------------------------------------
|
|
321
|
-
async function getLedger(env, sub, month, tier) {
|
|
322
|
-
const key = `credits:${sub}:${month}`;
|
|
323
|
-
const raw = await env.CARRIER_USERS.get(key, "json").catch(() => null);
|
|
324
|
-
if (raw && typeof raw === "object" && "allotment" in raw) {
|
|
325
|
-
return raw;
|
|
326
|
-
}
|
|
327
|
-
// Initialize new ledger for the month
|
|
328
|
-
const allotment = TIER_CREDIT_ALLOTMENTS[tier];
|
|
329
|
-
return {
|
|
330
|
-
allotment,
|
|
331
|
-
consumed: 0,
|
|
332
|
-
rollover_applied: 0,
|
|
333
|
-
overage: 0,
|
|
334
|
-
overage_billed_cents: 0,
|
|
335
|
-
threshold_triggered: false,
|
|
336
|
-
updated_at: new Date().toISOString(),
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
async function putLedger(env, sub, month, ledger) {
|
|
340
|
-
const key = `credits:${sub}:${month}`;
|
|
341
|
-
await env.CARRIER_USERS.put(key, JSON.stringify(ledger), {
|
|
342
|
-
expirationTtl: 65 * 24 * 60 * 60, // ~2 months
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
async function getDailyFreeCredits(env, sub, day) {
|
|
346
|
-
const key = `credits:daily:${sub}:${day}`;
|
|
347
|
-
const raw = await env.CARRIER_USERS.get(key, "json").catch(() => null);
|
|
348
|
-
if (raw && typeof raw === "object" && "granted" in raw) {
|
|
349
|
-
return raw;
|
|
350
|
-
}
|
|
351
|
-
// Initialize daily free credits
|
|
352
|
-
return {
|
|
353
|
-
granted: DAILY_FREE_CREDITS,
|
|
354
|
-
consumed: 0,
|
|
355
|
-
date: day,
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
async function putDailyFreeCredits(env, sub, day, credits) {
|
|
359
|
-
const key = `credits:daily:${sub}:${day}`;
|
|
360
|
-
await env.CARRIER_USERS.put(key, JSON.stringify(credits), {
|
|
361
|
-
expirationTtl: 2 * 24 * 60 * 60, // 2 days
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
async function getCreditConfig(env, sub, tier) {
|
|
365
|
-
const key = `credits:config:${sub}`;
|
|
366
|
-
const raw = await env.CARRIER_USERS.get(key, "json").catch(() => null);
|
|
367
|
-
if (raw && typeof raw === "object" && "billing_threshold_cents" in raw) {
|
|
368
|
-
return raw;
|
|
369
|
-
}
|
|
370
|
-
// Default config
|
|
371
|
-
return {
|
|
372
|
-
billing_threshold_cents: DEFAULT_BILLING_THRESHOLD_CENTS,
|
|
373
|
-
overages_enabled: tier !== "free",
|
|
374
|
-
rollovers_enabled: tier !== "free",
|
|
375
|
-
overage_rate_cents: OVERAGE_PRICE_PER_CREDIT_CENTS,
|
|
376
|
-
volume_discount_pct: 0,
|
|
377
|
-
notify_at_pct: [50, 80, 95, 100],
|
|
378
|
-
updated_at: new Date().toISOString(),
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
// ---------------------------------------------------------------------------
|
|
382
|
-
// Admin: Update Credit Config
|
|
383
|
-
// ---------------------------------------------------------------------------
|
|
384
|
-
/**
|
|
385
|
-
* Update a user's credit configuration (admin or self-service).
|
|
386
|
-
*/
|
|
387
|
-
export async function updateCreditConfig(env, sub, updates) {
|
|
388
|
-
const tier = await resolveUserTier(env, sub);
|
|
389
|
-
const current = await getCreditConfig(env, sub, tier);
|
|
390
|
-
const updated = {
|
|
391
|
-
...current,
|
|
392
|
-
...updates,
|
|
393
|
-
updated_at: new Date().toISOString(),
|
|
394
|
-
};
|
|
395
|
-
const key = `credits:config:${sub}`;
|
|
396
|
-
await env.CARRIER_USERS.put(key, JSON.stringify(updated));
|
|
397
|
-
return updated;
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Get credit usage summary for a user (used by billing UI).
|
|
401
|
-
*/
|
|
402
|
-
export async function getCreditSummary(env, sub, tier) {
|
|
403
|
-
const month = currentMonth();
|
|
404
|
-
const today = currentDay();
|
|
405
|
-
const ledger = await getLedger(env, sub, month, tier);
|
|
406
|
-
const dailyFree = await getDailyFreeCredits(env, sub, today);
|
|
407
|
-
const config = await getCreditConfig(env, sub, tier);
|
|
408
|
-
const rolloverRaw = await env.CARRIER_USERS.get(`credits:rollover:${sub}`, "json").catch(() => null);
|
|
409
|
-
const rollover = rolloverRaw;
|
|
410
|
-
const volumeDiscount = resolveVolumeDiscount(ledger.consumed);
|
|
411
|
-
return { ledger, daily_free: dailyFree, config, rollover, volume_discount_pct: volumeDiscount };
|
|
412
|
-
}
|
|
413
|
-
// ---------------------------------------------------------------------------
|
|
414
|
-
// Stripe Credit Grants
|
|
415
|
-
// ---------------------------------------------------------------------------
|
|
416
|
-
/**
|
|
417
|
-
* Issue a credit grant to a customer via Stripe Customer Balance Transactions.
|
|
418
|
-
* Used for promotional credits, referral bonuses, and compensation.
|
|
419
|
-
*/
|
|
420
|
-
export async function issueStripeCredits(env, sub, amountCents, description) {
|
|
421
|
-
const stripeKey = env.STRIPE_SECRET_KEY;
|
|
422
|
-
if (!stripeKey)
|
|
423
|
-
return false;
|
|
424
|
-
const customerId = await env.CARRIER_USERS.get(`stripe_customer_id:${sub}`);
|
|
425
|
-
if (!customerId)
|
|
426
|
-
return false;
|
|
427
|
-
const body = new URLSearchParams({
|
|
428
|
-
amount: String(-Math.abs(amountCents)), // Negative = credit to customer
|
|
429
|
-
currency: "usd",
|
|
430
|
-
description,
|
|
431
|
-
});
|
|
432
|
-
const resp = await fetch(`https://api.stripe.com/v1/customers/${customerId}/balance_transactions`, {
|
|
433
|
-
method: "POST",
|
|
434
|
-
headers: {
|
|
435
|
-
Authorization: `Bearer ${stripeKey}`,
|
|
436
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
437
|
-
},
|
|
438
|
-
body: body.toString(),
|
|
439
|
-
});
|
|
440
|
-
return resp.ok;
|
|
441
|
-
}
|
|
442
|
-
// ---------------------------------------------------------------------------
|
|
443
|
-
// Internal Helpers
|
|
444
|
-
// ---------------------------------------------------------------------------
|
|
445
|
-
async function resolveUserTier(env, sub) {
|
|
446
|
-
const raw = await env.CARRIER_USERS.get(`user:${sub}`, "json").catch(() => null);
|
|
447
|
-
if (raw && typeof raw === "object" && "tier" in raw) {
|
|
448
|
-
const t = raw.tier;
|
|
449
|
-
if (t === "pro" || t === "enterprise")
|
|
450
|
-
return t;
|
|
451
|
-
}
|
|
452
|
-
return "free";
|
|
453
|
-
}
|
|
454
|
-
function currentMonth() {
|
|
455
|
-
const now = new Date();
|
|
456
|
-
return `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
457
|
-
}
|
|
458
|
-
function currentDay() {
|
|
459
|
-
const now = new Date();
|
|
460
|
-
return `${now.getUTCFullYear()}${String(now.getUTCMonth() + 1).padStart(2, "0")}${String(now.getUTCDate()).padStart(2, "0")}`;
|
|
461
|
-
}
|
|
462
|
-
function previousMonth() {
|
|
463
|
-
const now = new Date();
|
|
464
|
-
const d = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1));
|
|
465
|
-
return `${d.getUTCFullYear()}${String(d.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
466
|
-
}
|
|
467
|
-
function firstDayNextMonth() {
|
|
468
|
-
const now = new Date();
|
|
469
|
-
const y = now.getUTCFullYear();
|
|
470
|
-
const m = now.getUTCMonth() + 1;
|
|
471
|
-
if (m === 12)
|
|
472
|
-
return new Date(Date.UTC(y + 1, 0, 1)).toISOString();
|
|
473
|
-
return new Date(Date.UTC(y, m, 1)).toISOString();
|
|
474
|
-
}
|
|
475
|
-
function firstDayMonthAfterNext() {
|
|
476
|
-
const now = new Date();
|
|
477
|
-
const y = now.getUTCFullYear();
|
|
478
|
-
const m = now.getUTCMonth() + 2;
|
|
479
|
-
if (m >= 12)
|
|
480
|
-
return new Date(Date.UTC(y + 1, m - 12, 1)).toISOString();
|
|
481
|
-
return new Date(Date.UTC(y, m, 1)).toISOString();
|
|
482
|
-
}
|
|
483
|
-
//# sourceMappingURL=credits.js.map
|