@nullpay/cli 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/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const onboard_1 = require("./commands/onboard");
5
+ const args = process.argv.slice(2);
6
+ if (args[0] === 'sdk' && args[1] === 'onboard') {
7
+ (0, onboard_1.onboard)().catch((err) => {
8
+ console.error('\n❌ Fatal error:', err.message || err);
9
+ process.exit(1);
10
+ });
11
+ }
12
+ else {
13
+ console.log(`
14
+ ╔═══════════════════════════════════════╗
15
+ ║ NullPay CLI v1.0.0 ║
16
+ ╚═══════════════════════════════════════╝
17
+
18
+ Usage:
19
+ nullpay sdk onboard — Interactive invoice setup wizard
20
+ `);
21
+ process.exit(0);
22
+ }
@@ -0,0 +1 @@
1
+ export declare function onboard(): Promise<void>;
@@ -0,0 +1,541 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.onboard = onboard;
40
+ const inquirer_1 = __importDefault(require("inquirer"));
41
+ const ora_1 = __importDefault(require("ora"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const crypto = __importStar(require("crypto"));
46
+ const BACKEND_URL = 'http://localhost:3000/api';
47
+ const ALEO_PROGRAM = 'zk_pay_proofs_privacy_v22.aleo';
48
+ const ALEO_MAP_BASE = `https://api.provable.com/v2/testnet/program/${ALEO_PROGRAM}/mapping/salt_to_invoice`;
49
+ const W = 56;
50
+ const C = {
51
+ brand: chalk_1.default.hex('#00FFD1'),
52
+ brandDim: chalk_1.default.hex('#00B894'),
53
+ gold: chalk_1.default.hex('#FFD166'),
54
+ rose: chalk_1.default.hex('#FF6B8A'),
55
+ slate: chalk_1.default.hex('#8892A4'),
56
+ white: chalk_1.default.hex('#F0F4FF'),
57
+ dim: chalk_1.default.hex('#3A4055'),
58
+ success: chalk_1.default.hex('#06D6A0'),
59
+ purple: chalk_1.default.hex('#A78BFA'),
60
+ orange: chalk_1.default.hex('#FB923C'),
61
+ };
62
+ const line = (content) => console.log(content);
63
+ const blank = () => line('');
64
+ const rule = (char = '=') => C.dim(` ${char.repeat(W)}`);
65
+ const tag = (label, color = C.brand) => color.bold(` ${label} `);
66
+ function printBanner() {
67
+ blank();
68
+ line(C.dim(` +${'-'.repeat(W)}+`));
69
+ line(C.dim(' |') + ' '.repeat(W) + C.dim('|'));
70
+ line(C.dim(' |') +
71
+ ' '.repeat(10) +
72
+ C.brand.bold('NULLPAY') +
73
+ C.slate(' | ') +
74
+ C.white.bold('SDK Onboard Wizard') +
75
+ ' '.repeat(13) +
76
+ C.dim('|'));
77
+ line(C.dim(' |') +
78
+ ' '.repeat(8) +
79
+ C.slate('Zero-knowledge blockchain invoicing on Aleo') +
80
+ ' '.repeat(5) +
81
+ C.dim('|'));
82
+ line(C.dim(' |') + ' '.repeat(W) + C.dim('|'));
83
+ line(C.dim(` +${'-'.repeat(W)}+`));
84
+ blank();
85
+ line(C.slate(` Version 1.0.0 | Aleo Testnet | ${new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}`));
86
+ blank();
87
+ }
88
+ function section(step, total, title) {
89
+ blank();
90
+ line(rule('-'));
91
+ line(C.dim(' | ') + C.slate(`Step ${step}/${total}`) + ' ' + C.white.bold(title));
92
+ line(rule('-'));
93
+ blank();
94
+ }
95
+ function kv(key, value, valueColor = C.white) {
96
+ const pad = 18;
97
+ line(` ${C.slate(key.padEnd(pad))} ${valueColor(value)}`);
98
+ }
99
+ function spin(text) {
100
+ return (0, ora_1.default)({
101
+ text,
102
+ spinner: 'dots12',
103
+ color: 'cyan',
104
+ prefixText: ' ',
105
+ }).start();
106
+ }
107
+ function generateSalt() {
108
+ const buf = crypto.randomBytes(16);
109
+ let n = BigInt(0);
110
+ for (const b of buf)
111
+ n = (n << BigInt(8)) + BigInt(b);
112
+ return `${n.toString()}field`;
113
+ }
114
+ function truncateHash(hash, len = 24) {
115
+ return hash.length > len ? `${hash.slice(0, len)}...` : hash;
116
+ }
117
+ function formatCurrencyLabel(currency) {
118
+ if (currency === 'ANY')
119
+ return 'CREDITS + USDCX + USAD';
120
+ return currency;
121
+ }
122
+ async function validateMerchant(secretKey, merchantAddress) {
123
+ const res = await fetch(`${BACKEND_URL}/sdk/onboard/validate`, {
124
+ method: 'POST',
125
+ headers: {
126
+ 'Content-Type': 'application/json',
127
+ Authorization: `Bearer ${secretKey}`,
128
+ },
129
+ body: JSON.stringify({ merchant_address: merchantAddress }),
130
+ });
131
+ if (!res.ok) {
132
+ const err = await res.json().catch(() => ({ error: res.statusText }));
133
+ throw new Error(err.error ?? `Validation failed (${res.status})`);
134
+ }
135
+ return res.json();
136
+ }
137
+ async function submitToRelayer(secretKey, invoice, salt) {
138
+ const invoiceTypeNum = invoice.type === 'multipay' ? 1 : 2;
139
+ const res = await fetch(`${BACKEND_URL}/dps/relayer/create-invoice`, {
140
+ method: 'POST',
141
+ headers: {
142
+ 'Content-Type': 'application/json',
143
+ Authorization: `Bearer ${secretKey}`,
144
+ },
145
+ body: JSON.stringify({
146
+ amount: invoice.type === 'donation' ? 0 : invoice.amount,
147
+ currency: invoice.currency,
148
+ salt,
149
+ memo: invoice.label ?? '',
150
+ invoice_type: invoiceTypeNum,
151
+ }),
152
+ });
153
+ if (!res.ok) {
154
+ const err = await res.json().catch(() => ({ error: res.statusText }));
155
+ throw new Error(err.error ?? `Relayer error (${res.status})`);
156
+ }
157
+ }
158
+ async function pollForHash(salt, maxRetries = 60) {
159
+ for (let i = 0; i < maxRetries; i++) {
160
+ await new Promise((resolve) => setTimeout(resolve, 2000));
161
+ try {
162
+ const res = await fetch(`${ALEO_MAP_BASE}/${salt}`);
163
+ if (res.ok) {
164
+ const val = await res.json();
165
+ if (val)
166
+ return val.toString().replace(/['"]/g, '');
167
+ }
168
+ }
169
+ catch (_) {
170
+ // Ignore transient mapping lookup failures while polling.
171
+ }
172
+ }
173
+ return null;
174
+ }
175
+ function updateGitignore(projectRoot) {
176
+ const filePath = path.join(projectRoot, '.gitignore');
177
+ const entry = 'nullpay.json';
178
+ const note = '# NullPay - contains sensitive salt values';
179
+ if (fs.existsSync(filePath)) {
180
+ const content = fs.readFileSync(filePath, 'utf-8');
181
+ if (!content.includes(entry)) {
182
+ fs.appendFileSync(filePath, `\n${note}\n${entry}\n`);
183
+ }
184
+ return;
185
+ }
186
+ fs.writeFileSync(filePath, `${note}\n${entry}\n`);
187
+ }
188
+ function renderInvoiceCard(inv, index) {
189
+ const typeLabel = inv.type === 'multipay'
190
+ ? tag('MULTI-PAY', C.brand)
191
+ : tag('DONATION', C.orange);
192
+ const amountLabel = inv.amount !== null
193
+ ? C.gold.bold(`${inv.amount} ${formatCurrencyLabel(inv.currency)}`)
194
+ : C.slate(`Open | ${formatCurrencyLabel(inv.currency)}`);
195
+ line(` ${C.slate(`${String(index).padStart(2, '0')}.`)} ` +
196
+ typeLabel +
197
+ C.dim(' ') +
198
+ C.white.bold(inv.name) +
199
+ C.dim(' - ') +
200
+ amountLabel);
201
+ }
202
+ function renderResultCard(inv, index, total) {
203
+ const typeColor = inv.type === 'multipay' ? C.brand : C.orange;
204
+ const amountText = inv.amount !== null
205
+ ? `${inv.amount} ${formatCurrencyLabel(inv.currency)}`
206
+ : `open / ${formatCurrencyLabel(inv.currency)}`;
207
+ const amountLabel = inv.amount !== null
208
+ ? C.gold(amountText)
209
+ : C.slate(amountText);
210
+ line(C.dim(` +${'-'.repeat(W + 1)}+`));
211
+ line(C.dim(' | ') + C.success('OK') + ' ' + typeColor.bold(inv.name.toUpperCase()) + C.dim(` [${index}/${total}]`));
212
+ line(C.dim(' |'));
213
+ line(C.dim(' | ') + C.slate('type ') + ' ' + typeColor(inv.type));
214
+ line(C.dim(' | ') + C.slate('amount ') + ' ' + amountLabel);
215
+ line(C.dim(' | ') + C.slate('hash ') + ' ' + C.brandDim(truncateHash(inv.hash, 36)));
216
+ line(C.dim(` +${'-'.repeat(W + 1)}+`));
217
+ }
218
+ async function promptDonationInvoice(template) {
219
+ blank();
220
+ line(` ${C.orange.bold(template.title)} ${C.dim('-'.repeat(28))}`);
221
+ const a = await inquirer_1.default.prompt([
222
+ {
223
+ type: 'input',
224
+ name: 'name',
225
+ message: C.slate('Name'),
226
+ prefix: C.dim(' >'),
227
+ suffix: C.dim(` e.g. ${template.exampleName}`),
228
+ validate: (v) => v.trim().length ? true : C.rose('Cannot be empty'),
229
+ },
230
+ {
231
+ type: 'input',
232
+ name: 'label',
233
+ message: C.slate('Memo') + C.dim(' (optional)'),
234
+ prefix: C.dim(' >'),
235
+ default: '',
236
+ },
237
+ ]);
238
+ return {
239
+ name: a.name.trim().toLowerCase().replace(/\s+/g, '-'),
240
+ type: 'donation',
241
+ amount: null,
242
+ currency: template.currency,
243
+ label: a.label,
244
+ };
245
+ }
246
+ async function onboard() {
247
+ printBanner();
248
+ section(1, 4, 'Authentication');
249
+ const { secretKey } = await inquirer_1.default.prompt([{
250
+ type: 'password',
251
+ name: 'secretKey',
252
+ message: C.slate('Secret Key') + ' ' + C.dim('(sk_test_... or sk_live_...)'),
253
+ mask: '*',
254
+ prefix: C.brand(' >'),
255
+ validate: (v) => v.startsWith('sk_') ? true : C.rose('Must start with sk_test_ or sk_live_'),
256
+ }]);
257
+ const { merchantAddress } = await inquirer_1.default.prompt([{
258
+ type: 'input',
259
+ name: 'merchantAddress',
260
+ message: C.slate('Aleo Address') + ' ' + C.dim('(aleo1...)'),
261
+ prefix: C.brand(' >'),
262
+ validate: (v) => v.startsWith('aleo1') ? true : C.rose('Must be a valid Aleo address (aleo1...)'),
263
+ }]);
264
+ section(2, 4, 'Verifying Credentials');
265
+ const spinner = spin('Connecting to NullPay network...');
266
+ let merchantName = '';
267
+ let resolvedAddress = '';
268
+ try {
269
+ const result = await validateMerchant(secretKey, merchantAddress);
270
+ merchantName = result.merchant_name;
271
+ resolvedAddress = result.merchant_address;
272
+ spinner.stopAndPersist({
273
+ symbol: C.success(' OK'),
274
+ text: C.white('Merchant verified'),
275
+ });
276
+ }
277
+ catch (err) {
278
+ const msg = err instanceof Error ? err.message : String(err);
279
+ spinner.stopAndPersist({
280
+ symbol: C.rose(' X'),
281
+ text: C.rose(`Auth failed: ${msg}`),
282
+ });
283
+ process.exit(1);
284
+ }
285
+ blank();
286
+ kv('Merchant', merchantName, C.white.bold);
287
+ kv('Address', resolvedAddress, C.brandDim);
288
+ kv('Network', 'Aleo Testnet', C.purple);
289
+ kv('Status', 'Active', C.success.bold);
290
+ blank();
291
+ section(3, 4, 'Invoice Configuration');
292
+ const multipayConfigs = [];
293
+ const donationConfigs = [];
294
+ line(` ${C.brand('>')} ${C.white.bold('Multi-Pay Invoices')} ${C.slate('| Fixed-amount recurring payments')}`);
295
+ blank();
296
+ const { wantsMultipay } = await inquirer_1.default.prompt([{
297
+ type: 'confirm',
298
+ name: 'wantsMultipay',
299
+ message: C.slate('Create multi-pay invoices?'),
300
+ prefix: C.dim(' -'),
301
+ default: true,
302
+ }]);
303
+ if (wantsMultipay) {
304
+ const { multipayCount } = await inquirer_1.default.prompt([{
305
+ type: 'number',
306
+ name: 'multipayCount',
307
+ message: C.slate('How many?'),
308
+ prefix: C.dim(' -'),
309
+ default: 1,
310
+ validate: (v) => (v > 0 && v <= 20) ? true : C.rose('Between 1 and 20'),
311
+ }]);
312
+ for (let i = 1; i <= multipayCount; i++) {
313
+ blank();
314
+ line(` ${C.brand.bold(`Invoice #${i}`)} ${C.dim('-'.repeat(30))}`);
315
+ const a = await inquirer_1.default.prompt([
316
+ {
317
+ type: 'input',
318
+ name: 'name',
319
+ message: C.slate('Name'),
320
+ prefix: C.dim(' >'),
321
+ suffix: C.dim(' e.g. pro-plan'),
322
+ validate: (v) => v.trim().length ? true : C.rose('Cannot be empty'),
323
+ },
324
+ {
325
+ type: 'number',
326
+ name: 'amount',
327
+ message: C.slate('Amount'),
328
+ prefix: C.dim(' >'),
329
+ validate: (v) => v > 0 ? true : C.rose('Must be > 0'),
330
+ },
331
+ {
332
+ type: 'list',
333
+ name: 'currency',
334
+ message: C.slate('Token'),
335
+ prefix: C.dim(' >'),
336
+ choices: [
337
+ { name: `${C.brand('CREDITS')} ${C.slate('| native Aleo')}`, value: 'CREDITS' },
338
+ { name: `${C.gold('USDCX')} ${C.slate('| USD stablecoin')}`, value: 'USDCX' },
339
+ { name: `${C.purple('USAD')} ${C.slate('| Aleo-native USD')}`, value: 'USAD' },
340
+ ],
341
+ },
342
+ ]);
343
+ multipayConfigs.push({
344
+ name: a.name.trim().toLowerCase().replace(/\s+/g, '-'),
345
+ type: 'multipay',
346
+ amount: a.amount,
347
+ currency: a.currency,
348
+ label: '',
349
+ });
350
+ }
351
+ }
352
+ blank();
353
+ line(` ${C.orange('>')} ${C.white.bold('Donation Invoices')} ${C.slate('| Open-amount contributions')}`);
354
+ blank();
355
+ const { wantsDonation } = await inquirer_1.default.prompt([{
356
+ type: 'confirm',
357
+ name: 'wantsDonation',
358
+ message: C.slate('Create donation invoices?'),
359
+ prefix: C.dim(' -'),
360
+ default: false,
361
+ }]);
362
+ if (wantsDonation) {
363
+ const donationChoices = await inquirer_1.default.prompt([
364
+ {
365
+ type: 'confirm',
366
+ name: 'creditsOnly',
367
+ message: C.slate('Create a Credits-only donation invoice?'),
368
+ prefix: C.dim(' -'),
369
+ default: true,
370
+ },
371
+ {
372
+ type: 'confirm',
373
+ name: 'usdcxOnly',
374
+ message: C.slate('Create a USDCX-only donation invoice?'),
375
+ prefix: C.dim(' -'),
376
+ default: false,
377
+ },
378
+ {
379
+ type: 'confirm',
380
+ name: 'usadOnly',
381
+ message: C.slate('Create a USAD-only donation invoice?'),
382
+ prefix: C.dim(' -'),
383
+ default: false,
384
+ },
385
+ {
386
+ type: 'confirm',
387
+ name: 'allTokens',
388
+ message: C.slate('Create a donation invoice that accepts all three tokens?'),
389
+ prefix: C.dim(' -'),
390
+ default: false,
391
+ },
392
+ ]);
393
+ const selectedTemplates = [];
394
+ if (donationChoices.creditsOnly) {
395
+ selectedTemplates.push({
396
+ currency: 'CREDITS',
397
+ title: 'Credits Donation',
398
+ exampleName: 'support-credits',
399
+ });
400
+ }
401
+ if (donationChoices.usdcxOnly) {
402
+ selectedTemplates.push({
403
+ currency: 'USDCX',
404
+ title: 'USDCX Donation',
405
+ exampleName: 'support-usdcx',
406
+ });
407
+ }
408
+ if (donationChoices.usadOnly) {
409
+ selectedTemplates.push({
410
+ currency: 'USAD',
411
+ title: 'USAD Donation',
412
+ exampleName: 'support-usad',
413
+ });
414
+ }
415
+ if (donationChoices.allTokens) {
416
+ selectedTemplates.push({
417
+ currency: 'ANY',
418
+ title: 'All Tokens Donation',
419
+ exampleName: 'support-any',
420
+ });
421
+ }
422
+ for (const template of selectedTemplates) {
423
+ donationConfigs.push(await promptDonationInvoice(template));
424
+ }
425
+ }
426
+ const allInvoices = [...multipayConfigs, ...donationConfigs];
427
+ if (allInvoices.length === 0) {
428
+ blank();
429
+ line(` ${C.gold('!')} ${C.white('No invoices configured.')} ${C.slate('Exiting.')}`);
430
+ process.exit(0);
431
+ }
432
+ blank();
433
+ line(rule('='));
434
+ line(` ${C.white.bold('DEPLOYMENT SUMMARY')}`);
435
+ line(rule('='));
436
+ blank();
437
+ kv('Merchant', resolvedAddress, C.brandDim);
438
+ kv('Invoices', `${allInvoices.length} total`, C.white.bold);
439
+ kv('Est. time', `${allInvoices.length * 2}-${allInvoices.length * 3} min`, C.slate);
440
+ blank();
441
+ allInvoices.forEach((inv, i) => renderInvoiceCard(inv, i + 1));
442
+ blank();
443
+ line(` ${C.gold('!')} ${C.slate('Each invoice submits a live Aleo transaction (60-120s each)')}`);
444
+ blank();
445
+ const { confirmed } = await inquirer_1.default.prompt([{
446
+ type: 'confirm',
447
+ name: 'confirmed',
448
+ message: C.white.bold(`Deploy ${allInvoices.length} invoice(s) to Aleo?`),
449
+ prefix: C.gold(' >'),
450
+ default: true,
451
+ }]);
452
+ if (!confirmed) {
453
+ blank();
454
+ line(` ${C.slate('Aborted. No transactions submitted.')}`);
455
+ process.exit(0);
456
+ }
457
+ section(4, 4, 'On-Chain Deployment');
458
+ const generatedInvoices = [];
459
+ for (let i = 0; i < allInvoices.length; i++) {
460
+ const inv = allInvoices[i];
461
+ const salt = generateSalt();
462
+ const prefix = C.dim(` [${i + 1}/${allInvoices.length}]`);
463
+ const sp = spin(`${prefix.toString()} Submitting "${C.brand(inv.name)}" to Aleo network...`);
464
+ try {
465
+ await submitToRelayer(secretKey, inv, salt);
466
+ sp.text = `${prefix.toString()} Waiting for block confirmation -> "${C.brand(inv.name)}"`;
467
+ const hash = await pollForHash(salt);
468
+ if (!hash) {
469
+ sp.stopAndPersist({
470
+ symbol: C.rose(' X'),
471
+ text: C.rose(`Timed out: "${inv.name}" - skipped`),
472
+ });
473
+ continue;
474
+ }
475
+ sp.stopAndPersist({
476
+ symbol: C.success(' OK'),
477
+ text: C.white(`"${inv.name}" confirmed on-chain`),
478
+ });
479
+ generatedInvoices.push({ ...inv, hash, salt });
480
+ }
481
+ catch (err) {
482
+ const msg = err instanceof Error ? err.message : String(err);
483
+ sp.stopAndPersist({
484
+ symbol: C.rose(' X'),
485
+ text: C.rose(`"${inv.name}": ${msg}`),
486
+ });
487
+ }
488
+ }
489
+ if (generatedInvoices.length === 0) {
490
+ blank();
491
+ line(` ${C.rose('X')} ${C.white('No invoices confirmed. Exiting.')}`);
492
+ process.exit(1);
493
+ }
494
+ const projectRoot = process.cwd();
495
+ const outputPath = path.join(projectRoot, 'nullpay.json');
496
+ const output = {
497
+ merchant: resolvedAddress,
498
+ generated_at: new Date().toISOString(),
499
+ invoices: generatedInvoices,
500
+ };
501
+ fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
502
+ updateGitignore(projectRoot);
503
+ blank();
504
+ blank();
505
+ line(rule('='));
506
+ line(` ${C.success.bold('OK nullpay.json generated')}`);
507
+ line(rule('='));
508
+ blank();
509
+ generatedInvoices.forEach((inv, i) => {
510
+ renderResultCard(inv, i + 1, generatedInvoices.length);
511
+ blank();
512
+ });
513
+ line(rule('-'));
514
+ blank();
515
+ kv('Output', outputPath, C.slate);
516
+ kv('Merchant', resolvedAddress, C.brandDim);
517
+ kv('Deployed', `${generatedInvoices.length} / ${allInvoices.length} invoices`, C.success.bold);
518
+ blank();
519
+ line(` ${C.gold('!')} ${C.slate('nullpay.json added to .gitignore - keep salts private')}`);
520
+ blank();
521
+ line(rule('-'));
522
+ blank();
523
+ line(` ${C.white.bold('Usage')}`);
524
+ blank();
525
+ line(C.dim(` import { NullPay } from '@nullpay/node';`));
526
+ blank();
527
+ line(C.dim(` const nullpay = new NullPay({`));
528
+ line(C.dim(` secretKey: process.env.NULLPAY_SECRET_KEY`));
529
+ line(C.dim(` });`));
530
+ blank();
531
+ line(C.dim(` const session = await nullpay.checkout.sessions.create({`));
532
+ line(C.dim(` nullpay_invoice_name: `) + C.brand(`'${generatedInvoices[0]?.name ?? 'your-invoice-name'}'`) + C.dim(','));
533
+ line(C.dim(` success_url: 'https://yourapp.com/success',`));
534
+ line(C.dim(` cancel_url: 'https://yourapp.com/cancel'`));
535
+ line(C.dim(` });`));
536
+ blank();
537
+ line(rule('='));
538
+ blank();
539
+ line(` ${C.brand.bold('NULLPAY')} ${C.slate('| zk-private invoicing on Aleo')}`);
540
+ blank();
541
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@nullpay/cli",
3
+ "version": "1.0.0",
4
+ "description": "NullPay CLI — Developer onboarding and invoice management tools",
5
+ "main": "dist/cli.js",
6
+ "bin": {
7
+ "nullpay": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "ts-node src/cli.ts"
15
+ },
16
+ "dependencies": {
17
+ "chalk": "^4.1.2",
18
+ "inquirer": "^8.2.6",
19
+ "node-fetch": "^2.7.0",
20
+ "ora": "^5.4.1"
21
+ },
22
+ "devDependencies": {
23
+ "@types/inquirer": "^8.2.10",
24
+ "@types/node": "^20.0.0",
25
+ "@types/node-fetch": "^2.6.13",
26
+ "ts-node": "^10.9.2",
27
+ "typescript": "^5.3.3"
28
+ }
29
+ }