@profullstack/coinpay 0.6.8 → 0.6.11
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/CHANGELOG.md +7 -0
- package/bin/coinpay.js +381 -0
- package/package.json +1 -1
- package/src/auth.js +6 -0
- package/src/client.js +90 -0
- package/src/index.js +28 -0
- package/src/lightning.js +218 -0
- package/src/x402.js +585 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to `@profullstack/coinpay` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.6.11] - 2026-03-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- OAuth client management methods: `listOAuthClients()`, `createOAuthClient()`, `getOAuthClient()`, `deleteOAuthClient()`
|
|
12
|
+
- CLI `coinpay oauth` commands: `list`, `create`, `get`, `delete`
|
|
13
|
+
- OAuth section in SDK documentation
|
|
14
|
+
|
|
8
15
|
## [0.2.0] - 2025-01-15
|
|
9
16
|
|
|
10
17
|
### Added
|
package/bin/coinpay.js
CHANGED
|
@@ -244,6 +244,12 @@ ${colors.cyan}Commands:${colors.reset}
|
|
|
244
244
|
events <id> Get escrow audit log
|
|
245
245
|
auth <id> Authenticate with escrow token
|
|
246
246
|
|
|
247
|
+
${colors.bright}oauth${colors.reset}
|
|
248
|
+
list List OAuth clients
|
|
249
|
+
create Create a new OAuth client
|
|
250
|
+
get <id> Get OAuth client details
|
|
251
|
+
delete <id> Delete an OAuth client
|
|
252
|
+
|
|
247
253
|
${colors.bright}reputation${colors.reset}
|
|
248
254
|
submit Submit a task receipt
|
|
249
255
|
query <agent-did> Query agent reputation
|
|
@@ -2567,6 +2573,147 @@ async function main() {
|
|
|
2567
2573
|
}
|
|
2568
2574
|
|
|
2569
2575
|
try {
|
|
2576
|
+
|
|
2577
|
+
async function handleLightning(subcommand, args, flags) {
|
|
2578
|
+
const client = createClient();
|
|
2579
|
+
|
|
2580
|
+
switch (subcommand) {
|
|
2581
|
+
case 'enable': {
|
|
2582
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2583
|
+
if (!walletId) {
|
|
2584
|
+
console.error(colors.red + 'Error: --wallet-id required' + colors.reset);
|
|
2585
|
+
process.exit(1);
|
|
2586
|
+
}
|
|
2587
|
+
const mnemonic = await getDecryptedMnemonic(flags);
|
|
2588
|
+
const result = await client.lightning.enableWallet({
|
|
2589
|
+
wallet_id: walletId,
|
|
2590
|
+
mnemonic,
|
|
2591
|
+
business_id: flags['business-id'],
|
|
2592
|
+
});
|
|
2593
|
+
console.log(colors.green + '⚡ Lightning wallet enabled!' + colors.reset);
|
|
2594
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2595
|
+
break;
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
case 'address': {
|
|
2599
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2600
|
+
const username = flags['username'] || args[0];
|
|
2601
|
+
if (!walletId) {
|
|
2602
|
+
console.error(colors.red + 'Error: --wallet-id required' + colors.reset);
|
|
2603
|
+
process.exit(1);
|
|
2604
|
+
}
|
|
2605
|
+
if (!username) {
|
|
2606
|
+
// GET current address
|
|
2607
|
+
const result = await client.lightning.getAddress(walletId);
|
|
2608
|
+
if (result.lightning_address) {
|
|
2609
|
+
console.log(colors.green + '⚡ ' + result.lightning_address + colors.reset);
|
|
2610
|
+
} else {
|
|
2611
|
+
console.log('No Lightning Address registered. Use: coinpay ln address --username <name>');
|
|
2612
|
+
}
|
|
2613
|
+
} else {
|
|
2614
|
+
// POST register address
|
|
2615
|
+
const result = await client.lightning.registerAddress({ wallet_id: walletId, username });
|
|
2616
|
+
console.log(colors.green + '⚡ Registered: ' + result.lightning_address + colors.reset);
|
|
2617
|
+
}
|
|
2618
|
+
break;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
case 'address-check': {
|
|
2622
|
+
const username = flags['username'] || args[0];
|
|
2623
|
+
if (!username) {
|
|
2624
|
+
console.error(colors.red + 'Error: --username required' + colors.reset);
|
|
2625
|
+
process.exit(1);
|
|
2626
|
+
}
|
|
2627
|
+
const result = await client.lightning.checkAddressAvailable(username);
|
|
2628
|
+
console.log(result.available
|
|
2629
|
+
? colors.green + '✓ ' + username + ' is available' + colors.reset
|
|
2630
|
+
: colors.red + '✗ ' + username + ' is taken' + colors.reset);
|
|
2631
|
+
break;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
case 'invoice': {
|
|
2635
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2636
|
+
const amount = parseInt(flags['amount'] || args[0]);
|
|
2637
|
+
if (!walletId || !amount) {
|
|
2638
|
+
console.error(colors.red + 'Error: --wallet-id and --amount required' + colors.reset);
|
|
2639
|
+
process.exit(1);
|
|
2640
|
+
}
|
|
2641
|
+
const result = await client.lightning.createInvoice({
|
|
2642
|
+
wallet_id: walletId,
|
|
2643
|
+
amount_sats: amount,
|
|
2644
|
+
description: flags['description'] || flags['desc'] || '',
|
|
2645
|
+
});
|
|
2646
|
+
console.log(colors.green + '⚡ Invoice created' + colors.reset);
|
|
2647
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2648
|
+
break;
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
case 'send': {
|
|
2652
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2653
|
+
const destination = flags['to'] || args[0];
|
|
2654
|
+
const amount = flags['amount'] ? parseInt(flags['amount']) : undefined;
|
|
2655
|
+
if (!walletId || !destination) {
|
|
2656
|
+
console.error(colors.red + 'Error: --wallet-id and --to required' + colors.reset);
|
|
2657
|
+
process.exit(1);
|
|
2658
|
+
}
|
|
2659
|
+
const result = await client.lightning.sendPayment({
|
|
2660
|
+
wallet_id: walletId,
|
|
2661
|
+
destination,
|
|
2662
|
+
amount_sats: amount,
|
|
2663
|
+
});
|
|
2664
|
+
console.log(colors.green + '⚡ Payment sent!' + colors.reset);
|
|
2665
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2666
|
+
break;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
case 'payments': {
|
|
2670
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2671
|
+
if (!walletId) {
|
|
2672
|
+
console.error(colors.red + 'Error: --wallet-id required' + colors.reset);
|
|
2673
|
+
process.exit(1);
|
|
2674
|
+
}
|
|
2675
|
+
const result = await client.lightning.listPayments({
|
|
2676
|
+
wallet_id: walletId,
|
|
2677
|
+
direction: flags['direction'],
|
|
2678
|
+
limit: flags['limit'] ? parseInt(flags['limit']) : undefined,
|
|
2679
|
+
});
|
|
2680
|
+
const payments = result.data?.payments || result.payments || [];
|
|
2681
|
+
if (payments.length === 0) {
|
|
2682
|
+
console.log('No Lightning payments found.');
|
|
2683
|
+
} else {
|
|
2684
|
+
for (const p of payments) {
|
|
2685
|
+
const dir = p.direction === 'outgoing' ? colors.red + '↑ SENT' : colors.green + '↓ RECV';
|
|
2686
|
+
const sats = Math.floor((p.amount_msat || 0) / 1000);
|
|
2687
|
+
console.log(`${dir}${colors.reset} ${sats} sats ${p.status} ${p.created_at}`);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
break;
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
case 'balance': {
|
|
2694
|
+
const walletId = flags['wallet-id'] || flags['wallet'];
|
|
2695
|
+
if (!walletId) {
|
|
2696
|
+
console.error(colors.red + 'Error: --wallet-id required' + colors.reset);
|
|
2697
|
+
process.exit(1);
|
|
2698
|
+
}
|
|
2699
|
+
// Use the web-wallet balance endpoint filtered by LN
|
|
2700
|
+
const result = await client.request(`/web-wallet/balance?wallet_id=${walletId}`);
|
|
2701
|
+
const balances = result.data?.balances || result.balances || [];
|
|
2702
|
+
const ln = balances.find(b => b.chain === 'LN' || b.currency === 'LN');
|
|
2703
|
+
if (ln) {
|
|
2704
|
+
console.log(colors.green + '⚡ ' + ln.balance + ' BTC (' + Math.floor(parseFloat(ln.balance) * 100_000_000) + ' sats)' + colors.reset);
|
|
2705
|
+
} else {
|
|
2706
|
+
console.log('No Lightning balance. Enable Lightning first: coinpay ln enable');
|
|
2707
|
+
}
|
|
2708
|
+
break;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
default:
|
|
2712
|
+
console.log(`Unknown lightning command: ${subcommand}`);
|
|
2713
|
+
showHelp();
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2570
2717
|
switch (command) {
|
|
2571
2718
|
case 'config':
|
|
2572
2719
|
await handleConfig(subcommand, args);
|
|
@@ -2596,6 +2743,11 @@ async function main() {
|
|
|
2596
2743
|
await handleEscrow(subcommand, args, flags);
|
|
2597
2744
|
break;
|
|
2598
2745
|
|
|
2746
|
+
case 'ln':
|
|
2747
|
+
case 'lightning':
|
|
2748
|
+
await handleLightning(subcommand, args, flags);
|
|
2749
|
+
break;
|
|
2750
|
+
|
|
2599
2751
|
case 'webhook':
|
|
2600
2752
|
await handleWebhook(subcommand, args, flags);
|
|
2601
2753
|
break;
|
|
@@ -2615,6 +2767,14 @@ async function main() {
|
|
|
2615
2767
|
case 'reputation':
|
|
2616
2768
|
await handleReputation(subcommand, args, flags);
|
|
2617
2769
|
break;
|
|
2770
|
+
|
|
2771
|
+
case 'x402':
|
|
2772
|
+
await handleX402(subcommand, args, flags);
|
|
2773
|
+
break;
|
|
2774
|
+
|
|
2775
|
+
case 'oauth':
|
|
2776
|
+
await handleOAuth(subcommand, args, flags);
|
|
2777
|
+
break;
|
|
2618
2778
|
|
|
2619
2779
|
default:
|
|
2620
2780
|
print.error(`Unknown command: ${command}`);
|
|
@@ -2630,4 +2790,225 @@ async function main() {
|
|
|
2630
2790
|
}
|
|
2631
2791
|
}
|
|
2632
2792
|
|
|
2793
|
+
/**
|
|
2794
|
+
* OAuth commands
|
|
2795
|
+
*/
|
|
2796
|
+
async function handleOAuth(subcommand, args, flags) {
|
|
2797
|
+
const client = createClient();
|
|
2798
|
+
|
|
2799
|
+
switch (subcommand) {
|
|
2800
|
+
case 'list': {
|
|
2801
|
+
const result = await client.listOAuthClients();
|
|
2802
|
+
const clients = result.clients || [];
|
|
2803
|
+
|
|
2804
|
+
if (clients.length === 0) {
|
|
2805
|
+
print.info('No OAuth clients found.');
|
|
2806
|
+
} else {
|
|
2807
|
+
console.log(`\n${colors.bright}OAuth Clients${colors.reset} (${clients.length})\n`);
|
|
2808
|
+
for (const c of clients) {
|
|
2809
|
+
console.log(` ${colors.bright}${c.name}${colors.reset}`);
|
|
2810
|
+
console.log(` Client ID: ${c.client_id}`);
|
|
2811
|
+
if (c.description) console.log(` Description: ${c.description}`);
|
|
2812
|
+
if (c.redirect_uris) console.log(` Redirect URIs: ${c.redirect_uris.join(', ')}`);
|
|
2813
|
+
if (c.scopes) console.log(` Scopes: ${Array.isArray(c.scopes) ? c.scopes.join(', ') : c.scopes}`);
|
|
2814
|
+
console.log();
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
if (flags.json) print.json(result);
|
|
2819
|
+
break;
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
case 'create': {
|
|
2823
|
+
const name = flags.name;
|
|
2824
|
+
const redirectUri = flags['redirect-uri'];
|
|
2825
|
+
const scope = flags.scope;
|
|
2826
|
+
const description = flags.description;
|
|
2827
|
+
|
|
2828
|
+
if (!name || !redirectUri) {
|
|
2829
|
+
print.error('Required: --name, --redirect-uri');
|
|
2830
|
+
print.info('Example: coinpay oauth create --name "My App" --redirect-uri https://myapp.com/callback --scope openid,profile');
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2834
|
+
const redirectUris = redirectUri.split(',').map(u => u.trim());
|
|
2835
|
+
const scopes = scope ? scope.split(',').map(s => s.trim()) : undefined;
|
|
2836
|
+
|
|
2837
|
+
const result = await client.createOAuthClient({
|
|
2838
|
+
name,
|
|
2839
|
+
redirectUris,
|
|
2840
|
+
scopes,
|
|
2841
|
+
description,
|
|
2842
|
+
});
|
|
2843
|
+
|
|
2844
|
+
print.success('OAuth client created');
|
|
2845
|
+
if (result.client_id) {
|
|
2846
|
+
console.log(`\n ${colors.bright}Client ID:${colors.reset} ${result.client_id}`);
|
|
2847
|
+
}
|
|
2848
|
+
if (result.client_secret) {
|
|
2849
|
+
console.log(` ${colors.bright}Client Secret:${colors.reset} ${colors.yellow}${result.client_secret}${colors.reset}`);
|
|
2850
|
+
console.log();
|
|
2851
|
+
print.warn('Save the client secret — it is only shown once!');
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
if (flags.json) print.json(result);
|
|
2855
|
+
break;
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
case 'get': {
|
|
2859
|
+
const id = args[0];
|
|
2860
|
+
if (!id) {
|
|
2861
|
+
print.error('OAuth client ID required');
|
|
2862
|
+
print.info('Usage: coinpay oauth get <client-id>');
|
|
2863
|
+
return;
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
const result = await client.getOAuthClient(id);
|
|
2867
|
+
|
|
2868
|
+
print.success(`OAuth Client: ${result.name || id}`);
|
|
2869
|
+
if (result.client_id) print.info(` Client ID: ${result.client_id}`);
|
|
2870
|
+
if (result.description) print.info(` Description: ${result.description}`);
|
|
2871
|
+
if (result.redirect_uris) print.info(` Redirect URIs: ${result.redirect_uris.join(', ')}`);
|
|
2872
|
+
if (result.scopes) print.info(` Scopes: ${Array.isArray(result.scopes) ? result.scopes.join(', ') : result.scopes}`);
|
|
2873
|
+
if (result.created_at) print.info(` Created: ${result.created_at}`);
|
|
2874
|
+
|
|
2875
|
+
if (flags.json) print.json(result);
|
|
2876
|
+
break;
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
case 'delete': {
|
|
2880
|
+
const id = args[0];
|
|
2881
|
+
if (!id) {
|
|
2882
|
+
print.error('OAuth client ID required');
|
|
2883
|
+
print.info('Usage: coinpay oauth delete <client-id>');
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
await client.deleteOAuthClient(id);
|
|
2888
|
+
print.success(`OAuth client ${id} deleted`);
|
|
2889
|
+
break;
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
default:
|
|
2893
|
+
print.error(`Unknown oauth command: ${subcommand}`);
|
|
2894
|
+
print.info('Available: list, create, get, delete');
|
|
2895
|
+
process.exit(1);
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
/**
|
|
2900
|
+
* Handle x402 commands
|
|
2901
|
+
*/
|
|
2902
|
+
async function handleX402(subcommand, args, flags) {
|
|
2903
|
+
const config = loadConfig();
|
|
2904
|
+
|
|
2905
|
+
if (!subcommand) {
|
|
2906
|
+
console.log(`
|
|
2907
|
+
${colors.bright}x402 Payment Protocol${colors.reset}
|
|
2908
|
+
|
|
2909
|
+
Usage: coinpay x402 <command> [options]
|
|
2910
|
+
|
|
2911
|
+
Commands:
|
|
2912
|
+
status Check x402 facilitator status
|
|
2913
|
+
test Test x402 flow against an endpoint
|
|
2914
|
+
|
|
2915
|
+
Options:
|
|
2916
|
+
--url <url> Endpoint URL for testing
|
|
2917
|
+
--network <net> Network: base, ethereum, polygon (default: base)
|
|
2918
|
+
--amount <amt> Amount in USDC smallest unit (default: 1000000 = 1 USDC)
|
|
2919
|
+
`);
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
switch (subcommand) {
|
|
2924
|
+
case 'status': {
|
|
2925
|
+
const apiKey = flags['api-key'] || config.apiKey;
|
|
2926
|
+
const baseUrl = flags['base-url'] || config.baseUrl || 'https://coinpayportal.com';
|
|
2927
|
+
|
|
2928
|
+
print.info('Checking x402 facilitator status...');
|
|
2929
|
+
|
|
2930
|
+
try {
|
|
2931
|
+
const response = await fetch(`${baseUrl}/api/x402/verify`, {
|
|
2932
|
+
method: 'POST',
|
|
2933
|
+
headers: {
|
|
2934
|
+
'Content-Type': 'application/json',
|
|
2935
|
+
...(apiKey ? { 'X-API-Key': apiKey } : {}),
|
|
2936
|
+
},
|
|
2937
|
+
body: JSON.stringify({ payment: { test: true } }),
|
|
2938
|
+
});
|
|
2939
|
+
|
|
2940
|
+
if (response.status === 401) {
|
|
2941
|
+
print.error('API key required or invalid. Set with: coinpay config set apiKey <key>');
|
|
2942
|
+
} else if (response.status === 400) {
|
|
2943
|
+
print.success('x402 facilitator is reachable and responding');
|
|
2944
|
+
print.info(`Facilitator URL: ${baseUrl}/api/x402`);
|
|
2945
|
+
print.info('Networks: Base, Ethereum, Polygon (Solana coming soon)');
|
|
2946
|
+
} else {
|
|
2947
|
+
print.warn(`Unexpected response: HTTP ${response.status}`);
|
|
2948
|
+
}
|
|
2949
|
+
} catch (err) {
|
|
2950
|
+
print.error(`Cannot reach facilitator at ${baseUrl}: ${err.message}`);
|
|
2951
|
+
}
|
|
2952
|
+
break;
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
case 'test': {
|
|
2956
|
+
const url = flags.url || args[0];
|
|
2957
|
+
if (!url) {
|
|
2958
|
+
print.error('URL required: coinpay x402 test --url http://localhost:3000/api/premium');
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
const network = flags.network || 'base';
|
|
2963
|
+
const amount = flags.amount || '1000000';
|
|
2964
|
+
|
|
2965
|
+
print.info(`Testing x402 flow against ${url}`);
|
|
2966
|
+
|
|
2967
|
+
// Step 1: Request without payment
|
|
2968
|
+
try {
|
|
2969
|
+
print.info('Step 1: Requesting resource without payment...');
|
|
2970
|
+
const response = await fetch(url);
|
|
2971
|
+
|
|
2972
|
+
if (response.status === 402) {
|
|
2973
|
+
const body = await response.json();
|
|
2974
|
+
print.success('Received HTTP 402 Payment Required');
|
|
2975
|
+
|
|
2976
|
+
if (body.x402Version) {
|
|
2977
|
+
print.success(`x402 version: ${body.x402Version}`);
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
if (body.accepts && body.accepts.length > 0) {
|
|
2981
|
+
print.success(`${body.accepts.length} payment method(s) offered:`);
|
|
2982
|
+
for (const accept of body.accepts) {
|
|
2983
|
+
const label = accept.extra?.label || `${accept.asset} on ${accept.network}`;
|
|
2984
|
+
const symbol = accept.extra?.assetSymbol || accept.asset || '';
|
|
2985
|
+
print.info(` • ${label}`);
|
|
2986
|
+
print.info(` Amount: ${accept.maxAmountRequired} ${symbol}`);
|
|
2987
|
+
print.info(` Pay to: ${accept.payTo || 'not specified'}`);
|
|
2988
|
+
}
|
|
2989
|
+
const facilitator = body.accepts[0]?.extra?.facilitator;
|
|
2990
|
+
if (facilitator) {
|
|
2991
|
+
print.info(` Facilitator: ${facilitator}`);
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
print.success('x402 endpoint is correctly configured!');
|
|
2996
|
+
print.warn('Note: Full payment test requires a funded wallet. Use the dashboard for end-to-end testing.');
|
|
2997
|
+
} else if (response.status === 200) {
|
|
2998
|
+
print.warn('Endpoint returned 200 OK — no payment required (is x402 middleware active?)');
|
|
2999
|
+
} else {
|
|
3000
|
+
print.warn(`Unexpected status: HTTP ${response.status}`);
|
|
3001
|
+
}
|
|
3002
|
+
} catch (err) {
|
|
3003
|
+
print.error(`Failed to reach ${url}: ${err.message}`);
|
|
3004
|
+
}
|
|
3005
|
+
break;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
3008
|
+
default:
|
|
3009
|
+
print.error(`Unknown x402 command: ${subcommand}`);
|
|
3010
|
+
print.info('Available: status, test');
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
|
|
2633
3014
|
main().then(() => process.exit(0));
|
package/package.json
CHANGED
package/src/auth.js
CHANGED
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
* @returns {Promise<Object>} { success: boolean, merchant: Object, token: string }
|
|
19
19
|
*/
|
|
20
20
|
export async function registerMerchant(client, { email, password, name }) {
|
|
21
|
+
if (!email) throw new Error('Email is required');
|
|
22
|
+
if (!password) throw new Error('Password is required');
|
|
23
|
+
|
|
21
24
|
return client.requestUnauthenticated('/auth/register', {
|
|
22
25
|
method: 'POST',
|
|
23
26
|
body: JSON.stringify({
|
|
@@ -37,6 +40,9 @@ export async function registerMerchant(client, { email, password, name }) {
|
|
|
37
40
|
* @returns {Promise<Object>} { success: boolean, merchant: Object, token: string }
|
|
38
41
|
*/
|
|
39
42
|
export async function loginMerchant(client, { email, password }) {
|
|
43
|
+
if (!email) throw new Error('Email is required');
|
|
44
|
+
if (!password) throw new Error('Password is required');
|
|
45
|
+
|
|
40
46
|
return client.requestUnauthenticated('/auth/login', {
|
|
41
47
|
method: 'POST',
|
|
42
48
|
body: JSON.stringify({
|
package/src/client.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Main client class for interacting with the CoinPay API
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { LightningClient } from './lightning.js';
|
|
7
|
+
|
|
6
8
|
const DEFAULT_BASE_URL = 'https://coinpayportal.com/api';
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -24,6 +26,9 @@ export class CoinPayClient {
|
|
|
24
26
|
this.#apiKey = apiKey;
|
|
25
27
|
this.#baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
26
28
|
this.#timeout = timeout;
|
|
29
|
+
|
|
30
|
+
/** @type {LightningClient} Lightning/BOLT12 operations */
|
|
31
|
+
this.lightning = new LightningClient(this);
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
/**
|
|
@@ -821,6 +826,91 @@ export class CoinPayClient {
|
|
|
821
826
|
method: 'DELETE',
|
|
822
827
|
});
|
|
823
828
|
}
|
|
829
|
+
|
|
830
|
+
// ── OAuth Client Methods ─────────────────────────────────
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* List OAuth clients
|
|
834
|
+
*
|
|
835
|
+
* Returns all OAuth clients registered under your account.
|
|
836
|
+
*
|
|
837
|
+
* @returns {Promise<Object>} List of OAuth clients
|
|
838
|
+
*
|
|
839
|
+
* @example
|
|
840
|
+
* const result = await client.listOAuthClients();
|
|
841
|
+
* for (const app of result.clients) {
|
|
842
|
+
* console.log(app.client_id, app.name);
|
|
843
|
+
* }
|
|
844
|
+
*/
|
|
845
|
+
async listOAuthClients() {
|
|
846
|
+
return this.request('/oauth/clients');
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Create a new OAuth client
|
|
851
|
+
*
|
|
852
|
+
* Registers a new OAuth application. Returns the client_id and client_secret.
|
|
853
|
+
* Store the client_secret securely — it is only shown once.
|
|
854
|
+
*
|
|
855
|
+
* @param {Object} params - OAuth client parameters
|
|
856
|
+
* @param {string} params.name - Application name
|
|
857
|
+
* @param {string[]} params.redirectUris - Authorized redirect URIs
|
|
858
|
+
* @param {string[]} [params.scopes] - Requested scopes (e.g., ['openid', 'profile'])
|
|
859
|
+
* @param {string} [params.description] - Application description
|
|
860
|
+
* @returns {Promise<Object>} Created OAuth client with client_id and client_secret
|
|
861
|
+
*
|
|
862
|
+
* @example
|
|
863
|
+
* const app = await client.createOAuthClient({
|
|
864
|
+
* name: 'My App',
|
|
865
|
+
* redirectUris: ['https://myapp.com/callback'],
|
|
866
|
+
* scopes: ['openid', 'profile'],
|
|
867
|
+
* description: 'My awesome application',
|
|
868
|
+
* });
|
|
869
|
+
* console.log('Client ID:', app.client_id);
|
|
870
|
+
* console.log('Client Secret:', app.client_secret);
|
|
871
|
+
*/
|
|
872
|
+
async createOAuthClient({ name, redirectUris, scopes, description }) {
|
|
873
|
+
return this.request('/oauth/clients', {
|
|
874
|
+
method: 'POST',
|
|
875
|
+
body: JSON.stringify({
|
|
876
|
+
name,
|
|
877
|
+
redirect_uris: redirectUris,
|
|
878
|
+
scopes,
|
|
879
|
+
description,
|
|
880
|
+
}),
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/**
|
|
885
|
+
* Get an OAuth client by ID
|
|
886
|
+
*
|
|
887
|
+
* @param {string} id - OAuth client ID
|
|
888
|
+
* @returns {Promise<Object>} OAuth client details
|
|
889
|
+
*
|
|
890
|
+
* @example
|
|
891
|
+
* const app = await client.getOAuthClient('oac_abc123');
|
|
892
|
+
* console.log(app.name, app.redirect_uris);
|
|
893
|
+
*/
|
|
894
|
+
async getOAuthClient(id) {
|
|
895
|
+
return this.request(`/oauth/clients/${id}`);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Delete an OAuth client
|
|
900
|
+
*
|
|
901
|
+
* Permanently removes an OAuth client and revokes all associated tokens.
|
|
902
|
+
*
|
|
903
|
+
* @param {string} id - OAuth client ID
|
|
904
|
+
* @returns {Promise<Object>} Deletion confirmation
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* await client.deleteOAuthClient('oac_abc123');
|
|
908
|
+
*/
|
|
909
|
+
async deleteOAuthClient(id) {
|
|
910
|
+
return this.request(`/oauth/clients/${id}`, {
|
|
911
|
+
method: 'DELETE',
|
|
912
|
+
});
|
|
913
|
+
}
|
|
824
914
|
}
|
|
825
915
|
|
|
826
916
|
export default CoinPayClient;
|
package/src/index.js
CHANGED
|
@@ -147,6 +147,21 @@ import {
|
|
|
147
147
|
SubscriptionStatus,
|
|
148
148
|
} from './subscriptions.js';
|
|
149
149
|
|
|
150
|
+
// Lightning
|
|
151
|
+
import { LightningClient } from './lightning.js';
|
|
152
|
+
|
|
153
|
+
// x402 Protocol
|
|
154
|
+
import {
|
|
155
|
+
createX402Middleware,
|
|
156
|
+
verifyX402Payment,
|
|
157
|
+
settleX402Payment,
|
|
158
|
+
buildPaymentRequired,
|
|
159
|
+
convertUsdToAssetAmount,
|
|
160
|
+
PAYMENT_METHODS,
|
|
161
|
+
USDC_CONTRACTS,
|
|
162
|
+
CHAIN_IDS,
|
|
163
|
+
} from './x402.js';
|
|
164
|
+
|
|
150
165
|
export {
|
|
151
166
|
// Client
|
|
152
167
|
CoinPayClient,
|
|
@@ -235,11 +250,24 @@ export {
|
|
|
235
250
|
linkDid,
|
|
236
251
|
getMyDid,
|
|
237
252
|
|
|
253
|
+
// Lightning
|
|
254
|
+
LightningClient,
|
|
255
|
+
|
|
238
256
|
// Auth
|
|
239
257
|
registerMerchant,
|
|
240
258
|
loginMerchant,
|
|
241
259
|
getMe,
|
|
242
260
|
|
|
261
|
+
// x402 Protocol
|
|
262
|
+
createX402Middleware,
|
|
263
|
+
verifyX402Payment,
|
|
264
|
+
settleX402Payment,
|
|
265
|
+
buildPaymentRequired,
|
|
266
|
+
convertUsdToAssetAmount,
|
|
267
|
+
PAYMENT_METHODS,
|
|
268
|
+
USDC_CONTRACTS,
|
|
269
|
+
CHAIN_IDS,
|
|
270
|
+
|
|
243
271
|
// Webhook utilities
|
|
244
272
|
verifyWebhookSignature,
|
|
245
273
|
generateWebhookSignature,
|