@opencard-dev/webhook-server 0.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/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +389 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCard Webhook Server
|
|
3
|
+
* =======================
|
|
4
|
+
* A lightweight HTTP server built with Hono that receives real-time
|
|
5
|
+
* authorization requests from Stripe and responds with approve/decline
|
|
6
|
+
* decisions based on the card's spend rules.
|
|
7
|
+
*
|
|
8
|
+
* ─── Why Hono? ──────────────────────────────────────────────────────────────
|
|
9
|
+
* Hono is a tiny, fast web framework — roughly 14KB. We chose it over Express
|
|
10
|
+
* because it's TypeScript-native, has cleaner middleware, and is significantly
|
|
11
|
+
* faster. Speed matters here: Stripe gives us ~2 seconds to respond to
|
|
12
|
+
* authorization requests before automatically declining.
|
|
13
|
+
*
|
|
14
|
+
* ─── The one endpoint that matters ─────────────────────────────────────────
|
|
15
|
+
* POST /webhooks/stripe
|
|
16
|
+
* ↑ Stripe calls this in real time for every card event.
|
|
17
|
+
* We verify the signature, parse the event, and route it to a handler.
|
|
18
|
+
* For authorization requests, we return { approved: true/false }.
|
|
19
|
+
*
|
|
20
|
+
* ─── Security ───────────────────────────────────────────────────────────────
|
|
21
|
+
* Stripe signs every webhook with HMAC-SHA256 using a shared secret.
|
|
22
|
+
* We verify this signature before processing ANYTHING. An invalid signature
|
|
23
|
+
* means the request didn't come from Stripe — we reject it with a 400.
|
|
24
|
+
*
|
|
25
|
+
* ─── Running the server ─────────────────────────────────────────────────────
|
|
26
|
+
* Set env vars and start:
|
|
27
|
+
* STRIPE_SECRET_KEY=sk_test_... \
|
|
28
|
+
* STRIPE_WEBHOOK_SECRET=whsec_... \
|
|
29
|
+
* PORT=3000 \
|
|
30
|
+
* node dist/index.js
|
|
31
|
+
*
|
|
32
|
+
* Get your webhook secret from the Stripe CLI:
|
|
33
|
+
* stripe listen --forward-to localhost:3000/webhooks/stripe
|
|
34
|
+
*/
|
|
35
|
+
import { Hono } from 'hono';
|
|
36
|
+
/**
|
|
37
|
+
* Create and return the Hono webhook application.
|
|
38
|
+
* This is exported so the serve command can reuse the same app.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createWebhookApp(): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
41
|
+
/**
|
|
42
|
+
* Start the webhook server on the specified port.
|
|
43
|
+
* Returns a promise that resolves when the server is ready.
|
|
44
|
+
*/
|
|
45
|
+
export declare function startWebhookServer(port: number): Promise<void>;
|
|
46
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAyU5B;;;GAGG;AACH,wBAAgB,gBAAgB,+EAE/B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA4C9D"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenCard Webhook Server
|
|
4
|
+
* =======================
|
|
5
|
+
* A lightweight HTTP server built with Hono that receives real-time
|
|
6
|
+
* authorization requests from Stripe and responds with approve/decline
|
|
7
|
+
* decisions based on the card's spend rules.
|
|
8
|
+
*
|
|
9
|
+
* ─── Why Hono? ──────────────────────────────────────────────────────────────
|
|
10
|
+
* Hono is a tiny, fast web framework — roughly 14KB. We chose it over Express
|
|
11
|
+
* because it's TypeScript-native, has cleaner middleware, and is significantly
|
|
12
|
+
* faster. Speed matters here: Stripe gives us ~2 seconds to respond to
|
|
13
|
+
* authorization requests before automatically declining.
|
|
14
|
+
*
|
|
15
|
+
* ─── The one endpoint that matters ─────────────────────────────────────────
|
|
16
|
+
* POST /webhooks/stripe
|
|
17
|
+
* ↑ Stripe calls this in real time for every card event.
|
|
18
|
+
* We verify the signature, parse the event, and route it to a handler.
|
|
19
|
+
* For authorization requests, we return { approved: true/false }.
|
|
20
|
+
*
|
|
21
|
+
* ─── Security ───────────────────────────────────────────────────────────────
|
|
22
|
+
* Stripe signs every webhook with HMAC-SHA256 using a shared secret.
|
|
23
|
+
* We verify this signature before processing ANYTHING. An invalid signature
|
|
24
|
+
* means the request didn't come from Stripe — we reject it with a 400.
|
|
25
|
+
*
|
|
26
|
+
* ─── Running the server ─────────────────────────────────────────────────────
|
|
27
|
+
* Set env vars and start:
|
|
28
|
+
* STRIPE_SECRET_KEY=sk_test_... \
|
|
29
|
+
* STRIPE_WEBHOOK_SECRET=whsec_... \
|
|
30
|
+
* PORT=3000 \
|
|
31
|
+
* node dist/index.js
|
|
32
|
+
*
|
|
33
|
+
* Get your webhook secret from the Stripe CLI:
|
|
34
|
+
* stripe listen --forward-to localhost:3000/webhooks/stripe
|
|
35
|
+
*/
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.createWebhookApp = createWebhookApp;
|
|
41
|
+
exports.startWebhookServer = startWebhookServer;
|
|
42
|
+
const node_server_1 = require("@hono/node-server");
|
|
43
|
+
const hono_1 = require("hono");
|
|
44
|
+
const stripe_1 = __importDefault(require("stripe"));
|
|
45
|
+
const core_1 = require("@opencard-dev/core");
|
|
46
|
+
// ─── Configuration ────────────────────────────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* The port this server listens on. Default 3000.
|
|
49
|
+
* Override with the PORT environment variable.
|
|
50
|
+
*/
|
|
51
|
+
const PORT = parseInt(process.env.PORT || '3000', 10);
|
|
52
|
+
/**
|
|
53
|
+
* Your Stripe secret key. Used to initialize the Stripe SDK so we can
|
|
54
|
+
* call Stripe's API (e.g., to update card status after a decision).
|
|
55
|
+
* Must start with sk_test_... for test mode, sk_live_... for production.
|
|
56
|
+
*/
|
|
57
|
+
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY;
|
|
58
|
+
/**
|
|
59
|
+
* The webhook signing secret Stripe uses to sign webhook payloads.
|
|
60
|
+
* Get this from:
|
|
61
|
+
* - Stripe CLI: `stripe listen` prints it on first run
|
|
62
|
+
* - Stripe Dashboard: Developers → Webhooks → your endpoint → Signing secret
|
|
63
|
+
*
|
|
64
|
+
* Without this, we can't verify that webhook requests actually came from Stripe.
|
|
65
|
+
* The server will still start without it (for ease of local dev), but will
|
|
66
|
+
* print a clear warning and skip verification.
|
|
67
|
+
*/
|
|
68
|
+
const STRIPE_WEBHOOK_SECRET = process.env.STRIPE_WEBHOOK_SECRET;
|
|
69
|
+
// ─── Initialize Stripe (deferred) ──────────────────────────────────────────────
|
|
70
|
+
/**
|
|
71
|
+
* The Stripe SDK instance. Created lazily when the server starts.
|
|
72
|
+
* This allows the module to be imported without requiring STRIPE_SECRET_KEY.
|
|
73
|
+
*/
|
|
74
|
+
let stripe = null;
|
|
75
|
+
// ─── Server State ───────────────────────────────────────────────────────────
|
|
76
|
+
/**
|
|
77
|
+
* Track when the server started so we can report uptime in health checks.
|
|
78
|
+
*/
|
|
79
|
+
const startTime = Date.now();
|
|
80
|
+
// ─── Hono App ─────────────────────────────────────────────────────────────────
|
|
81
|
+
/**
|
|
82
|
+
* Create the Hono application.
|
|
83
|
+
* Hono is structured as an app with routes — similar to Express but more
|
|
84
|
+
* TypeScript-friendly. Each route is defined with app.get/post/etc.
|
|
85
|
+
*/
|
|
86
|
+
const app = new hono_1.Hono();
|
|
87
|
+
// ─── Health check ─────────────────────────────────────────────────────────────
|
|
88
|
+
/**
|
|
89
|
+
* GET /health
|
|
90
|
+
* A simple endpoint that returns 200 OK.
|
|
91
|
+
* Used by monitoring systems, load balancers, and developers to confirm
|
|
92
|
+
* the server is up and responding. The webhook doesn't do any Stripe logic here.
|
|
93
|
+
*/
|
|
94
|
+
app.get('/health', (c) => {
|
|
95
|
+
const uptime = Math.floor((Date.now() - startTime) / 1000); // seconds
|
|
96
|
+
return c.json({
|
|
97
|
+
status: 'ok',
|
|
98
|
+
service: 'opencard-webhook-server',
|
|
99
|
+
version: '0.1.0',
|
|
100
|
+
uptime, // seconds
|
|
101
|
+
timestamp: new Date().toISOString(),
|
|
102
|
+
// Tell the caller whether signature verification is active.
|
|
103
|
+
// If this shows false in production, something is wrong.
|
|
104
|
+
signatureVerification: !!STRIPE_WEBHOOK_SECRET,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
// ─── Webhook handler ──────────────────────────────────────────────────────────
|
|
108
|
+
/**
|
|
109
|
+
* POST /webhooks/stripe
|
|
110
|
+
* ──────────────────────
|
|
111
|
+
* The main endpoint. Stripe calls this for every Issuing event.
|
|
112
|
+
*
|
|
113
|
+
* IMPORTANT: Stripe signature verification requires the raw request body
|
|
114
|
+
* (the exact bytes Stripe sent), not a parsed JSON object. If we let Hono
|
|
115
|
+
* parse the body first, the byte-for-byte signature check will fail.
|
|
116
|
+
* That's why we read `req.arrayBuffer()` and convert to text manually.
|
|
117
|
+
*/
|
|
118
|
+
app.post('/webhooks/stripe', async (c) => {
|
|
119
|
+
// ── Step 1: Read the raw request body ────────────────────────────────────
|
|
120
|
+
//
|
|
121
|
+
// We need the raw bytes for signature verification. Hono doesn't parse
|
|
122
|
+
// the body automatically, so we read it ourselves.
|
|
123
|
+
const rawBody = await c.req.text();
|
|
124
|
+
// ── Step 2: Get the Stripe signature header ───────────────────────────────
|
|
125
|
+
//
|
|
126
|
+
// Stripe attaches a `Stripe-Signature` header to every webhook request.
|
|
127
|
+
// It contains a timestamp and an HMAC signature computed from the body.
|
|
128
|
+
// We need this to verify the request is authentic.
|
|
129
|
+
const signature = c.req.header('stripe-signature');
|
|
130
|
+
if (!signature) {
|
|
131
|
+
// No signature header at all — definitely not a legitimate Stripe request.
|
|
132
|
+
console.warn('[Webhook] Request missing Stripe-Signature header — rejecting');
|
|
133
|
+
return c.json({ error: 'Missing Stripe-Signature header' }, 400);
|
|
134
|
+
}
|
|
135
|
+
// ── Step 3: Verify the signature (if we have a secret) ───────────────────
|
|
136
|
+
//
|
|
137
|
+
// stripe.webhooks.constructEvent() does three things:
|
|
138
|
+
// 1. Parses the raw body as JSON to get the event object
|
|
139
|
+
// 2. Verifies the HMAC signature using our webhook secret
|
|
140
|
+
// 3. Checks that the timestamp isn't too old (prevents replay attacks)
|
|
141
|
+
//
|
|
142
|
+
// If any of those checks fail, it throws an error.
|
|
143
|
+
let event;
|
|
144
|
+
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
|
|
145
|
+
if (!stripe) {
|
|
146
|
+
console.error('[Webhook] Stripe not initialized — server not started with startWebhookServer()');
|
|
147
|
+
return c.json({ error: 'Server not initialized' }, 500);
|
|
148
|
+
}
|
|
149
|
+
if (webhookSecret) {
|
|
150
|
+
try {
|
|
151
|
+
event = stripe.webhooks.constructEvent(rawBody, signature, webhookSecret);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
// Signature invalid or timestamp too old. This could mean:
|
|
155
|
+
// - Someone is trying to send fake events (attack)
|
|
156
|
+
// - The webhook secret is misconfigured
|
|
157
|
+
// - The request was somehow corrupted in transit
|
|
158
|
+
console.error('[Webhook] Signature verification failed:', err instanceof Error ? err.message : err);
|
|
159
|
+
return c.json({ error: 'Webhook signature verification failed' }, 400);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// No webhook secret configured — skip verification.
|
|
164
|
+
// Parse the body directly. Only acceptable in local development.
|
|
165
|
+
try {
|
|
166
|
+
event = JSON.parse(rawBody);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return c.json({ error: 'Invalid JSON body' }, 400);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// ── Step 4: Route the event to the right handler ─────────────────────────
|
|
173
|
+
//
|
|
174
|
+
// Stripe sends different event types for different things. We pattern-match
|
|
175
|
+
// on `event.type` to decide what to do.
|
|
176
|
+
//
|
|
177
|
+
// Reference: https://stripe.com/docs/api/events/types
|
|
178
|
+
console.log(`[Webhook] Received event: ${event.type} (id: ${event.id})`);
|
|
179
|
+
switch (event.type) {
|
|
180
|
+
// ── issuing_authorization.request ──────────────────────────────────────
|
|
181
|
+
//
|
|
182
|
+
// THE critical real-time event. Stripe sends this synchronously when a
|
|
183
|
+
// card is presented for payment. We MUST respond within ~2 seconds with
|
|
184
|
+
// our approve/decline decision.
|
|
185
|
+
//
|
|
186
|
+
// The response body is what Stripe actually uses to decide:
|
|
187
|
+
// { approved: true } → Stripe approves the charge
|
|
188
|
+
// { approved: false } → Stripe declines the charge
|
|
189
|
+
//
|
|
190
|
+
// Note: For this event type, Stripe expects a specific response format.
|
|
191
|
+
// Any non-200 response or a response missing `approved` will cause
|
|
192
|
+
// Stripe to decline by default.
|
|
193
|
+
case 'issuing_authorization.request': {
|
|
194
|
+
const authorization = event.data.object;
|
|
195
|
+
// handleAuthorizationRequest is now async (it looks up rules from the store)
|
|
196
|
+
const result = await (0, core_1.handleAuthorizationRequest)(authorization);
|
|
197
|
+
// Return the decision to Stripe. This is the actual approve/decline.
|
|
198
|
+
return c.json({ approved: result.approved });
|
|
199
|
+
}
|
|
200
|
+
// ── issuing_authorization.created ──────────────────────────────────────
|
|
201
|
+
//
|
|
202
|
+
// Fired AFTER an authorization is definitively approved or declined.
|
|
203
|
+
// This is NOT the real-time request — it's a notification after the fact.
|
|
204
|
+
// We log it but don't need to return any special response.
|
|
205
|
+
case 'issuing_authorization.created': {
|
|
206
|
+
const authorization = event.data.object;
|
|
207
|
+
(0, core_1.handleAuthorizationCreated)(authorization);
|
|
208
|
+
return c.json({ received: true });
|
|
209
|
+
}
|
|
210
|
+
// ── issuing_transaction.created ────────────────────────────────────────
|
|
211
|
+
//
|
|
212
|
+
// Fired when a transaction is fully captured (money actually moved).
|
|
213
|
+
// This can happen hours or days after the authorization, and the final
|
|
214
|
+
// amount may differ from the authorization amount (e.g. tips at restaurants).
|
|
215
|
+
case 'issuing_transaction.created': {
|
|
216
|
+
const transaction = event.data.object;
|
|
217
|
+
(0, core_1.handleTransactionCreated)(transaction);
|
|
218
|
+
return c.json({ received: true });
|
|
219
|
+
}
|
|
220
|
+
// ── issuing_card.updated ───────────────────────────────────────────────
|
|
221
|
+
//
|
|
222
|
+
// Fired when a card's status, spending limits, or metadata changes.
|
|
223
|
+
// Useful for tracking when agents' cards are paused or resumed.
|
|
224
|
+
case 'issuing_card.updated': {
|
|
225
|
+
const card = event.data.object;
|
|
226
|
+
(0, core_1.handleCardUpdated)(card);
|
|
227
|
+
return c.json({ received: true });
|
|
228
|
+
}
|
|
229
|
+
// ── Unhandled event types ──────────────────────────────────────────────
|
|
230
|
+
//
|
|
231
|
+
// Stripe may send many other event types. We acknowledge receipt (200 OK)
|
|
232
|
+
// without doing anything — this prevents Stripe from retrying the event.
|
|
233
|
+
// Silently ignoring is intentional; we only care about Issuing events.
|
|
234
|
+
default:
|
|
235
|
+
console.log(`[Webhook] Unhandled event type: ${event.type} — acknowledged and ignored`);
|
|
236
|
+
return c.json({ received: true });
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
// ─── Approval API endpoints ───────────────────────────────────────────────────
|
|
240
|
+
/**
|
|
241
|
+
* GET /approvals
|
|
242
|
+
* List pending approval requests. Useful for building custom approval UIs,
|
|
243
|
+
* Slack bots, or mobile apps that show pending requests.
|
|
244
|
+
*/
|
|
245
|
+
app.get('/approvals', (c) => {
|
|
246
|
+
try {
|
|
247
|
+
const db = new core_1.OpenCardDatabase();
|
|
248
|
+
const pending = db.listPendingApprovalRequests();
|
|
249
|
+
db.close();
|
|
250
|
+
return c.json({ pending });
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
console.error('[Approvals] Error listing requests:', err);
|
|
254
|
+
return c.json({ error: 'Failed to list approval requests' }, 500);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
/**
|
|
258
|
+
* POST /approvals/:id/approve
|
|
259
|
+
* Approve a pending request. Body: { decided_by: string, note?: string }
|
|
260
|
+
*/
|
|
261
|
+
app.post('/approvals/:id/approve', async (c) => {
|
|
262
|
+
const id = c.req.param('id');
|
|
263
|
+
try {
|
|
264
|
+
const body = await c.req.json();
|
|
265
|
+
const decidedBy = body.decided_by || 'api';
|
|
266
|
+
const db = new core_1.OpenCardDatabase();
|
|
267
|
+
const request = db.approveRequest(id, decidedBy, body.note);
|
|
268
|
+
db.close();
|
|
269
|
+
console.log(`[Approvals] Approved request ${id} by ${decidedBy}`);
|
|
270
|
+
return c.json({
|
|
271
|
+
status: 'approved',
|
|
272
|
+
request_id: request.id,
|
|
273
|
+
approved_by: request.decided_by,
|
|
274
|
+
note: request.decision_note ?? null,
|
|
275
|
+
approved_at: request.decided_at,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
280
|
+
console.error(`[Approvals] Error approving ${id}:`, message);
|
|
281
|
+
if (message.includes('not found'))
|
|
282
|
+
return c.json({ error: message }, 404);
|
|
283
|
+
if (message.includes('already'))
|
|
284
|
+
return c.json({ error: message }, 409);
|
|
285
|
+
return c.json({ error: 'Failed to approve request' }, 500);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
/**
|
|
289
|
+
* POST /approvals/:id/deny
|
|
290
|
+
* Deny a pending request. Body: { decided_by: string, note?: string }
|
|
291
|
+
*/
|
|
292
|
+
app.post('/approvals/:id/deny', async (c) => {
|
|
293
|
+
const id = c.req.param('id');
|
|
294
|
+
try {
|
|
295
|
+
const body = await c.req.json();
|
|
296
|
+
const decidedBy = body.decided_by || 'api';
|
|
297
|
+
const db = new core_1.OpenCardDatabase();
|
|
298
|
+
const request = db.denyRequest(id, decidedBy, body.note);
|
|
299
|
+
db.close();
|
|
300
|
+
console.log(`[Approvals] Denied request ${id} by ${decidedBy}`);
|
|
301
|
+
return c.json({
|
|
302
|
+
status: 'denied',
|
|
303
|
+
request_id: request.id,
|
|
304
|
+
denied_by: request.decided_by,
|
|
305
|
+
note: request.decision_note ?? null,
|
|
306
|
+
denied_at: request.decided_at,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
311
|
+
console.error(`[Approvals] Error denying ${id}:`, message);
|
|
312
|
+
if (message.includes('not found'))
|
|
313
|
+
return c.json({ error: message }, 404);
|
|
314
|
+
if (message.includes('already'))
|
|
315
|
+
return c.json({ error: message }, 409);
|
|
316
|
+
return c.json({ error: 'Failed to deny request' }, 500);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
// ─── Initialize Database ─────────────────────────────────────────────────────
|
|
320
|
+
/**
|
|
321
|
+
* Initialize the SQLite database and wire it into the reconciler.
|
|
322
|
+
* This runs at startup, before the server begins listening.
|
|
323
|
+
* If DB init fails, we log a warning and continue without persistence —
|
|
324
|
+
* the auth decision path must never be blocked by DB availability.
|
|
325
|
+
*/
|
|
326
|
+
try {
|
|
327
|
+
const db = (0, core_1.initDatabase)();
|
|
328
|
+
const reconciler = new core_1.TransactionReconciler(db);
|
|
329
|
+
(0, core_1.setReconciler)(reconciler);
|
|
330
|
+
console.log('[WebhookServer] SQLite database initialized and reconciler wired ✓');
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
console.error('[WebhookServer] ⚠ Failed to initialize SQLite database — webhook events will NOT be persisted.');
|
|
334
|
+
console.error('[WebhookServer] ⚠ Auth decisions are unaffected. Fix the DB issue to restore audit trail.');
|
|
335
|
+
console.error('[WebhookServer] DB init error:', err instanceof Error ? err.message : String(err));
|
|
336
|
+
}
|
|
337
|
+
// ─── Export app creation and server startup functions ─────────────────────────
|
|
338
|
+
/**
|
|
339
|
+
* Create and return the Hono webhook application.
|
|
340
|
+
* This is exported so the serve command can reuse the same app.
|
|
341
|
+
*/
|
|
342
|
+
function createWebhookApp() {
|
|
343
|
+
return app;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Start the webhook server on the specified port.
|
|
347
|
+
* Returns a promise that resolves when the server is ready.
|
|
348
|
+
*/
|
|
349
|
+
function startWebhookServer(port) {
|
|
350
|
+
return new Promise((resolve, reject) => {
|
|
351
|
+
// ── Validate and initialize Stripe ──────────────────────────────────────
|
|
352
|
+
const stripeKey = process.env.STRIPE_SECRET_KEY;
|
|
353
|
+
if (!stripeKey) {
|
|
354
|
+
console.error('[WebhookServer] FATAL: STRIPE_SECRET_KEY environment variable is not set.');
|
|
355
|
+
console.error('[WebhookServer] Set it with: export STRIPE_SECRET_KEY=sk_test_...');
|
|
356
|
+
reject(new Error('STRIPE_SECRET_KEY not set'));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
if (!stripe) {
|
|
360
|
+
stripe = new stripe_1.default(stripeKey, {
|
|
361
|
+
apiVersion: '2023-10-16',
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
|
|
365
|
+
if (!webhookSecret) {
|
|
366
|
+
console.warn('[WebhookServer] ⚠ WARNING: STRIPE_WEBHOOK_SECRET not set — signature verification DISABLED');
|
|
367
|
+
console.warn('[WebhookServer] ⚠ This is insecure. Only acceptable for local development.');
|
|
368
|
+
console.warn('[WebhookServer] ⚠ Get your secret from: stripe listen --forward-to localhost:3000/webhooks/stripe');
|
|
369
|
+
}
|
|
370
|
+
// ── Start server ────────────────────────────────────────────────────────
|
|
371
|
+
console.log('[WebhookServer] OpenCard webhook server starting...');
|
|
372
|
+
console.log(`[WebhookServer] Stripe signature verification: ${webhookSecret ? 'ENABLED ✓' : 'DISABLED ⚠'}`);
|
|
373
|
+
(0, node_server_1.serve)({
|
|
374
|
+
fetch: app.fetch,
|
|
375
|
+
port: port,
|
|
376
|
+
}, (info) => {
|
|
377
|
+
console.log(`[WebhookServer] Server listening on port ${info.port}`);
|
|
378
|
+
console.log(`[WebhookServer] Webhook endpoint: POST http://localhost:${info.port}/webhooks/stripe`);
|
|
379
|
+
console.log(`[WebhookServer] Health check: GET http://localhost:${info.port}/health`);
|
|
380
|
+
console.log('[WebhookServer] Ready to handle Stripe events.');
|
|
381
|
+
resolve();
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
// ─── Auto-start when run directly ──────────────────────────────────────────────
|
|
386
|
+
// Note: This is now a library module imported by the CLI.
|
|
387
|
+
// The CLI uses startWebhookServer() to control when the server starts.
|
|
388
|
+
// Auto-start is no longer supported from the library itself.
|
|
389
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opencard-dev/webhook-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Hono-based webhook server for real-time Stripe Issuing authorization decisions",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"type-check": "tsc --noEmit",
|
|
9
|
+
"dev": "tsc --watch",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"test": "vitest run"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@hono/node-server": "^1.13.8",
|
|
15
|
+
"@opencard-dev/core": "workspace:*",
|
|
16
|
+
"hono": "^4.7.7",
|
|
17
|
+
"stripe": "^14.0.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^20.0.0",
|
|
21
|
+
"typescript": "^5.3.0",
|
|
22
|
+
"vitest": "^1.0.0"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"webhook",
|
|
29
|
+
"stripe",
|
|
30
|
+
"hono",
|
|
31
|
+
"opencard"
|
|
32
|
+
],
|
|
33
|
+
"author": "Longshot Design",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/JLongshot/opencard.git",
|
|
38
|
+
"directory": "packages/webhook-server"
|
|
39
|
+
}
|
|
40
|
+
}
|