arispay 0.1.4 → 0.1.6

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.
Files changed (3) hide show
  1. package/dist/cli.js +57 -62
  2. package/package.json +1 -1
  3. package/src/cli.ts +67 -53
package/dist/cli.js CHANGED
@@ -110,75 +110,69 @@ ${bold("Get started")}
110
110
  }
111
111
  }
112
112
  }
113
- async function collectAndTokenizeCard(client, userId) {
114
- console.log(`
115
- ${dim("Enter your card details below. They are sent directly to Fiserv for")}`);
116
- console.log(` ${dim("tokenization \u2014 ArisPay never sees or stores your full card number.")}
117
- `);
118
- const cardNumber = await promptSecret(` Card number: `);
119
- if (!cardNumber) {
120
- console.log(dim(" Cancelled."));
121
- return false;
122
- }
123
- const expiry = await prompt(` Expiry ${dim("(MM/YY)")}: `);
124
- if (!expiry) {
125
- console.log(dim(" Cancelled."));
126
- return false;
127
- }
128
- const securityCode = await promptSecret(` CVV: `);
129
- if (!securityCode) {
130
- console.log(dim(" Cancelled."));
131
- return false;
132
- }
133
- const [expiryMonth, expiryYear] = expiry.split("/").map((s) => s.trim());
134
- if (!expiryMonth || !expiryYear) {
135
- console.log(` ${dim("Invalid expiry format. Use MM/YY.")}`);
136
- return false;
137
- }
113
+ async function setupCardViaBrowser(endUserId, agentId) {
114
+ const config = loadConfig(BRAND);
115
+ const { apiKey, environment, baseUrl } = config;
116
+ const defaultUrl = environment === "production" ? "https://api.arispay.app" : "https://api-production-79ea.up.railway.app";
117
+ const apiUrl = baseUrl || defaultUrl;
138
118
  console.log(`
139
- ${dim("Tokenizing and verifying card...")}`);
119
+ ${dim("Creating secure card setup link...")}`);
140
120
  try {
141
- const result = await client.users.addPaymentMethod({
142
- userId,
143
- type: "card",
144
- cardNumber: cardNumber.replace(/\s/g, ""),
145
- expiryMonth,
146
- expiryYear: expiryYear.length === 2 ? `20${expiryYear}` : expiryYear,
147
- securityCode
121
+ const res = await fetch(`${apiUrl}/v1/card-setup-sessions`, {
122
+ method: "POST",
123
+ headers: {
124
+ "Content-Type": "application/json",
125
+ Authorization: `Bearer ${apiKey}`
126
+ },
127
+ body: JSON.stringify({ endUserId, ...agentId ? { agentId } : {} }),
128
+ signal: AbortSignal.timeout(15e3)
148
129
  });
149
- const r = result;
150
- if (r.requiresVerification) {
151
- const challengeUrl = r.challengeUrl;
152
- if (challengeUrl) {
153
- console.log();
154
- console.log(` ${bold("Card verification required")}`);
155
- console.log(` ${dim("Your bank requires 3D Secure verification. Open this link:")}
156
- `);
157
- console.log(` ${cyan(challengeUrl)}
158
- `);
159
- console.log(` ${dim("Complete the verification in your browser, then come back here.")}`);
160
- await prompt(`
161
- Press Enter when done...`);
162
- success("Card added (verification pending)\n");
163
- } else {
164
- console.log();
165
- success("Card tokenized (verification in progress)\n");
166
- }
167
- console.log(` ${bold("Card:")} ${r.cardBrand?.toUpperCase() || "Card"} ending in ${r.cardLast4 || "****"}
168
- `);
169
- return true;
130
+ if (!res.ok) {
131
+ const body = await res.json().catch(() => ({}));
132
+ throw new Error(body.error?.message || `Failed to create card setup (HTTP ${res.status})`);
170
133
  }
134
+ const data = await res.json();
171
135
  console.log();
172
- success(`Card verified!
136
+ console.log(` ${bold("Open this link to securely add and verify your card:")}
137
+ `);
138
+ console.log(` ${cyan(data.setupUrl)}
139
+ `);
140
+ console.log(` ${dim("Your card details are entered on a secure page hosted by ArisPay.")}`);
141
+ console.log(` ${dim("3D Secure verification is handled automatically in the browser.")}`);
142
+ console.log(` ${dim("This link expires in 15 minutes.")}
143
+ `);
144
+ console.log(` ${dim("Waiting for you to complete card setup...")}`);
145
+ const deadline = Date.now() + 15 * 60 * 1e3;
146
+ while (Date.now() < deadline) {
147
+ await new Promise((r) => setTimeout(r, 3e3));
148
+ try {
149
+ const statusRes = await fetch(`${apiUrl}/v1/card-setup-sessions/${data.token}/status`, {
150
+ signal: AbortSignal.timeout(1e4)
151
+ });
152
+ const statusData = await statusRes.json();
153
+ if (statusData.status === "completed") {
154
+ console.log();
155
+ success("Card verified!\n");
156
+ console.log(` ${bold("Card:")} ${statusData.cardBrand?.toUpperCase() || "Card"} ending in ${statusData.cardLast4 || "****"}`);
157
+ console.log(`
158
+ ${dim("Your card is linked and verified. All payments will process instantly.")}
173
159
  `);
174
- console.log(` ${bold("Card:")} ${r.cardBrand?.toUpperCase() || "Card"} ending in ${r.cardLast4 || "****"}`);
160
+ return true;
161
+ }
162
+ if (statusData.status === "failed") {
163
+ console.log();
164
+ console.error(` \x1B[31m\u2717\x1B[0m Card verification failed. Please try again.`);
165
+ return false;
166
+ }
167
+ } catch {
168
+ }
169
+ }
175
170
  console.log(`
176
- ${dim("Your card is now linked and verified. You can make payments.")}
177
- `);
178
- return true;
171
+ ${dim("Card setup link expired. Run option [2] to try again.")}`);
172
+ return false;
179
173
  } catch (e) {
180
174
  console.log();
181
- console.error(` \x1B[31m\u2717\x1B[0m ${e.message || "Failed to tokenize card"}`);
175
+ console.error(` \x1B[31m\u2717\x1B[0m ${e.message || "Failed to start card setup"}`);
182
176
  return false;
183
177
  }
184
178
  }
@@ -284,7 +278,7 @@ ${bold("Connect your agent to ArisPay")}`);
284
278
  return;
285
279
  }
286
280
  }
287
- const cardOk = await collectAndTokenizeCard(client, endUserId);
281
+ const cardOk = await setupCardViaBrowser(endUserId, a.id);
288
282
  if (!cardOk) return;
289
283
  const topupStr = await prompt(`
290
284
  Amount to load ${dim("(e.g. 50.00)")}: $`);
@@ -351,7 +345,8 @@ ${bold("Add a payment method")}`);
351
345
  `);
352
346
  const methodChoice = await prompt(` Enter choice: `);
353
347
  if (methodChoice === "1") {
354
- await collectAndTokenizeCard(client, userId);
348
+ const storedAgentId = config.agentId;
349
+ await setupCardViaBrowser(userId, storedAgentId);
355
350
  } else if (methodChoice === "2") {
356
351
  console.log();
357
352
  const chain = await prompt(` Chain ${dim("(ethereum, base, polygon, solana)")}: `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arispay",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "SDK and CLI for enabling agent-initiated payments via Visa TAP + Fiserv",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/cli.ts CHANGED
@@ -130,69 +130,82 @@ async function postInitMenu(config: { apiKey?: string; environment?: string; bas
130
130
  }
131
131
  }
132
132
 
133
- // ── Card Tokenization + 3DS Helper ──────────────────────────────────
133
+ // ── Card Setup Helper (browser-based with 3DS) ─────────────────────
134
134
 
135
- async function collectAndTokenizeCard(
136
- client: ArisPayClient,
137
- userId: string,
135
+ async function setupCardViaBrowser(
136
+ endUserId: string,
137
+ agentId?: string,
138
138
  ): Promise<boolean> {
139
- console.log(`\n ${dim('Enter your card details below. They are sent directly to Fiserv for')}`);
140
- console.log(` ${dim('tokenization ArisPay never sees or stores your full card number.')}\n`);
139
+ const config = loadConfig(BRAND);
140
+ const { apiKey, environment, baseUrl } = config;
141
+ const defaultUrl = environment === 'production'
142
+ ? 'https://api.arispay.app'
143
+ : 'https://api-production-79ea.up.railway.app';
144
+ const apiUrl = baseUrl || defaultUrl;
141
145
 
142
- const cardNumber = await promptSecret(` Card number: `);
143
- if (!cardNumber) { console.log(dim(' Cancelled.')); return false; }
146
+ console.log(`\n ${dim('Creating secure card setup link...')}`);
144
147
 
145
- const expiry = await prompt(` Expiry ${dim('(MM/YY)')}: `);
146
- if (!expiry) { console.log(dim(' Cancelled.')); return false; }
148
+ try {
149
+ // Create card setup session
150
+ const res = await fetch(`${apiUrl}/v1/card-setup-sessions`, {
151
+ method: 'POST',
152
+ headers: {
153
+ 'Content-Type': 'application/json',
154
+ Authorization: `Bearer ${apiKey}`,
155
+ },
156
+ body: JSON.stringify({ endUserId, ...(agentId ? { agentId } : {}) }),
157
+ signal: AbortSignal.timeout(15_000),
158
+ });
147
159
 
148
- const securityCode = await promptSecret(` CVV: `);
149
- if (!securityCode) { console.log(dim(' Cancelled.')); return false; }
160
+ if (!res.ok) {
161
+ const body = await res.json().catch(() => ({}));
162
+ throw new Error((body as any).error?.message || `Failed to create card setup (HTTP ${res.status})`);
163
+ }
150
164
 
151
- const [expiryMonth, expiryYear] = expiry.split('/').map(s => s.trim());
152
- if (!expiryMonth || !expiryYear) {
153
- console.log(` ${dim('Invalid expiry format. Use MM/YY.')}`);
154
- return false;
155
- }
165
+ const data = await res.json() as { token: string; setupUrl: string; expiresAt: string };
156
166
 
157
- console.log(`\n ${dim('Tokenizing and verifying card...')}`);
158
- try {
159
- const result = await client.users.addPaymentMethod({
160
- userId,
161
- type: 'card',
162
- cardNumber: cardNumber.replace(/\s/g, ''),
163
- expiryMonth,
164
- expiryYear: expiryYear.length === 2 ? `20${expiryYear}` : expiryYear,
165
- securityCode,
166
- });
167
- const r = result as any;
168
-
169
- if (r.requiresVerification) {
170
- // 3DS verification required — open browser
171
- const challengeUrl = r.challengeUrl;
172
- if (challengeUrl) {
173
- console.log();
174
- console.log(` ${bold('Card verification required')}`);
175
- console.log(` ${dim('Your bank requires 3D Secure verification. Open this link:')}\n`);
176
- console.log(` ${cyan(challengeUrl)}\n`);
177
- console.log(` ${dim('Complete the verification in your browser, then come back here.')}`);
178
- await prompt(`\n Press Enter when done...`);
179
- success('Card added (verification pending)\n');
180
- } else {
181
- console.log();
182
- success('Card tokenized (verification in progress)\n');
167
+ console.log();
168
+ console.log(` ${bold('Open this link to securely add and verify your card:')}\n`);
169
+ console.log(` ${cyan(data.setupUrl)}\n`);
170
+ console.log(` ${dim('Your card details are entered on a secure page hosted by ArisPay.')}`);
171
+ console.log(` ${dim('3D Secure verification is handled automatically in the browser.')}`);
172
+ console.log(` ${dim('This link expires in 15 minutes.')}\n`);
173
+
174
+ // Poll for completion
175
+ console.log(` ${dim('Waiting for you to complete card setup...')}`);
176
+ const deadline = Date.now() + 15 * 60 * 1000; // 15 min
177
+ while (Date.now() < deadline) {
178
+ await new Promise(r => setTimeout(r, 3000));
179
+
180
+ try {
181
+ const statusRes = await fetch(`${apiUrl}/v1/card-setup-sessions/${data.token}/status`, {
182
+ signal: AbortSignal.timeout(10_000),
183
+ });
184
+ const statusData = await statusRes.json() as any;
185
+
186
+ if (statusData.status === 'completed') {
187
+ console.log();
188
+ success('Card verified!\n');
189
+ console.log(` ${bold('Card:')} ${statusData.cardBrand?.toUpperCase() || 'Card'} ending in ${statusData.cardLast4 || '****'}`);
190
+ console.log(`\n ${dim('Your card is linked and verified. All payments will process instantly.')}\n`);
191
+ return true;
192
+ }
193
+
194
+ if (statusData.status === 'failed') {
195
+ console.log();
196
+ console.error(` \x1b[31m✗\x1b[0m Card verification failed. Please try again.`);
197
+ return false;
198
+ }
199
+ } catch {
200
+ // Network error — keep polling
183
201
  }
184
- console.log(` ${bold('Card:')} ${r.cardBrand?.toUpperCase() || 'Card'} ending in ${r.cardLast4 || '****'}\n`);
185
- return true;
186
202
  }
187
203
 
188
- console.log();
189
- success(`Card verified!\n`);
190
- console.log(` ${bold('Card:')} ${r.cardBrand?.toUpperCase() || 'Card'} ending in ${r.cardLast4 || '****'}`);
191
- console.log(`\n ${dim('Your card is now linked and verified. You can make payments.')}\n`);
192
- return true;
204
+ console.log(`\n ${dim('Card setup link expired. Run option [2] to try again.')}`);
205
+ return false;
193
206
  } catch (e: any) {
194
207
  console.log();
195
- console.error(` \x1b[31m✗\x1b[0m ${e.message || 'Failed to tokenize card'}`);
208
+ console.error(` \x1b[31m✗\x1b[0m ${e.message || 'Failed to start card setup'}`);
196
209
  return false;
197
210
  }
198
211
  }
@@ -305,7 +318,7 @@ async function wizardConnectAgent(client: ArisPayClient): Promise<void> {
305
318
  }
306
319
 
307
320
  // Collect card details and verify via 3DS
308
- const cardOk = await collectAndTokenizeCard(client, endUserId);
321
+ const cardOk = await setupCardViaBrowser(endUserId, a.id);
309
322
  if (!cardOk) return;
310
323
 
311
324
  // Top up
@@ -373,7 +386,8 @@ async function wizardAddPaymentMethod(client: ArisPayClient): Promise<void> {
373
386
  const methodChoice = await prompt(` Enter choice: `);
374
387
 
375
388
  if (methodChoice === '1') {
376
- await collectAndTokenizeCard(client, userId);
389
+ const storedAgentId = (config as any).agentId;
390
+ await setupCardViaBrowser(userId, storedAgentId);
377
391
  } else if (methodChoice === '2') {
378
392
  console.log();
379
393
  const chain = await prompt(` Chain ${dim('(ethereum, base, polygon, solana)')}: `);