@nordsym/apiclaw 1.0.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/.github/ISSUE_TEMPLATE/add-api.yml +123 -0
- package/BRIEFING.md +30 -0
- package/CONCEPT.md +494 -0
- package/README.md +272 -0
- package/backend/convex/README.md +90 -0
- package/backend/convex/_generated/api.d.ts +55 -0
- package/backend/convex/_generated/api.js +23 -0
- package/backend/convex/_generated/dataModel.d.ts +60 -0
- package/backend/convex/_generated/server.d.ts +143 -0
- package/backend/convex/_generated/server.js +93 -0
- package/backend/convex/apiKeys.ts +75 -0
- package/backend/convex/purchases.ts +74 -0
- package/backend/convex/schema.ts +45 -0
- package/backend/convex/transactions.ts +57 -0
- package/backend/convex/tsconfig.json +25 -0
- package/backend/convex/users.ts +94 -0
- package/backend/package-lock.json +521 -0
- package/backend/package.json +15 -0
- package/dist/credits.d.ts +54 -0
- package/dist/credits.d.ts.map +1 -0
- package/dist/credits.js +209 -0
- package/dist/credits.js.map +1 -0
- package/dist/discovery.d.ts +37 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +109 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +355 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/apis.json +20894 -0
- package/dist/registry/parse_apis.py +146 -0
- package/dist/revenuecat.d.ts +61 -0
- package/dist/revenuecat.d.ts.map +1 -0
- package/dist/revenuecat.js +166 -0
- package/dist/revenuecat.js.map +1 -0
- package/dist/test.d.ts +6 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +81 -0
- package/dist/test.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/webhooks/revenuecat.d.ts +48 -0
- package/dist/webhooks/revenuecat.d.ts.map +1 -0
- package/dist/webhooks/revenuecat.js +119 -0
- package/dist/webhooks/revenuecat.js.map +1 -0
- package/docs/revenuecat-setup.md +89 -0
- package/landing/next-env.d.ts +5 -0
- package/landing/next.config.mjs +6 -0
- package/landing/package-lock.json +1666 -0
- package/landing/package.json +27 -0
- package/landing/postcss.config.js +6 -0
- package/landing/src/app/api/keys/route.ts +71 -0
- package/landing/src/app/api/log/route.ts +37 -0
- package/landing/src/app/api/stats/route.ts +37 -0
- package/landing/src/app/globals.css +261 -0
- package/landing/src/app/layout.tsx +37 -0
- package/landing/src/app/page.tsx +753 -0
- package/landing/src/app/page.tsx.bak +567 -0
- package/landing/src/components/AddKeyModal.tsx +159 -0
- package/landing/tailwind.config.ts +34 -0
- package/landing/tsconfig.json +20 -0
- package/newsletter-template.html +71 -0
- package/outreach/OUTREACH-SYSTEM.md +211 -0
- package/outreach/email-template.html +179 -0
- package/outreach/targets.md +133 -0
- package/package.json +39 -0
- package/src/credits.ts +261 -0
- package/src/discovery.ts +147 -0
- package/src/index.ts +396 -0
- package/src/registry/apis.json +20894 -0
- package/src/registry/parse_apis.py +146 -0
- package/src/revenuecat.ts +239 -0
- package/src/test.ts +97 -0
- package/src/types.ts +110 -0
- package/src/webhooks/revenuecat.ts +187 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,119 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revenuecat.js","sourceRoot":"","sources":["../../src/webhooks/revenuecat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
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.
|