@nordsym/apiclaw 1.0.0 → 1.1.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/AGENTS.md +74 -0
- package/HEARTBEAT.md +4 -0
- package/IDENTITY.md +22 -0
- package/README.md +197 -202
- package/SOUL.md +36 -0
- package/STATUS.md +237 -0
- package/TOOLS.md +36 -0
- package/USER.md +17 -0
- package/{backend/convex → convex}/_generated/api.d.ts +6 -6
- package/convex/credits.ts +211 -0
- package/convex/http.ts +490 -0
- package/convex/providers.ts +516 -0
- package/convex/purchases.ts +183 -0
- package/convex/schema.ts +180 -0
- package/convex.json +3 -0
- package/dist/credentials.d.ts +19 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +158 -0
- package/dist/credentials.js.map +1 -0
- package/dist/credits.d.ts +14 -11
- package/dist/credits.d.ts.map +1 -1
- package/dist/credits.js +151 -99
- package/dist/credits.js.map +1 -1
- package/dist/discovery.d.ts +7 -16
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +33 -40
- package/dist/discovery.js.map +1 -1
- package/dist/execute.d.ts +19 -0
- package/dist/execute.d.ts.map +1 -0
- package/dist/execute.js +285 -0
- package/dist/execute.js.map +1 -0
- package/dist/index.js +106 -30
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +6 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +19 -0
- package/dist/proxy.js.map +1 -0
- package/dist/registry/apis.json +95362 -202
- package/dist/registry/apis_expanded.json +100853 -0
- package/dist/stripe.d.ts +68 -0
- package/dist/stripe.d.ts.map +1 -0
- package/dist/stripe.js +196 -0
- package/dist/stripe.js.map +1 -0
- package/dist/test.d.ts +3 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +105 -75
- package/dist/test.js.map +1 -1
- package/dist/types.d.ts +0 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/webhook.d.ts +2 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +90 -0
- package/dist/webhook.js.map +1 -0
- package/landing/DESIGN.md +343 -0
- package/landing/package-lock.json +1190 -40
- package/landing/package.json +5 -2
- package/landing/public/android-chrome-192x192.png +0 -0
- package/landing/public/android-chrome-512x512.png +0 -0
- package/landing/public/apple-touch-icon.png +0 -0
- package/landing/public/demo.gif +0 -0
- package/landing/public/demo.mp4 +0 -0
- package/landing/public/favicon-16x16.png +0 -0
- package/landing/public/favicon-32x32.png +0 -0
- package/landing/public/favicon.ico +0 -0
- package/landing/public/favicon.svg +3 -0
- package/landing/public/icon.svg +47 -0
- package/landing/public/logo-mono.svg +37 -0
- package/landing/public/logo-simple.svg +45 -0
- package/landing/public/logo.svg +84 -0
- package/landing/public/og-image.png +0 -0
- package/landing/public/og-template.html +184 -0
- package/landing/public/site.webmanifest +31 -0
- package/landing/scripts/generate-assets.js +284 -0
- package/landing/scripts/generate-pngs.js +48 -0
- package/landing/scripts/generate-stats.js +42 -0
- package/landing/src/app/admin/page.tsx +348 -0
- package/landing/src/app/api/auth/magic-link/route.ts +73 -0
- package/landing/src/app/api/auth/session/route.ts +38 -0
- package/landing/src/app/api/auth/verify/route.ts +43 -0
- package/landing/src/app/api/og/route.tsx +74 -0
- package/landing/src/app/globals.css +439 -100
- package/landing/src/app/layout.tsx +37 -9
- package/landing/src/app/page.tsx +640 -552
- package/landing/src/app/providers/dashboard/login/page.tsx +176 -0
- package/landing/src/app/providers/dashboard/page.tsx +589 -0
- package/landing/src/app/providers/dashboard/verify/page.tsx +106 -0
- package/landing/src/app/providers/layout.tsx +14 -0
- package/landing/src/app/providers/page.tsx +402 -0
- package/landing/src/app/providers/register/page.tsx +670 -0
- package/landing/src/components/ProviderDashboard.tsx +794 -0
- package/landing/src/hooks/useDashboardData.ts +99 -0
- package/landing/src/lib/apis.json +116054 -0
- package/landing/src/lib/convex-client.ts +106 -0
- package/landing/src/lib/mock-data.ts +285 -0
- package/landing/src/lib/stats.json +6 -0
- package/landing/tailwind.config.ts +12 -11
- package/landing/tsconfig.tsbuildinfo +1 -0
- package/package.json +21 -20
- package/scripts/SYMBOT-FIX.md +238 -0
- package/scripts/demo-simulation.py +177 -0
- package/scripts/expand-more.py +502 -0
- package/scripts/expand-registry.py +434 -0
- package/scripts/history-sanitizer.ts +272 -0
- package/scripts/mass-scrape.py +1308 -0
- package/scripts/sync-and-deploy.sh +36 -0
- package/src/credentials.ts +177 -0
- package/src/credits.ts +190 -122
- package/src/discovery.ts +45 -58
- package/src/execute.ts +350 -0
- package/src/index.ts +113 -31
- package/src/proxy.ts +24 -0
- package/src/registry/apis.json +95362 -202
- package/src/registry/apis_expanded.json +100853 -0
- package/src/stripe.ts +243 -0
- package/src/test.ts +127 -89
- package/src/types.ts +0 -34
- package/src/webhook.ts +107 -0
- package/.github/ISSUE_TEMPLATE/add-api.yml +0 -123
- package/BRIEFING.md +0 -30
- package/backend/convex/apiKeys.ts +0 -75
- package/backend/convex/purchases.ts +0 -74
- package/backend/convex/schema.ts +0 -45
- package/backend/convex/transactions.ts +0 -57
- package/backend/convex/users.ts +0 -94
- package/backend/package-lock.json +0 -521
- package/backend/package.json +0 -15
- package/dist/registry/parse_apis.py +0 -146
- package/dist/revenuecat.d.ts +0 -61
- package/dist/revenuecat.d.ts.map +0 -1
- package/dist/revenuecat.js +0 -166
- package/dist/revenuecat.js.map +0 -1
- package/dist/webhooks/revenuecat.d.ts +0 -48
- package/dist/webhooks/revenuecat.d.ts.map +0 -1
- package/dist/webhooks/revenuecat.js +0 -119
- package/dist/webhooks/revenuecat.js.map +0 -1
- package/docs/revenuecat-setup.md +0 -89
- package/landing/src/app/api/keys/route.ts +0 -71
- package/landing/src/app/api/log/route.ts +0 -37
- package/landing/src/app/api/stats/route.ts +0 -37
- package/landing/src/app/page.tsx.bak +0 -567
- package/landing/src/components/AddKeyModal.tsx +0 -159
- package/newsletter-template.html +0 -71
- package/outreach/OUTREACH-SYSTEM.md +0 -211
- package/outreach/email-template.html +0 -179
- package/outreach/targets.md +0 -133
- package/src/registry/parse_apis.py +0 -146
- package/src/revenuecat.ts +0 -239
- package/src/webhooks/revenuecat.ts +0 -187
- /package/{backend/convex → convex}/README.md +0 -0
- /package/{backend/convex → convex}/_generated/api.js +0 -0
- /package/{backend/convex → convex}/_generated/dataModel.d.ts +0 -0
- /package/{backend/convex → convex}/_generated/server.d.ts +0 -0
- /package/{backend/convex → convex}/_generated/server.js +0 -0
- /package/{backend/convex → convex}/tsconfig.json +0 -0
package/dist/revenuecat.d.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
export declare const ENTITLEMENTS: {
|
|
2
|
-
readonly PRO: "pro";
|
|
3
|
-
};
|
|
4
|
-
export declare const PRODUCTS: {
|
|
5
|
-
readonly PRO_MONTHLY: "apiclaw_pro_monthly";
|
|
6
|
-
};
|
|
7
|
-
interface RevenueCatSubscriber {
|
|
8
|
-
subscriber: {
|
|
9
|
-
entitlements: Record<string, {
|
|
10
|
-
product_identifier: string;
|
|
11
|
-
expires_date: string | null;
|
|
12
|
-
purchase_date: string;
|
|
13
|
-
}>;
|
|
14
|
-
subscriptions: Record<string, {
|
|
15
|
-
expires_date: string | null;
|
|
16
|
-
purchase_date: string;
|
|
17
|
-
product_plan_identifier?: string;
|
|
18
|
-
billing_issues_detected_at?: string | null;
|
|
19
|
-
unsubscribe_detected_at?: string | null;
|
|
20
|
-
}>;
|
|
21
|
-
non_subscriptions: Record<string, unknown>;
|
|
22
|
-
first_seen: string;
|
|
23
|
-
original_app_user_id: string;
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Check if a user has Pro subscription
|
|
28
|
-
*/
|
|
29
|
-
export declare function hasProSubscription(userId: string): Promise<boolean>;
|
|
30
|
-
/**
|
|
31
|
-
* Get all entitlements for a user
|
|
32
|
-
*/
|
|
33
|
-
export declare function getEntitlements(userId: string): Promise<{
|
|
34
|
-
pro: boolean;
|
|
35
|
-
expiresAt: string | null;
|
|
36
|
-
purchaseDate: string | null;
|
|
37
|
-
}>;
|
|
38
|
-
/**
|
|
39
|
-
* Get fee percentage based on subscription status
|
|
40
|
-
* Pro = 2%, Free = 5%
|
|
41
|
-
*/
|
|
42
|
-
export declare function getFeePercentage(userId: string): Promise<number>;
|
|
43
|
-
/**
|
|
44
|
-
* Calculate platform fee for a transaction
|
|
45
|
-
*/
|
|
46
|
-
export declare function calculateFee(userId: string, amountUsd: number): Promise<{
|
|
47
|
-
feePercentage: number;
|
|
48
|
-
feeAmount: number;
|
|
49
|
-
netAmount: number;
|
|
50
|
-
isPro: boolean;
|
|
51
|
-
}>;
|
|
52
|
-
/**
|
|
53
|
-
* Create or update subscriber attributes
|
|
54
|
-
*/
|
|
55
|
-
export declare function setSubscriberAttributes(userId: string, attributes: Record<string, string>): Promise<boolean>;
|
|
56
|
-
/**
|
|
57
|
-
* Grant promotional entitlement (for testing/promos)
|
|
58
|
-
*/
|
|
59
|
-
export declare function grantPromoEntitlement(userId: string, durationDays?: number): Promise<boolean>;
|
|
60
|
-
export type { RevenueCatSubscriber };
|
|
61
|
-
//# sourceMappingURL=revenuecat.d.ts.map
|
package/dist/revenuecat.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"revenuecat.d.ts","sourceRoot":"","sources":["../src/revenuecat.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY;;CAEf,CAAC;AAGX,eAAO,MAAM,QAAQ;;CAEX,CAAC;AAQX,UAAU,oBAAoB;IAC5B,UAAU,EAAE;QACV,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE;YAC3B,kBAAkB,EAAE,MAAM,CAAC;YAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;YAC5B,aAAa,EAAE,MAAM,CAAC;SACvB,CAAC,CAAC;QACH,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE;YAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;YAC5B,aAAa,EAAE,MAAM,CAAC;YACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;YACjC,0BAA0B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;YAC3C,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;SACzC,CAAC,CAAC;QACH,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3C,UAAU,EAAE,MAAM,CAAC;QACnB,oBAAoB,EAAE,MAAM,CAAC;KAC9B,CAAC;CACH;AA2CD;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAsBzE;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7D,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC,CA0BD;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGtE;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC,CAWD;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC,OAAO,CAAC,OAAO,CAAC,CAwBlB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,YAAY,GAAE,MAAW,GACxB,OAAO,CAAC,OAAO,CAAC,CAsBlB;AAGD,YAAY,EAAE,oBAAoB,EAAE,CAAC"}
|
package/dist/revenuecat.js
DELETED
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
// RevenueCat integration for APIClaw
|
|
2
|
-
// Handles Pro subscription checking and fee calculation
|
|
3
|
-
const REVENUECAT_API_URL = 'https://api.revenuecat.com/v1';
|
|
4
|
-
const PROJECT_ID = '0d074df4';
|
|
5
|
-
// Entitlement identifiers
|
|
6
|
-
export const ENTITLEMENTS = {
|
|
7
|
-
PRO: 'pro',
|
|
8
|
-
};
|
|
9
|
-
// Product identifiers
|
|
10
|
-
export const PRODUCTS = {
|
|
11
|
-
PRO_MONTHLY: 'apiclaw_pro_monthly',
|
|
12
|
-
};
|
|
13
|
-
// Fee structure
|
|
14
|
-
const FEES = {
|
|
15
|
-
FREE: 0.05, // 5% transaction fee
|
|
16
|
-
PRO: 0.02, // 2% transaction fee
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Get RevenueCat API key from environment
|
|
20
|
-
*/
|
|
21
|
-
function getApiKey() {
|
|
22
|
-
const key = process.env.REVENUECAT_SECRET_KEY;
|
|
23
|
-
if (!key) {
|
|
24
|
-
throw new Error('REVENUECAT_SECRET_KEY not set');
|
|
25
|
-
}
|
|
26
|
-
return key;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Fetch subscriber info from RevenueCat
|
|
30
|
-
*/
|
|
31
|
-
async function getSubscriber(userId) {
|
|
32
|
-
try {
|
|
33
|
-
const response = await fetch(`${REVENUECAT_API_URL}/subscribers/${encodeURIComponent(userId)}`, {
|
|
34
|
-
headers: {
|
|
35
|
-
'Authorization': `Bearer ${getApiKey()}`,
|
|
36
|
-
'Content-Type': 'application/json',
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
if (response.status === 404) {
|
|
40
|
-
return null; // User doesn't exist in RevenueCat
|
|
41
|
-
}
|
|
42
|
-
if (!response.ok) {
|
|
43
|
-
throw new Error(`RevenueCat API error: ${response.status}`);
|
|
44
|
-
}
|
|
45
|
-
return await response.json();
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
console.error('Error fetching subscriber:', error);
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Check if a user has Pro subscription
|
|
54
|
-
*/
|
|
55
|
-
export async function hasProSubscription(userId) {
|
|
56
|
-
const subscriber = await getSubscriber(userId);
|
|
57
|
-
if (!subscriber) {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
const proEntitlement = subscriber.subscriber.entitlements[ENTITLEMENTS.PRO];
|
|
61
|
-
if (!proEntitlement) {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
// Check if entitlement is active (not expired)
|
|
65
|
-
if (proEntitlement.expires_date) {
|
|
66
|
-
const expiresAt = new Date(proEntitlement.expires_date);
|
|
67
|
-
if (expiresAt < new Date()) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Get all entitlements for a user
|
|
75
|
-
*/
|
|
76
|
-
export async function getEntitlements(userId) {
|
|
77
|
-
const subscriber = await getSubscriber(userId);
|
|
78
|
-
const result = {
|
|
79
|
-
pro: false,
|
|
80
|
-
expiresAt: null,
|
|
81
|
-
purchaseDate: null,
|
|
82
|
-
};
|
|
83
|
-
if (!subscriber) {
|
|
84
|
-
return result;
|
|
85
|
-
}
|
|
86
|
-
const proEntitlement = subscriber.subscriber.entitlements[ENTITLEMENTS.PRO];
|
|
87
|
-
if (proEntitlement) {
|
|
88
|
-
const expiresAt = proEntitlement.expires_date
|
|
89
|
-
? new Date(proEntitlement.expires_date)
|
|
90
|
-
: null;
|
|
91
|
-
result.pro = !expiresAt || expiresAt > new Date();
|
|
92
|
-
result.expiresAt = proEntitlement.expires_date;
|
|
93
|
-
result.purchaseDate = proEntitlement.purchase_date;
|
|
94
|
-
}
|
|
95
|
-
return result;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Get fee percentage based on subscription status
|
|
99
|
-
* Pro = 2%, Free = 5%
|
|
100
|
-
*/
|
|
101
|
-
export async function getFeePercentage(userId) {
|
|
102
|
-
const isPro = await hasProSubscription(userId);
|
|
103
|
-
return isPro ? FEES.PRO : FEES.FREE;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Calculate platform fee for a transaction
|
|
107
|
-
*/
|
|
108
|
-
export async function calculateFee(userId, amountUsd) {
|
|
109
|
-
const isPro = await hasProSubscription(userId);
|
|
110
|
-
const feePercentage = isPro ? FEES.PRO : FEES.FREE;
|
|
111
|
-
const feeAmount = amountUsd * feePercentage;
|
|
112
|
-
return {
|
|
113
|
-
feePercentage,
|
|
114
|
-
feeAmount: Math.round(feeAmount * 100) / 100, // Round to 2 decimals
|
|
115
|
-
netAmount: Math.round((amountUsd - feeAmount) * 100) / 100,
|
|
116
|
-
isPro,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Create or update subscriber attributes
|
|
121
|
-
*/
|
|
122
|
-
export async function setSubscriberAttributes(userId, attributes) {
|
|
123
|
-
try {
|
|
124
|
-
const formattedAttributes = {};
|
|
125
|
-
for (const [key, value] of Object.entries(attributes)) {
|
|
126
|
-
formattedAttributes[`$${key}`] = { value };
|
|
127
|
-
}
|
|
128
|
-
const response = await fetch(`${REVENUECAT_API_URL}/subscribers/${encodeURIComponent(userId)}/attributes`, {
|
|
129
|
-
method: 'POST',
|
|
130
|
-
headers: {
|
|
131
|
-
'Authorization': `Bearer ${getApiKey()}`,
|
|
132
|
-
'Content-Type': 'application/json',
|
|
133
|
-
},
|
|
134
|
-
body: JSON.stringify({ attributes: formattedAttributes }),
|
|
135
|
-
});
|
|
136
|
-
return response.ok;
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
console.error('Error setting subscriber attributes:', error);
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Grant promotional entitlement (for testing/promos)
|
|
145
|
-
*/
|
|
146
|
-
export async function grantPromoEntitlement(userId, durationDays = 30) {
|
|
147
|
-
try {
|
|
148
|
-
const response = await fetch(`${REVENUECAT_API_URL}/subscribers/${encodeURIComponent(userId)}/entitlements/${ENTITLEMENTS.PRO}/promotional`, {
|
|
149
|
-
method: 'POST',
|
|
150
|
-
headers: {
|
|
151
|
-
'Authorization': `Bearer ${getApiKey()}`,
|
|
152
|
-
'Content-Type': 'application/json',
|
|
153
|
-
},
|
|
154
|
-
body: JSON.stringify({
|
|
155
|
-
duration: 'daily',
|
|
156
|
-
duration_count: durationDays,
|
|
157
|
-
}),
|
|
158
|
-
});
|
|
159
|
-
return response.ok;
|
|
160
|
-
}
|
|
161
|
-
catch (error) {
|
|
162
|
-
console.error('Error granting promo entitlement:', error);
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
//# sourceMappingURL=revenuecat.js.map
|
package/dist/revenuecat.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"revenuecat.js","sourceRoot":"","sources":["../src/revenuecat.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { Hono } from 'hono';
|
|
2
|
-
type WebhookEventType = 'INITIAL_PURCHASE' | 'RENEWAL' | 'CANCELLATION' | 'UNCANCELLATION' | 'NON_RENEWING_PURCHASE' | 'SUBSCRIPTION_PAUSED' | 'SUBSCRIPTION_EXTENDED' | 'EXPIRATION' | 'BILLING_ISSUE' | 'PRODUCT_CHANGE' | 'TRANSFER';
|
|
3
|
-
interface RevenueCatWebhookEvent {
|
|
4
|
-
api_version: string;
|
|
5
|
-
event: {
|
|
6
|
-
type: WebhookEventType;
|
|
7
|
-
id: string;
|
|
8
|
-
app_id: string;
|
|
9
|
-
app_user_id: string;
|
|
10
|
-
original_app_user_id: string;
|
|
11
|
-
aliases: string[];
|
|
12
|
-
product_id: string;
|
|
13
|
-
entitlement_ids: string[];
|
|
14
|
-
period_type: string;
|
|
15
|
-
purchased_at_ms: number;
|
|
16
|
-
expiration_at_ms: number | null;
|
|
17
|
-
environment: 'SANDBOX' | 'PRODUCTION';
|
|
18
|
-
store: string;
|
|
19
|
-
is_trial_conversion?: boolean;
|
|
20
|
-
cancel_reason?: string;
|
|
21
|
-
price_in_purchased_currency?: number;
|
|
22
|
-
currency?: string;
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
interface UserSubscriptionUpdate {
|
|
26
|
-
userId: string;
|
|
27
|
-
isPro: boolean;
|
|
28
|
-
subscriptionStatus: 'active' | 'cancelled' | 'expired' | 'paused' | 'billing_issue';
|
|
29
|
-
productId: string | null;
|
|
30
|
-
expiresAt: number | null;
|
|
31
|
-
updatedAt: number;
|
|
32
|
-
eventType: WebhookEventType;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Update user subscription status in Convex
|
|
36
|
-
*/
|
|
37
|
-
declare function updateConvexUser(update: UserSubscriptionUpdate): Promise<boolean>;
|
|
38
|
-
/**
|
|
39
|
-
* Process webhook event and determine subscription state
|
|
40
|
-
*/
|
|
41
|
-
declare function processEvent(event: RevenueCatWebhookEvent['event']): UserSubscriptionUpdate;
|
|
42
|
-
/**
|
|
43
|
-
* Create Hono router for webhook handling
|
|
44
|
-
*/
|
|
45
|
-
export declare function createWebhookRouter(): Hono;
|
|
46
|
-
export { processEvent, updateConvexUser };
|
|
47
|
-
export type { RevenueCatWebhookEvent, UserSubscriptionUpdate };
|
|
48
|
-
//# sourceMappingURL=revenuecat.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"revenuecat.d.ts","sourceRoot":"","sources":["../../src/webhooks/revenuecat.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK5B,KAAK,gBAAgB,GACjB,kBAAkB,GAClB,SAAS,GACT,cAAc,GACd,gBAAgB,GAChB,uBAAuB,GACvB,qBAAqB,GACrB,uBAAuB,GACvB,YAAY,GACZ,eAAe,GACf,gBAAgB,GAChB,UAAU,CAAC;AAEf,UAAU,sBAAsB;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE;QACL,IAAI,EAAE,gBAAgB,CAAC;QACvB,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,WAAW,EAAE,SAAS,GAAG,YAAY,CAAC;QACtC,KAAK,EAAE,MAAM,CAAC;QACd,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,2BAA2B,CAAC,EAAE,MAAM,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,eAAe,CAAC;IACpF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,gBAAgB,CAAC;CAC7B;AAED;;GAEG;AACH,iBAAe,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuBhF;AAED;;GAEG;AACH,iBAAS,YAAY,CAAC,KAAK,EAAE,sBAAsB,CAAC,OAAO,CAAC,GAAG,sBAAsB,CA0DpF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAkC1C;AAGD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAC1C,YAAY,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// RevenueCat webhook handler for APIClaw
|
|
2
|
-
// Handles subscription lifecycle events and syncs to Convex
|
|
3
|
-
import { Hono } from 'hono';
|
|
4
|
-
const CONVEX_URL = process.env.CONVEX_URL || 'https://agile-crane-840.convex.cloud';
|
|
5
|
-
/**
|
|
6
|
-
* Update user subscription status in Convex
|
|
7
|
-
*/
|
|
8
|
-
async function updateConvexUser(update) {
|
|
9
|
-
try {
|
|
10
|
-
const response = await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
11
|
-
method: 'POST',
|
|
12
|
-
headers: {
|
|
13
|
-
'Content-Type': 'application/json',
|
|
14
|
-
},
|
|
15
|
-
body: JSON.stringify({
|
|
16
|
-
path: 'users:updateSubscription',
|
|
17
|
-
args: update,
|
|
18
|
-
}),
|
|
19
|
-
});
|
|
20
|
-
if (!response.ok) {
|
|
21
|
-
console.error('Convex update failed:', await response.text());
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
console.error('Error updating Convex:', error);
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Process webhook event and determine subscription state
|
|
33
|
-
*/
|
|
34
|
-
function processEvent(event) {
|
|
35
|
-
const now = Date.now();
|
|
36
|
-
const expiresAt = event.expiration_at_ms;
|
|
37
|
-
const isExpired = expiresAt ? expiresAt < now : false;
|
|
38
|
-
let subscriptionStatus = 'active';
|
|
39
|
-
let isPro = true;
|
|
40
|
-
switch (event.type) {
|
|
41
|
-
case 'INITIAL_PURCHASE':
|
|
42
|
-
case 'RENEWAL':
|
|
43
|
-
case 'UNCANCELLATION':
|
|
44
|
-
case 'SUBSCRIPTION_EXTENDED':
|
|
45
|
-
subscriptionStatus = 'active';
|
|
46
|
-
isPro = true;
|
|
47
|
-
break;
|
|
48
|
-
case 'CANCELLATION':
|
|
49
|
-
// Still active until expiration
|
|
50
|
-
subscriptionStatus = 'cancelled';
|
|
51
|
-
isPro = !isExpired;
|
|
52
|
-
break;
|
|
53
|
-
case 'EXPIRATION':
|
|
54
|
-
subscriptionStatus = 'expired';
|
|
55
|
-
isPro = false;
|
|
56
|
-
break;
|
|
57
|
-
case 'SUBSCRIPTION_PAUSED':
|
|
58
|
-
subscriptionStatus = 'paused';
|
|
59
|
-
isPro = false;
|
|
60
|
-
break;
|
|
61
|
-
case 'BILLING_ISSUE':
|
|
62
|
-
subscriptionStatus = 'billing_issue';
|
|
63
|
-
// Keep Pro until grace period ends
|
|
64
|
-
isPro = !isExpired;
|
|
65
|
-
break;
|
|
66
|
-
case 'PRODUCT_CHANGE':
|
|
67
|
-
case 'TRANSFER':
|
|
68
|
-
subscriptionStatus = 'active';
|
|
69
|
-
isPro = event.entitlement_ids.includes('pro');
|
|
70
|
-
break;
|
|
71
|
-
default:
|
|
72
|
-
isPro = event.entitlement_ids.includes('pro') && !isExpired;
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
userId: event.app_user_id,
|
|
76
|
-
isPro,
|
|
77
|
-
subscriptionStatus,
|
|
78
|
-
productId: event.product_id,
|
|
79
|
-
expiresAt: expiresAt,
|
|
80
|
-
updatedAt: now,
|
|
81
|
-
eventType: event.type,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Create Hono router for webhook handling
|
|
86
|
-
*/
|
|
87
|
-
export function createWebhookRouter() {
|
|
88
|
-
const app = new Hono();
|
|
89
|
-
// Health check
|
|
90
|
-
app.get('/webhooks/revenuecat/health', (c) => {
|
|
91
|
-
return c.json({ status: 'ok', service: 'revenuecat-webhook' });
|
|
92
|
-
});
|
|
93
|
-
// RevenueCat webhook endpoint
|
|
94
|
-
app.post('/webhooks/revenuecat', async (c) => {
|
|
95
|
-
try {
|
|
96
|
-
const body = await c.req.json();
|
|
97
|
-
console.log(`[RevenueCat] Received ${body.event.type} for user ${body.event.app_user_id}`);
|
|
98
|
-
// Process the event
|
|
99
|
-
const update = processEvent(body.event);
|
|
100
|
-
// Update Convex
|
|
101
|
-
const success = await updateConvexUser(update);
|
|
102
|
-
if (success) {
|
|
103
|
-
console.log(`[RevenueCat] Updated user ${update.userId}: isPro=${update.isPro}`);
|
|
104
|
-
return c.json({ success: true });
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
return c.json({ success: false, error: 'Failed to update database' }, 500);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
catch (error) {
|
|
111
|
-
console.error('[RevenueCat] Webhook error:', error);
|
|
112
|
-
return c.json({ success: false, error: 'Internal error' }, 500);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
return app;
|
|
116
|
-
}
|
|
117
|
-
// Export for testing
|
|
118
|
-
export { processEvent, updateConvexUser };
|
|
119
|
-
//# sourceMappingURL=revenuecat.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"revenuecat.js","sourceRoot":"","sources":["../../src/webhooks/revenuecat.ts"],"names":[],"mappings":"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"}
|
package/docs/revenuecat-setup.md
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
# APIClaw RevenueCat Setup
|
|
2
|
-
|
|
3
|
-
## Current Status ✅
|
|
4
|
-
|
|
5
|
-
**Entitlement Created:**
|
|
6
|
-
- ID: `entl34d5186e73`
|
|
7
|
-
- Lookup Key: `pro`
|
|
8
|
-
- Display Name: "APIClaw Pro"
|
|
9
|
-
|
|
10
|
-
**Project ID:** `0d074df4`
|
|
11
|
-
|
|
12
|
-
## Required Manual Setup
|
|
13
|
-
|
|
14
|
-
### 1. Link Stripe Account
|
|
15
|
-
|
|
16
|
-
Go to RevenueCat Dashboard → Project Settings → Integrations → Stripe
|
|
17
|
-
|
|
18
|
-
Link your Stripe account to enable web subscriptions.
|
|
19
|
-
|
|
20
|
-
### 2. Create Stripe Product
|
|
21
|
-
|
|
22
|
-
After linking Stripe, create:
|
|
23
|
-
- **Product ID:** `apiclaw_pro_monthly`
|
|
24
|
-
- **Price:** $99/month
|
|
25
|
-
- **Description:** APIClaw Pro - 2% transaction fees, priority support
|
|
26
|
-
|
|
27
|
-
### 3. Attach Product to Entitlement
|
|
28
|
-
|
|
29
|
-
In RevenueCat Dashboard:
|
|
30
|
-
1. Go to Products
|
|
31
|
-
2. Create product pointing to Stripe product ID
|
|
32
|
-
3. Attach to "pro" entitlement
|
|
33
|
-
|
|
34
|
-
### 4. Configure Webhook
|
|
35
|
-
|
|
36
|
-
Add webhook URL in RevenueCat Dashboard → Webhooks:
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
https://your-api-domain.com/webhooks/revenuecat
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Events to enable:
|
|
43
|
-
- INITIAL_PURCHASE
|
|
44
|
-
- RENEWAL
|
|
45
|
-
- CANCELLATION
|
|
46
|
-
- EXPIRATION
|
|
47
|
-
- BILLING_ISSUE
|
|
48
|
-
|
|
49
|
-
## Environment Variables
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
# Required in .env
|
|
53
|
-
REVENUECAT_SECRET_KEY=sk_TwFjVtmGbrgtULpWgHDYBHiZuQvlK
|
|
54
|
-
CONVEX_URL=https://agile-crane-840.convex.cloud
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## API Usage
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
import { hasProSubscription, getFeePercentage, calculateFee } from './revenuecat';
|
|
61
|
-
|
|
62
|
-
// Check if user has Pro
|
|
63
|
-
const isPro = await hasProSubscription('user_123');
|
|
64
|
-
|
|
65
|
-
// Get fee percentage (2% Pro, 5% Free)
|
|
66
|
-
const fee = await getFeePercentage('user_123');
|
|
67
|
-
|
|
68
|
-
// Calculate fee for transaction
|
|
69
|
-
const { feeAmount, netAmount, isPro } = await calculateFee('user_123', 100);
|
|
70
|
-
// isPro: true → feeAmount: $2, netAmount: $98
|
|
71
|
-
// isPro: false → feeAmount: $5, netAmount: $95
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Pro Benefits
|
|
75
|
-
|
|
76
|
-
| Feature | Free | Pro ($99/mo) |
|
|
77
|
-
|---------|------|--------------|
|
|
78
|
-
| Transaction Fee | 5% | 2% |
|
|
79
|
-
| Support | Community | Priority |
|
|
80
|
-
| API Calls | Standard | Priority routing |
|
|
81
|
-
| Analytics | Basic | Advanced |
|
|
82
|
-
|
|
83
|
-
## Break-even Analysis
|
|
84
|
-
|
|
85
|
-
Monthly API spend for Pro to pay off:
|
|
86
|
-
- Fee savings: 3% per transaction
|
|
87
|
-
- Break-even: $99 / 0.03 = $3,300/month in API spend
|
|
88
|
-
|
|
89
|
-
If spending >$3.3k/month on APIs → Pro saves money.
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
-
|
|
3
|
-
const CONVEX_URL = "https://fastidious-bloodhound-287.convex.cloud";
|
|
4
|
-
|
|
5
|
-
export async function POST(req: NextRequest) {
|
|
6
|
-
try {
|
|
7
|
-
const body = await req.json();
|
|
8
|
-
const { userEmail, apiId, apiName, keyName, keyValue } = body;
|
|
9
|
-
|
|
10
|
-
if (!userEmail || !apiId || !keyValue) {
|
|
11
|
-
return NextResponse.json({ error: "Missing required fields" }, { status: 400 });
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Save to Convex
|
|
15
|
-
const response = await fetch(`${CONVEX_URL}/api/mutation`, {
|
|
16
|
-
method: "POST",
|
|
17
|
-
headers: { "Content-Type": "application/json" },
|
|
18
|
-
body: JSON.stringify({
|
|
19
|
-
path: "apiKeys:add",
|
|
20
|
-
args: {
|
|
21
|
-
userEmail,
|
|
22
|
-
apiId,
|
|
23
|
-
apiName: apiName || apiId,
|
|
24
|
-
keyName: keyName || "API Key",
|
|
25
|
-
keyValue,
|
|
26
|
-
},
|
|
27
|
-
}),
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
if (!response.ok) {
|
|
31
|
-
throw new Error("Convex mutation failed");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const result = await response.json();
|
|
35
|
-
return NextResponse.json({ success: true, id: result });
|
|
36
|
-
} catch (error) {
|
|
37
|
-
console.error("Error saving key:", error);
|
|
38
|
-
return NextResponse.json({ error: "Failed to save key" }, { status: 500 });
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function GET(req: NextRequest) {
|
|
43
|
-
try {
|
|
44
|
-
const email = req.nextUrl.searchParams.get("email");
|
|
45
|
-
const apiId = req.nextUrl.searchParams.get("apiId");
|
|
46
|
-
|
|
47
|
-
if (!email) {
|
|
48
|
-
return NextResponse.json({ error: "Email required" }, { status: 400 });
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Query Convex
|
|
52
|
-
const path = apiId ? "apiKeys:get" : "apiKeys:listByEmail";
|
|
53
|
-
const args = apiId ? { userEmail: email, apiId } : { userEmail: email };
|
|
54
|
-
|
|
55
|
-
const response = await fetch(`${CONVEX_URL}/api/query`, {
|
|
56
|
-
method: "POST",
|
|
57
|
-
headers: { "Content-Type": "application/json" },
|
|
58
|
-
body: JSON.stringify({ path, args }),
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
if (!response.ok) {
|
|
62
|
-
throw new Error("Convex query failed");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const keys = await response.json();
|
|
66
|
-
return NextResponse.json({ keys });
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.error("Error fetching keys:", error);
|
|
69
|
-
return NextResponse.json({ error: "Failed to fetch keys" }, { status: 500 });
|
|
70
|
-
}
|
|
71
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { NextResponse } from 'next/server';
|
|
2
|
-
|
|
3
|
-
// Use Mission Control Convex for logging
|
|
4
|
-
const MC_CONVEX_URL = 'https://agile-crane-840.convex.cloud';
|
|
5
|
-
|
|
6
|
-
export async function POST(request: Request) {
|
|
7
|
-
try {
|
|
8
|
-
const body = await request.json();
|
|
9
|
-
const { event, data } = body;
|
|
10
|
-
|
|
11
|
-
// Log to Mission Control as activity
|
|
12
|
-
await fetch(`${MC_CONVEX_URL}/api/mutation`, {
|
|
13
|
-
method: 'POST',
|
|
14
|
-
headers: { 'Content-Type': 'application/json' },
|
|
15
|
-
body: JSON.stringify({
|
|
16
|
-
path: 'activities:create',
|
|
17
|
-
args: {
|
|
18
|
-
type: 'notification',
|
|
19
|
-
title: `APIClaw: ${event}`,
|
|
20
|
-
description: JSON.stringify(data),
|
|
21
|
-
status: 'auto_done',
|
|
22
|
-
agentId: 'apiclaw',
|
|
23
|
-
agentLabel: 'APIClaw Landing'
|
|
24
|
-
}
|
|
25
|
-
})
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
return NextResponse.json({ ok: true });
|
|
29
|
-
} catch (error) {
|
|
30
|
-
// Silently fail - don't block user experience
|
|
31
|
-
return NextResponse.json({ ok: true });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function GET() {
|
|
36
|
-
return NextResponse.json({ status: 'logging endpoint' });
|
|
37
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { NextResponse } from 'next/server';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
|
|
5
|
-
export const dynamic = 'force-dynamic';
|
|
6
|
-
export const revalidate = 0;
|
|
7
|
-
|
|
8
|
-
export async function GET() {
|
|
9
|
-
try {
|
|
10
|
-
// Read from the registry
|
|
11
|
-
const registryPath = path.join(process.cwd(), '..', 'src', 'registry', 'apis.json');
|
|
12
|
-
|
|
13
|
-
let apiCount = 1490; // fallback
|
|
14
|
-
let categories = 52; // fallback
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
const data = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
|
|
18
|
-
apiCount = data.length;
|
|
19
|
-
categories = new Set(data.map((api: any) => api.category)).size;
|
|
20
|
-
} catch (e) {
|
|
21
|
-
// Use fallbacks if file not found
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return NextResponse.json({
|
|
25
|
-
apis: apiCount,
|
|
26
|
-
categories: categories,
|
|
27
|
-
timestamp: new Date().toISOString()
|
|
28
|
-
}, {
|
|
29
|
-
headers: {
|
|
30
|
-
'Cache-Control': 'no-store, max-age=0',
|
|
31
|
-
'Access-Control-Allow-Origin': '*'
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
} catch (error) {
|
|
35
|
-
return NextResponse.json({ apis: 1490, categories: 52 }, { status: 200 });
|
|
36
|
-
}
|
|
37
|
-
}
|