@opencard-dev/core 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/db.d.ts +145 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +373 -0
- package/dist/db.js.map +1 -0
- package/dist/errors.d.ts +72 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +124 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +53 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +109 -0
- package/dist/logger.js.map +1 -0
- package/dist/rules-engine.d.ts +159 -0
- package/dist/rules-engine.d.ts.map +1 -0
- package/dist/rules-engine.js +375 -0
- package/dist/rules-engine.js.map +1 -0
- package/dist/rules-store.d.ts +187 -0
- package/dist/rules-store.d.ts.map +1 -0
- package/dist/rules-store.js +291 -0
- package/dist/rules-store.js.map +1 -0
- package/dist/rules-validation.d.ts +54 -0
- package/dist/rules-validation.d.ts.map +1 -0
- package/dist/rules-validation.js +110 -0
- package/dist/rules-validation.js.map +1 -0
- package/dist/stripe-client.d.ts +154 -0
- package/dist/stripe-client.d.ts.map +1 -0
- package/dist/stripe-client.js +444 -0
- package/dist/stripe-client.js.map +1 -0
- package/dist/test-utils.d.ts +55 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +91 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/tracker.d.ts +130 -0
- package/dist/tracker.d.ts.map +1 -0
- package/dist/tracker.js +196 -0
- package/dist/tracker.js.map +1 -0
- package/dist/transaction-reconciler.d.ts +30 -0
- package/dist/transaction-reconciler.d.ts.map +1 -0
- package/dist/transaction-reconciler.js +131 -0
- package/dist/transaction-reconciler.js.map +1 -0
- package/dist/types.d.ts +194 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/webhooks.d.ts +121 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +307 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* StripeClient
|
|
4
|
+
* ============
|
|
5
|
+
* A clean, typed wrapper around Stripe's Issuing API.
|
|
6
|
+
*
|
|
7
|
+
* Stripe's Issuing product lets you create virtual (and physical) payment cards
|
|
8
|
+
* programmatically. OpenCard uses it to give AI agents their own cards with
|
|
9
|
+
* spending controls attached.
|
|
10
|
+
*
|
|
11
|
+
* ─── Why a wrapper? ─────────────────────────────────────────────────────────
|
|
12
|
+
* The raw Stripe SDK is powerful but returns Stripe's own types, which don't
|
|
13
|
+
* always match what OpenCard needs. This wrapper:
|
|
14
|
+
* - Converts Stripe's snake_case fields to camelCase (matches our TypeScript style)
|
|
15
|
+
* - Maps Stripe's types to our internal interfaces (Card, Cardholder, etc.)
|
|
16
|
+
* - Centralizes error handling in one place
|
|
17
|
+
* - Makes the API surface smaller and easier to understand
|
|
18
|
+
*
|
|
19
|
+
* ─── Stripe API version ─────────────────────────────────────────────────────
|
|
20
|
+
* We pin to "2023-10-16". If you upgrade the Stripe SDK, check if the API
|
|
21
|
+
* version needs updating and whether any field names/types changed.
|
|
22
|
+
*
|
|
23
|
+
* ─── Test vs. Live mode ─────────────────────────────────────────────────────
|
|
24
|
+
* Stripe uses the key prefix to determine mode:
|
|
25
|
+
* sk_test_... → test mode (no real money, safe to experiment)
|
|
26
|
+
* sk_live_... → live mode (REAL money — be careful!)
|
|
27
|
+
* Never commit a live key to git.
|
|
28
|
+
*/
|
|
29
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
30
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
31
|
+
};
|
|
32
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
+
exports.StripeClient = void 0;
|
|
34
|
+
const stripe_1 = __importDefault(require("stripe"));
|
|
35
|
+
const rules_store_1 = require("./rules-store");
|
|
36
|
+
const errors_1 = require("./errors");
|
|
37
|
+
class StripeClient {
|
|
38
|
+
/** The underlying Stripe SDK instance. Use this for any Stripe calls not yet wrapped here. */
|
|
39
|
+
stripe;
|
|
40
|
+
/** Stored config so we can reference it in later operations. */
|
|
41
|
+
config;
|
|
42
|
+
/**
|
|
43
|
+
* Creates a new StripeClient.
|
|
44
|
+
*
|
|
45
|
+
* @param config - Optional configuration. If omitted, reads from environment variables.
|
|
46
|
+
*
|
|
47
|
+
* @throws Error if no API key is found (neither in config nor in STRIPE_SECRET_KEY env var)
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // Use environment variable (recommended for production)
|
|
51
|
+
* const client = new StripeClient();
|
|
52
|
+
*
|
|
53
|
+
* // Pass key directly (good for testing)
|
|
54
|
+
* const client = new StripeClient({ secretKey: 'sk_test_...' });
|
|
55
|
+
*/
|
|
56
|
+
constructor(config = {}) {
|
|
57
|
+
// The API key can come from either the config object or the environment variable.
|
|
58
|
+
// If neither is set, we throw early with a helpful message.
|
|
59
|
+
const secretKey = config.secretKey || process.env.STRIPE_SECRET_KEY;
|
|
60
|
+
// Check if key is missing or empty
|
|
61
|
+
if (!secretKey || secretKey.trim() === '') {
|
|
62
|
+
throw new errors_1.StripeAuthenticationError('STRIPE_SECRET_KEY not set. Get your test key from https://dashboard.stripe.com/apikeys', { source: 'config' });
|
|
63
|
+
}
|
|
64
|
+
// Validate key format
|
|
65
|
+
const trimmedKey = secretKey.trim();
|
|
66
|
+
if (!trimmedKey.startsWith('sk_test_') && !trimmedKey.startsWith('sk_live_')) {
|
|
67
|
+
console.warn('STRIPE_SECRET_KEY looks invalid — test keys start with sk_test_, live keys with sk_live_');
|
|
68
|
+
}
|
|
69
|
+
// Initialize the Stripe SDK. The apiVersion pin ensures consistent behavior
|
|
70
|
+
// even if the SDK package is updated.
|
|
71
|
+
this.stripe = new stripe_1.default(trimmedKey, {
|
|
72
|
+
apiVersion: '2023-10-16',
|
|
73
|
+
});
|
|
74
|
+
this.config = {
|
|
75
|
+
secretKey: trimmedKey,
|
|
76
|
+
// defaultRules is optional — if not provided, use an empty rule set
|
|
77
|
+
defaultRules: config.defaultRules ?? {},
|
|
78
|
+
apiVersion: config.apiVersion || '2023-10-16',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// ─── Cardholders ────────────────────────────────────────────────────────────
|
|
82
|
+
/**
|
|
83
|
+
* Creates a new cardholder in Stripe Issuing.
|
|
84
|
+
*
|
|
85
|
+
* A cardholder is the entity that "owns" the cards. In a typical OpenCard
|
|
86
|
+
* setup, you'd create one cardholder per agent or per project, then create
|
|
87
|
+
* multiple cards under that cardholder.
|
|
88
|
+
*
|
|
89
|
+
* Stripe requires a billing address for cardholders in most configurations.
|
|
90
|
+
* In test mode this requirement may be relaxed.
|
|
91
|
+
*
|
|
92
|
+
* @param name - Display name for the cardholder (e.g. "Atlas Agent", "Research Bot")
|
|
93
|
+
* @param email - Contact email (used by Stripe for notifications)
|
|
94
|
+
* @param billing - Billing address (required for live mode)
|
|
95
|
+
* @returns The created Cardholder object
|
|
96
|
+
*/
|
|
97
|
+
async createCardholder(name, email, billing) {
|
|
98
|
+
try {
|
|
99
|
+
const cardholder = await this.stripe.issuing.cardholders.create({
|
|
100
|
+
name,
|
|
101
|
+
email,
|
|
102
|
+
type: 'individual', // Stripe supports 'individual' and 'company'
|
|
103
|
+
billing: billing
|
|
104
|
+
? {
|
|
105
|
+
address: {
|
|
106
|
+
line1: billing.line1,
|
|
107
|
+
line2: billing.line2,
|
|
108
|
+
city: billing.city,
|
|
109
|
+
state: billing.state,
|
|
110
|
+
postal_code: billing.postalCode,
|
|
111
|
+
country: billing.country,
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
: {
|
|
115
|
+
// Stripe requires a billing field even if minimal.
|
|
116
|
+
// In test mode, a placeholder address is acceptable.
|
|
117
|
+
address: {
|
|
118
|
+
line1: '354 Oyster Point Blvd',
|
|
119
|
+
city: 'South San Francisco',
|
|
120
|
+
state: 'CA',
|
|
121
|
+
postal_code: '94080',
|
|
122
|
+
country: 'US',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
status: 'active',
|
|
126
|
+
});
|
|
127
|
+
// Map Stripe's response to our internal Cardholder type.
|
|
128
|
+
// Note: Stripe's `email` field can be null, so we fall back to the
|
|
129
|
+
// input email (which we know is a string since the caller passed it).
|
|
130
|
+
return {
|
|
131
|
+
id: cardholder.id,
|
|
132
|
+
name: cardholder.name,
|
|
133
|
+
email: cardholder.email ?? email,
|
|
134
|
+
billing,
|
|
135
|
+
createdAt: new Date(cardholder.created * 1000), // Stripe uses Unix timestamps (seconds)
|
|
136
|
+
status: 'active',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
throw (0, errors_1.wrapStripeError)(error, 'Failed to create cardholder', 'stripe-client', { name, email });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// ─── Cards ──────────────────────────────────────────────────────────────────
|
|
144
|
+
/**
|
|
145
|
+
* Creates a new virtual card for an agent.
|
|
146
|
+
*
|
|
147
|
+
* The card is issued under the specified cardholder. Spending limits can be
|
|
148
|
+
* applied either via Stripe's native spending_controls (enforced at the
|
|
149
|
+
* Stripe level, last-resort safeguard) or via OpenCard's rules engine
|
|
150
|
+
* (enforced via the webhook server, smarter but requires the server to be running).
|
|
151
|
+
*
|
|
152
|
+
* The `agentName` is stored in card metadata so you can identify which
|
|
153
|
+
* agent this card belongs to when reviewing Stripe dashboard or logs.
|
|
154
|
+
*
|
|
155
|
+
* @param cardholderId - The Stripe cardholder ID (e.g. "ich_abc123")
|
|
156
|
+
* @param options - Card creation options (name, rules, status, metadata)
|
|
157
|
+
* @returns The created Card object
|
|
158
|
+
*/
|
|
159
|
+
async createCard(cardholderId, options) {
|
|
160
|
+
try {
|
|
161
|
+
// ── Resolve the rule ID ───────────────────────────────────────────────
|
|
162
|
+
// We want to store only a rule reference ID in Stripe metadata, not the
|
|
163
|
+
// full rule JSON. This prevents anyone with Stripe dashboard access from
|
|
164
|
+
// modifying rules by editing card metadata.
|
|
165
|
+
//
|
|
166
|
+
// Three cases:
|
|
167
|
+
// 1. Caller passed `ruleId` directly → use it as-is
|
|
168
|
+
// 2. Caller passed `rules` (inline) without a ruleId → auto-create in store
|
|
169
|
+
// 3. Neither passed → no rules on this card
|
|
170
|
+
let resolvedRuleId = options.ruleId;
|
|
171
|
+
if (!resolvedRuleId && options.rules) {
|
|
172
|
+
// Auto-migrate: create the inline rules in the store and use the returned ID.
|
|
173
|
+
// This maintains backward compatibility — callers don't HAVE to know about
|
|
174
|
+
// the rules store, it just happens automatically.
|
|
175
|
+
console.log(`[StripeClient] createCard: inline rules provided without ruleId — auto-creating in rules store`);
|
|
176
|
+
resolvedRuleId = await rules_store_1.rulesStore.createRule(options.rules);
|
|
177
|
+
console.log(`[StripeClient] createCard: auto-created rule ${resolvedRuleId} for card under cardholder ${cardholderId}`);
|
|
178
|
+
}
|
|
179
|
+
const card = await this.stripe.issuing.cards.create({
|
|
180
|
+
cardholder: cardholderId,
|
|
181
|
+
currency: 'usd',
|
|
182
|
+
type: 'virtual', // We only create virtual cards (no physical plastic)
|
|
183
|
+
status: options.status || 'active',
|
|
184
|
+
// spending_controls are Stripe-native limits — they're enforced even if
|
|
185
|
+
// our webhook server is down. Think of them as the safety net under the net.
|
|
186
|
+
spending_controls: options.spendingLimits
|
|
187
|
+
? {
|
|
188
|
+
spending_limits: [
|
|
189
|
+
{
|
|
190
|
+
amount: options.spendingLimits.amount,
|
|
191
|
+
interval: options.spendingLimits.intervalType,
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
}
|
|
195
|
+
: undefined,
|
|
196
|
+
metadata: {
|
|
197
|
+
// Store the agent name so we can identify this card in Stripe's dashboard
|
|
198
|
+
// and in our webhook logs.
|
|
199
|
+
agentName: options.agentName,
|
|
200
|
+
// Store only the rule reference ID, not the full rule JSON.
|
|
201
|
+
// The webhook server will look up the actual rule from our rules store.
|
|
202
|
+
// This is the core security improvement: Stripe metadata no longer holds
|
|
203
|
+
// the actual rules, so modifying metadata cannot change spending behavior.
|
|
204
|
+
...(resolvedRuleId ? { opencard_rule_id: resolvedRuleId } : {}),
|
|
205
|
+
// Human-readable description of what this card is for. Helps agents
|
|
206
|
+
// pick the right card at purchase time without guessing from limits alone.
|
|
207
|
+
...(options.description ? { opencard_description: options.description } : {}),
|
|
208
|
+
// Spread any caller-provided custom metadata (project IDs, tags, etc.)
|
|
209
|
+
...options.metadata,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
return mapCardResponse(card, cardholderId, options);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
throw (0, errors_1.wrapStripeError)(error, 'Failed to create card', 'stripe-client', { cardholderId, ruleId: options.ruleId });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Retrieves a card by its Stripe card ID.
|
|
220
|
+
* Useful for checking current status, limits, and metadata.
|
|
221
|
+
*
|
|
222
|
+
* @param cardId - Stripe card ID (e.g. "ic_abc123")
|
|
223
|
+
*/
|
|
224
|
+
async getCard(cardId) {
|
|
225
|
+
try {
|
|
226
|
+
const card = await this.stripe.issuing.cards.retrieve(cardId);
|
|
227
|
+
const cardholderId = typeof card.cardholder === 'string'
|
|
228
|
+
? card.cardholder
|
|
229
|
+
: card.cardholder?.id || '';
|
|
230
|
+
return mapCardResponse(card, cardholderId, {
|
|
231
|
+
agentName: card.metadata?.agentName || 'unknown',
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
throw (0, errors_1.wrapStripeError)(error, 'Failed to get card', 'stripe-client', { cardId });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Pauses a card by setting its status to 'inactive'.
|
|
240
|
+
*
|
|
241
|
+
* A paused card will decline all new charges but can be resumed later.
|
|
242
|
+
* This is different from canceling — canceled cards cannot be reactivated.
|
|
243
|
+
*
|
|
244
|
+
* Use this when an agent has violated rules and needs to be stopped temporarily,
|
|
245
|
+
* or when you want to manually review spending before re-enabling.
|
|
246
|
+
*
|
|
247
|
+
* @param cardId - Stripe card ID to pause
|
|
248
|
+
*/
|
|
249
|
+
async pauseCard(cardId) {
|
|
250
|
+
try {
|
|
251
|
+
const card = await this.stripe.issuing.cards.update(cardId, {
|
|
252
|
+
status: 'inactive', // Stripe's term for "paused" on virtual cards
|
|
253
|
+
});
|
|
254
|
+
const cardholderId = typeof card.cardholder === 'string'
|
|
255
|
+
? card.cardholder
|
|
256
|
+
: card.cardholder?.id || '';
|
|
257
|
+
return mapCardResponse(card, cardholderId, {
|
|
258
|
+
agentName: card.metadata?.agentName || 'unknown',
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
throw (0, errors_1.wrapStripeError)(error, 'Failed to pause card', 'stripe-client', { cardId });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Resumes a paused card by setting its status back to 'active'.
|
|
267
|
+
* The card will accept new charges again immediately after this call.
|
|
268
|
+
*
|
|
269
|
+
* @param cardId - Stripe card ID to resume
|
|
270
|
+
*/
|
|
271
|
+
async resumeCard(cardId) {
|
|
272
|
+
try {
|
|
273
|
+
const card = await this.stripe.issuing.cards.update(cardId, {
|
|
274
|
+
status: 'active',
|
|
275
|
+
});
|
|
276
|
+
const cardholderId = typeof card.cardholder === 'string'
|
|
277
|
+
? card.cardholder
|
|
278
|
+
: card.cardholder?.id || '';
|
|
279
|
+
return mapCardResponse(card, cardholderId, {
|
|
280
|
+
agentName: card.metadata?.agentName || 'unknown',
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
throw (0, errors_1.wrapStripeError)(error, 'Failed to resume card', 'stripe-client', { cardId });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Updates the spending limits on a card.
|
|
289
|
+
*
|
|
290
|
+
* These are Stripe-native limits (not OpenCard rules). They're enforced at
|
|
291
|
+
* the Stripe level regardless of whether our webhook server is running.
|
|
292
|
+
* Good for setting hard floors that can never be bypassed.
|
|
293
|
+
*
|
|
294
|
+
* @param cardId - Stripe card ID
|
|
295
|
+
* @param limits - Array of spending limit objects
|
|
296
|
+
*/
|
|
297
|
+
async setSpendingLimits(cardId, limits) {
|
|
298
|
+
try {
|
|
299
|
+
const card = await this.stripe.issuing.cards.update(cardId, {
|
|
300
|
+
spending_controls: {
|
|
301
|
+
spending_limits: limits.map((limit) => ({
|
|
302
|
+
amount: limit.amount,
|
|
303
|
+
interval: limit.intervalType,
|
|
304
|
+
})),
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
const cardholderId = typeof card.cardholder === 'string'
|
|
308
|
+
? card.cardholder
|
|
309
|
+
: card.cardholder?.id || '';
|
|
310
|
+
return mapCardResponse(card, cardholderId, {
|
|
311
|
+
agentName: card.metadata?.agentName || 'unknown',
|
|
312
|
+
spendingLimits: limits[0], // Return the first limit for display purposes
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
throw (0, errors_1.wrapStripeError)(error, 'Failed to set spending limits', 'stripe-client', { cardId, limits });
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// ─── Transactions ────────────────────────────────────────────────────────────
|
|
320
|
+
/**
|
|
321
|
+
* Retrieves the transaction history for a card.
|
|
322
|
+
*
|
|
323
|
+
* Note: Stripe Issuing transactions are distinct from regular Stripe charges.
|
|
324
|
+
* They represent captured spending on issued cards.
|
|
325
|
+
*
|
|
326
|
+
* Note on Stripe v14 Transaction type: The `status` and `decline_reason` fields
|
|
327
|
+
* don't exist on Issuing.Transaction in the v14 type definitions (the transaction
|
|
328
|
+
* object represents *completed* captures, not pending authorizations). For
|
|
329
|
+
* pending/declined status, use the Issuing.Authorization type instead.
|
|
330
|
+
*
|
|
331
|
+
* @param cardId - Stripe card ID
|
|
332
|
+
* @param options - Optional filters (limit, date range, etc.)
|
|
333
|
+
*/
|
|
334
|
+
async getTransactions(cardId, options) {
|
|
335
|
+
try {
|
|
336
|
+
const transactions = await this.stripe.issuing.transactions.list({
|
|
337
|
+
card: cardId,
|
|
338
|
+
limit: options?.limit || 100,
|
|
339
|
+
});
|
|
340
|
+
return transactions.data.map((tx) => ({
|
|
341
|
+
id: tx.id,
|
|
342
|
+
// tx.card can be either a card ID string or a full Card object
|
|
343
|
+
cardId: typeof tx.card === 'string' ? tx.card : tx.card?.id || '',
|
|
344
|
+
amount: tx.amount,
|
|
345
|
+
currency: tx.currency,
|
|
346
|
+
merchant: {
|
|
347
|
+
name: tx.merchant_data?.name || 'Unknown',
|
|
348
|
+
category: tx.merchant_data?.category || 'unknown',
|
|
349
|
+
// Stripe returns null for optional geo fields — convert to undefined
|
|
350
|
+
// for consistency with our Transaction type (which uses string | undefined)
|
|
351
|
+
city: tx.merchant_data?.city ?? undefined,
|
|
352
|
+
country: tx.merchant_data?.country ?? undefined,
|
|
353
|
+
},
|
|
354
|
+
// Issuing transactions represent completed captures, so status is always 'captured'
|
|
355
|
+
// unless the transaction was reversed.
|
|
356
|
+
status: tx.type === 'capture' ? 'captured' : 'reversed',
|
|
357
|
+
createdAt: new Date(tx.created * 1000),
|
|
358
|
+
metadata: tx.metadata || {},
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
throw (0, errors_1.wrapStripeError)(error, 'Failed to get transactions', 'stripe-client', { cardId });
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// ─── Balance ─────────────────────────────────────────────────────────────────
|
|
366
|
+
/**
|
|
367
|
+
* Returns a balance snapshot for this account.
|
|
368
|
+
*
|
|
369
|
+
* Note: Stripe Issuing doesn't expose a per-card balance API. The "balance"
|
|
370
|
+
* for virtual cards is determined by your spending limits, not a pre-loaded
|
|
371
|
+
* amount. This is a stub that returns zeroes.
|
|
372
|
+
*
|
|
373
|
+
* Phase 2 will compute this by summing transactions against spending limits.
|
|
374
|
+
*/
|
|
375
|
+
async getBalance() {
|
|
376
|
+
return {
|
|
377
|
+
available: 0,
|
|
378
|
+
reserved: 0,
|
|
379
|
+
spent: 0,
|
|
380
|
+
asOf: new Date(),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Returns pending authorizations for a card.
|
|
385
|
+
*
|
|
386
|
+
* Stripe doesn't provide a REST API for pending authorizations — they're
|
|
387
|
+
* delivered exclusively via webhooks. This stub exists for API completeness.
|
|
388
|
+
* Phase 1 uses the webhook server for real-time authorization handling.
|
|
389
|
+
*
|
|
390
|
+
* @param _cardId - Card ID (unused in Phase 1)
|
|
391
|
+
*/
|
|
392
|
+
async getPendingAuthorizations(_cardId) {
|
|
393
|
+
// In Phase 1, authorizations are handled via webhooks, not polling.
|
|
394
|
+
// The webhook server receives issuing_authorization.request events in real time.
|
|
395
|
+
// This endpoint is here for future use (Phase 2 may add a polling fallback).
|
|
396
|
+
return [];
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
exports.StripeClient = StripeClient;
|
|
400
|
+
// ─── Internal helpers ─────────────────────────────────────────────────────────
|
|
401
|
+
/**
|
|
402
|
+
* Converts a Stripe.Issuing.Card response into our internal Card type.
|
|
403
|
+
*
|
|
404
|
+
* This helper is used by multiple methods (createCard, getCard, pauseCard, etc.)
|
|
405
|
+
* to avoid duplicating the mapping logic. Any time the Stripe SDK returns a card
|
|
406
|
+
* object, we run it through this function to get our clean Card interface.
|
|
407
|
+
*
|
|
408
|
+
* @param card - Raw Stripe.Issuing.Card from the SDK
|
|
409
|
+
* @param cardholderId - The cardholder's ID string
|
|
410
|
+
* @param options - The original creation options (for agentName and rules)
|
|
411
|
+
*/
|
|
412
|
+
function mapCardResponse(card, cardholderId, options) {
|
|
413
|
+
// Stripe stores expiry as separate month/year integers.
|
|
414
|
+
// We combine them into MM/YY format (e.g. "03/28" for March 2028).
|
|
415
|
+
const expiry = `${String(card.exp_month).padStart(2, '0')}/${String(card.exp_year).slice(-2)}`;
|
|
416
|
+
// card.metadata is typed as Stripe.Metadata (Record<string, string>), but
|
|
417
|
+
// can be null in some edge cases. We cast to our expected shape.
|
|
418
|
+
const metadata = card.metadata;
|
|
419
|
+
return {
|
|
420
|
+
id: card.id,
|
|
421
|
+
last4: card.last4,
|
|
422
|
+
brand: 'visa', // Stripe Issuing only issues Visa cards (in the US)
|
|
423
|
+
expiry,
|
|
424
|
+
// Stripe card status: 'active', 'inactive', or 'canceled'
|
|
425
|
+
status: card.status,
|
|
426
|
+
cardholderId,
|
|
427
|
+
agentName: metadata?.agentName || options.agentName || 'unknown',
|
|
428
|
+
// Human-readable description of what this card is for.
|
|
429
|
+
// Stored in Stripe metadata as opencard_description. Null if not set.
|
|
430
|
+
description: metadata?.opencard_description ?? null,
|
|
431
|
+
rules: options.rules,
|
|
432
|
+
// Stripe returns spending limits as an array of SpendingLimit objects.
|
|
433
|
+
// Our SpendingLimits type has different field names (camelCase vs snake_case),
|
|
434
|
+
// so we map them here. If there are no limits set, return an empty array.
|
|
435
|
+
spendingControls: (card.spending_controls?.spending_limits || []).map((sl) => ({
|
|
436
|
+
amount: sl.amount,
|
|
437
|
+
intervalType: sl.interval,
|
|
438
|
+
intervalCurrencyUnit: 'usd',
|
|
439
|
+
})),
|
|
440
|
+
metadata: metadata ?? undefined,
|
|
441
|
+
createdAt: new Date(card.created * 1000),
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
//# sourceMappingURL=stripe-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stripe-client.js","sourceRoot":"","sources":["../src/stripe-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @opencard/core — Test Utilities
|
|
3
|
+
* ================================
|
|
4
|
+
* Shared helpers for testing code that depends on Stripe Issuing types.
|
|
5
|
+
*
|
|
6
|
+
* These utilities exist to avoid duplicating fake Stripe object construction
|
|
7
|
+
* across test files and examples. All builders produce minimal partial objects
|
|
8
|
+
* that contain only the fields our code actually reads — the real Stripe
|
|
9
|
+
* authorization has ~30 fields, but we only need a handful for evaluation.
|
|
10
|
+
*
|
|
11
|
+
* ─── Usage ───────────────────────────────────────────────────────────────────
|
|
12
|
+
*
|
|
13
|
+
* import { mockAuthorization } from '@opencard-dev/core/test-utils';
|
|
14
|
+
*
|
|
15
|
+
* const auth = mockAuthorization({ amount: 2500, category: 'saas' });
|
|
16
|
+
*
|
|
17
|
+
* ─── Why cast through unknown? ───────────────────────────────────────────────
|
|
18
|
+
* We intentionally build partial Stripe objects — only the fields our rules
|
|
19
|
+
* engine reads. TypeScript would complain about missing required fields, so we
|
|
20
|
+
* cast via `unknown` as a deliberate signal that this is a test-only shortcut.
|
|
21
|
+
*/
|
|
22
|
+
import Stripe from 'stripe';
|
|
23
|
+
/**
|
|
24
|
+
* Parameters for `mockAuthorization()`.
|
|
25
|
+
* All fields are optional — sensible defaults are provided for each.
|
|
26
|
+
*/
|
|
27
|
+
export interface MockAuthorizationParams {
|
|
28
|
+
/** Transaction amount in cents. Default: 2500 ($25.00) */
|
|
29
|
+
amount?: number;
|
|
30
|
+
/** Merchant category code (e.g. 'saas', 'grocery'). Default: 'saas' */
|
|
31
|
+
category?: string;
|
|
32
|
+
/** Merchant display name. Default: 'Acme Corp' */
|
|
33
|
+
merchantName?: string;
|
|
34
|
+
/** Stripe card ID. Default: 'ic_test123' */
|
|
35
|
+
cardId?: string;
|
|
36
|
+
/** Additional metadata to set on the card object. Default: {} */
|
|
37
|
+
cardMetadata?: Record<string, string>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates a minimal fake `Stripe.Issuing.Authorization` for use in tests
|
|
41
|
+
* and runnable examples.
|
|
42
|
+
*
|
|
43
|
+
* Only the fields read by `evaluateAuthorization` are populated:
|
|
44
|
+
* - `amount`
|
|
45
|
+
* - `merchant_data.category` and `merchant_data.name`
|
|
46
|
+
* - `card.id` and `card.metadata`
|
|
47
|
+
*
|
|
48
|
+
* All other required Stripe fields are set to null/empty/defaults to satisfy
|
|
49
|
+
* TypeScript without pulling in the full Stripe fixture machinery.
|
|
50
|
+
*
|
|
51
|
+
* @param params - Override defaults for any field
|
|
52
|
+
* @returns A partial Stripe.Issuing.Authorization cast to the full type
|
|
53
|
+
*/
|
|
54
|
+
export declare function mockAuthorization(params?: MockAuthorizationParams): Stripe.Issuing.Authorization;
|
|
55
|
+
//# sourceMappingURL=test-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,uBAA4B,GACnC,MAAM,CAAC,OAAO,CAAC,aAAa,CAuD9B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @opencard/core — Test Utilities
|
|
4
|
+
* ================================
|
|
5
|
+
* Shared helpers for testing code that depends on Stripe Issuing types.
|
|
6
|
+
*
|
|
7
|
+
* These utilities exist to avoid duplicating fake Stripe object construction
|
|
8
|
+
* across test files and examples. All builders produce minimal partial objects
|
|
9
|
+
* that contain only the fields our code actually reads — the real Stripe
|
|
10
|
+
* authorization has ~30 fields, but we only need a handful for evaluation.
|
|
11
|
+
*
|
|
12
|
+
* ─── Usage ───────────────────────────────────────────────────────────────────
|
|
13
|
+
*
|
|
14
|
+
* import { mockAuthorization } from '@opencard-dev/core/test-utils';
|
|
15
|
+
*
|
|
16
|
+
* const auth = mockAuthorization({ amount: 2500, category: 'saas' });
|
|
17
|
+
*
|
|
18
|
+
* ─── Why cast through unknown? ───────────────────────────────────────────────
|
|
19
|
+
* We intentionally build partial Stripe objects — only the fields our rules
|
|
20
|
+
* engine reads. TypeScript would complain about missing required fields, so we
|
|
21
|
+
* cast via `unknown` as a deliberate signal that this is a test-only shortcut.
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.mockAuthorization = mockAuthorization;
|
|
25
|
+
/**
|
|
26
|
+
* Creates a minimal fake `Stripe.Issuing.Authorization` for use in tests
|
|
27
|
+
* and runnable examples.
|
|
28
|
+
*
|
|
29
|
+
* Only the fields read by `evaluateAuthorization` are populated:
|
|
30
|
+
* - `amount`
|
|
31
|
+
* - `merchant_data.category` and `merchant_data.name`
|
|
32
|
+
* - `card.id` and `card.metadata`
|
|
33
|
+
*
|
|
34
|
+
* All other required Stripe fields are set to null/empty/defaults to satisfy
|
|
35
|
+
* TypeScript without pulling in the full Stripe fixture machinery.
|
|
36
|
+
*
|
|
37
|
+
* @param params - Override defaults for any field
|
|
38
|
+
* @returns A partial Stripe.Issuing.Authorization cast to the full type
|
|
39
|
+
*/
|
|
40
|
+
function mockAuthorization(params = {}) {
|
|
41
|
+
const { amount = 2500, // Default: $25.00
|
|
42
|
+
category = 'saas', // Default: software category
|
|
43
|
+
merchantName = 'Acme Corp', cardId = 'ic_test123', cardMetadata = {}, } = params;
|
|
44
|
+
return {
|
|
45
|
+
id: `iauth_${Date.now()}`,
|
|
46
|
+
object: 'issuing.authorization',
|
|
47
|
+
amount,
|
|
48
|
+
approved: false, // Before evaluation, always false
|
|
49
|
+
currency: 'usd',
|
|
50
|
+
merchant_data: {
|
|
51
|
+
category,
|
|
52
|
+
name: merchantName,
|
|
53
|
+
city: 'San Francisco',
|
|
54
|
+
country: 'US',
|
|
55
|
+
network_id: '',
|
|
56
|
+
postal_code: '94105',
|
|
57
|
+
state: 'CA',
|
|
58
|
+
},
|
|
59
|
+
// The card object embedded in the authorization.
|
|
60
|
+
// Our code reads card.id and card.metadata from here.
|
|
61
|
+
card: {
|
|
62
|
+
id: cardId,
|
|
63
|
+
metadata: cardMetadata,
|
|
64
|
+
},
|
|
65
|
+
metadata: {},
|
|
66
|
+
// Fields below exist in real authorizations but are unused by our code.
|
|
67
|
+
pending_request: null,
|
|
68
|
+
request_history: [],
|
|
69
|
+
status: 'pending',
|
|
70
|
+
created: Math.floor(Date.now() / 1000),
|
|
71
|
+
livemode: false,
|
|
72
|
+
network_data: null,
|
|
73
|
+
transactions: [],
|
|
74
|
+
verification_data: {
|
|
75
|
+
address_line1_check: 'not_provided',
|
|
76
|
+
address_postal_code_check: 'not_provided',
|
|
77
|
+
cvc_check: 'not_provided',
|
|
78
|
+
expiry_check: 'match',
|
|
79
|
+
},
|
|
80
|
+
wallet: null,
|
|
81
|
+
amount_details: null,
|
|
82
|
+
balance_transactions: [],
|
|
83
|
+
cardholder: 'ich_test',
|
|
84
|
+
fleet: null,
|
|
85
|
+
fuel: null,
|
|
86
|
+
merchant_amount: amount,
|
|
87
|
+
merchant_currency: 'usd',
|
|
88
|
+
token: null,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=test-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;AAoCH,8CAyDC;AAxED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,iBAAiB,CAC/B,SAAkC,EAAE;IAEpC,MAAM,EACJ,MAAM,GAAG,IAAI,EAAa,kBAAkB;IAC5C,QAAQ,GAAG,MAAM,EAAS,6BAA6B;IACvD,YAAY,GAAG,WAAW,EAC1B,MAAM,GAAG,YAAY,EACrB,YAAY,GAAG,EAAE,GAClB,GAAG,MAAM,CAAC;IAEX,OAAO;QACL,EAAE,EAAE,SAAS,IAAI,CAAC,GAAG,EAAE,EAAE;QACzB,MAAM,EAAE,uBAAuB;QAC/B,MAAM;QACN,QAAQ,EAAE,KAAK,EAAW,kCAAkC;QAC5D,QAAQ,EAAE,KAAK;QACf,aAAa,EAAE;YACb,QAAQ;YACR,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,IAAI;YACb,UAAU,EAAE,EAAE;YACd,WAAW,EAAE,OAAO;YACpB,KAAK,EAAE,IAAI;SACZ;QACD,iDAAiD;QACjD,sDAAsD;QACtD,IAAI,EAAE;YACJ,EAAE,EAAE,MAAM;YACV,QAAQ,EAAE,YAAY;SACW;QACnC,QAAQ,EAAE,EAAE;QACZ,wEAAwE;QACxE,eAAe,EAAE,IAAkE;QACnF,eAAe,EAAE,EAAE;QACnB,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QACtC,QAAQ,EAAE,KAAK;QACf,YAAY,EAAE,IAA+D;QAC7E,YAAY,EAAE,EAAE;QAChB,iBAAiB,EAAE;YACjB,mBAAmB,EAAE,cAAc;YACnC,yBAAyB,EAAE,cAAc;YACzC,SAAS,EAAE,cAAc;YACzB,YAAY,EAAE,OAAO;SACtB;QACD,MAAM,EAAE,IAAI;QACZ,cAAc,EAAE,IAAI;QACpB,oBAAoB,EAAE,EAAE;QACxB,UAAU,EAAE,UAAU;QACtB,KAAK,EAAE,IAAI;QACX,IAAI,EAAE,IAAI;QACV,eAAe,EAAE,MAAM;QACvB,iBAAiB,EAAE,KAAK;QACxB,KAAK,EAAE,IAAI;KAC+B,CAAC;AAC/C,CAAC"}
|