@profullstack/coinpay 0.6.6 → 0.6.8
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/bin/coinpay.js +351 -1
- package/package.json +1 -1
- package/src/auth.d.ts +72 -0
- package/src/auth.js +58 -0
- package/src/card-payments.js +127 -0
- package/src/client.js +142 -6
- package/src/index.js +87 -0
- package/src/payouts.js +79 -0
- package/src/subscriptions.js +231 -0
package/bin/coinpay.js
CHANGED
|
@@ -119,6 +119,13 @@ function createClient() {
|
|
|
119
119
|
return new CoinPayClient({ apiKey, baseUrl: getBaseUrl() });
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Create unauthenticated client for auth operations
|
|
124
|
+
*/
|
|
125
|
+
function createUnauthenticatedClient() {
|
|
126
|
+
return new CoinPayClient({ baseUrl: getBaseUrl() });
|
|
127
|
+
}
|
|
128
|
+
|
|
122
129
|
/**
|
|
123
130
|
* Parse command line arguments
|
|
124
131
|
*/
|
|
@@ -171,6 +178,11 @@ ${colors.cyan}Commands:${colors.reset}
|
|
|
171
178
|
set-url <base-url> Set custom API URL
|
|
172
179
|
show Show current configuration
|
|
173
180
|
|
|
181
|
+
${colors.bright}auth${colors.reset}
|
|
182
|
+
register Register new merchant account
|
|
183
|
+
login Login to merchant account
|
|
184
|
+
me Show current merchant info
|
|
185
|
+
|
|
174
186
|
${colors.bright}payment${colors.reset}
|
|
175
187
|
create Create a new payment
|
|
176
188
|
get <id> Get payment details
|
|
@@ -208,6 +220,11 @@ ${colors.cyan}Commands:${colors.reset}
|
|
|
208
220
|
status <swap-id> Check swap status
|
|
209
221
|
history Swap history
|
|
210
222
|
|
|
223
|
+
${colors.bright}payout${colors.reset}
|
|
224
|
+
create Create a payout to your Stripe account
|
|
225
|
+
list List payouts
|
|
226
|
+
get <id> Get payout details
|
|
227
|
+
|
|
211
228
|
${colors.bright}card${colors.reset}
|
|
212
229
|
create Create a card payment via Stripe
|
|
213
230
|
get <id> Get card payment status
|
|
@@ -236,6 +253,8 @@ ${colors.cyan}Commands:${colors.reset}
|
|
|
236
253
|
badge [did] Get embeddable reputation badge URL
|
|
237
254
|
verify <id> Verify a credential
|
|
238
255
|
revocations List revoked credentials
|
|
256
|
+
did claim Claim (auto-generate) a new DID
|
|
257
|
+
did me Show your current DID
|
|
239
258
|
issuer register Register platform issuer (--name, --domain)
|
|
240
259
|
issuer list List your platform issuers
|
|
241
260
|
issuer rotate Rotate issuer API key (--id)
|
|
@@ -1593,9 +1612,249 @@ async function handleEscrow(subcommand, args, flags) {
|
|
|
1593
1612
|
break;
|
|
1594
1613
|
}
|
|
1595
1614
|
|
|
1615
|
+
case 'series': {
|
|
1616
|
+
const seriesCmd = args[0];
|
|
1617
|
+
const seriesId = args[1];
|
|
1618
|
+
|
|
1619
|
+
switch (seriesCmd) {
|
|
1620
|
+
case 'create': {
|
|
1621
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1622
|
+
const ask = (q) => new Promise((r) => rl.question(q, r));
|
|
1623
|
+
|
|
1624
|
+
try {
|
|
1625
|
+
const businessId = flags['business-id'] || await ask('Business ID: ');
|
|
1626
|
+
const paymentMethod = flags['payment-method'] || await ask('Payment method (crypto/card): ');
|
|
1627
|
+
const amount = flags.amount || await ask('Amount (cents for card, satoshis-equiv for crypto): ');
|
|
1628
|
+
const currency = flags.currency || await ask('Currency [USD]: ') || 'USD';
|
|
1629
|
+
const interval = flags.interval || await ask('Interval (weekly/biweekly/monthly): ');
|
|
1630
|
+
const maxPeriods = flags['max-periods'] || await ask('Max periods (blank=infinite): ') || undefined;
|
|
1631
|
+
const customerEmail = flags.email || await ask('Customer email (optional): ') || undefined;
|
|
1632
|
+
const description = flags.description || await ask('Description (optional): ') || undefined;
|
|
1633
|
+
|
|
1634
|
+
const params = {
|
|
1635
|
+
business_id: businessId,
|
|
1636
|
+
payment_method: paymentMethod,
|
|
1637
|
+
amount: parseInt(amount),
|
|
1638
|
+
currency,
|
|
1639
|
+
interval,
|
|
1640
|
+
max_periods: maxPeriods ? parseInt(maxPeriods) : undefined,
|
|
1641
|
+
customer_email: customerEmail,
|
|
1642
|
+
description,
|
|
1643
|
+
};
|
|
1644
|
+
|
|
1645
|
+
if (paymentMethod === 'crypto') {
|
|
1646
|
+
params.coin = flags.coin || await ask('Coin (BTC/ETH/SOL/etc): ');
|
|
1647
|
+
params.beneficiary_address = flags.beneficiary || await ask('Beneficiary address: ');
|
|
1648
|
+
params.depositor_address = flags.depositor || await ask('Depositor address: ');
|
|
1649
|
+
} else {
|
|
1650
|
+
params.stripe_account_id = flags['stripe-account'] || await ask('Stripe account ID: ');
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
rl.close();
|
|
1654
|
+
|
|
1655
|
+
const series = await client.createEscrowSeries(params);
|
|
1656
|
+
print.success(`Escrow series created: ${series.id}`);
|
|
1657
|
+
print.info(` Status: ${series.status}`);
|
|
1658
|
+
print.info(` Interval: ${series.interval}`);
|
|
1659
|
+
print.info(` Amount: ${series.amount} ${series.currency}`);
|
|
1660
|
+
if (flags.json) print.json(series);
|
|
1661
|
+
} catch (e) {
|
|
1662
|
+
rl.close();
|
|
1663
|
+
throw e;
|
|
1664
|
+
}
|
|
1665
|
+
break;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
case 'list': {
|
|
1669
|
+
const businessId = flags['business-id'];
|
|
1670
|
+
if (!businessId) { print.error('--business-id required'); process.exit(1); }
|
|
1671
|
+
const result = await client.listEscrowSeries(businessId, flags.status);
|
|
1672
|
+
print.info(`Series (${result.series.length}):`);
|
|
1673
|
+
for (const s of result.series) {
|
|
1674
|
+
console.log(` ${s.id} | ${s.status} | ${s.payment_method} | ${s.interval} | ${s.amount} ${s.currency}`);
|
|
1675
|
+
}
|
|
1676
|
+
if (flags.json) print.json(result);
|
|
1677
|
+
break;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
case 'get': {
|
|
1681
|
+
if (!seriesId) { print.error('Series ID required'); process.exit(1); }
|
|
1682
|
+
const result = await client.getEscrowSeries(seriesId);
|
|
1683
|
+
print.success(`Series ${result.series.id}`);
|
|
1684
|
+
print.info(` Status: ${result.series.status}`);
|
|
1685
|
+
print.info(` Method: ${result.series.payment_method}`);
|
|
1686
|
+
print.info(` Amount: ${result.series.amount} ${result.series.currency}`);
|
|
1687
|
+
print.info(` Interval: ${result.series.interval}`);
|
|
1688
|
+
print.info(` Periods: ${result.series.periods_completed}${result.series.max_periods ? '/' + result.series.max_periods : ''}`);
|
|
1689
|
+
print.info(` Next charge: ${result.series.next_charge_at}`);
|
|
1690
|
+
print.info(` Crypto escrows: ${result.escrows.crypto.length}`);
|
|
1691
|
+
print.info(` Stripe escrows: ${result.escrows.stripe.length}`);
|
|
1692
|
+
if (flags.json) print.json(result);
|
|
1693
|
+
break;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
case 'pause': {
|
|
1697
|
+
if (!seriesId) { print.error('Series ID required'); process.exit(1); }
|
|
1698
|
+
await client.updateEscrowSeries(seriesId, { status: 'paused' });
|
|
1699
|
+
print.success(`Series ${seriesId} paused`);
|
|
1700
|
+
break;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
case 'resume': {
|
|
1704
|
+
if (!seriesId) { print.error('Series ID required'); process.exit(1); }
|
|
1705
|
+
await client.updateEscrowSeries(seriesId, { status: 'active' });
|
|
1706
|
+
print.success(`Series ${seriesId} resumed`);
|
|
1707
|
+
break;
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
case 'cancel': {
|
|
1711
|
+
if (!seriesId) { print.error('Series ID required'); process.exit(1); }
|
|
1712
|
+
await client.cancelEscrowSeries(seriesId);
|
|
1713
|
+
print.success(`Series ${seriesId} cancelled`);
|
|
1714
|
+
break;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
default:
|
|
1718
|
+
print.error(`Unknown series command: ${seriesCmd}`);
|
|
1719
|
+
print.info('Available: create, list, get, pause, resume, cancel');
|
|
1720
|
+
process.exit(1);
|
|
1721
|
+
}
|
|
1722
|
+
break;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1596
1725
|
default:
|
|
1597
1726
|
print.error(`Unknown escrow command: ${subcommand}`);
|
|
1598
|
-
print.info('Available: create, get, list, release, refund, dispute, events, auth');
|
|
1727
|
+
print.info('Available: create, get, list, release, refund, dispute, events, auth, series');
|
|
1728
|
+
process.exit(1);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
/**
|
|
1733
|
+
* Auth commands
|
|
1734
|
+
*/
|
|
1735
|
+
async function handleAuth(subcommand, args, flags) {
|
|
1736
|
+
const config = loadConfig();
|
|
1737
|
+
|
|
1738
|
+
switch (subcommand) {
|
|
1739
|
+
case 'register': {
|
|
1740
|
+
const email = flags.email;
|
|
1741
|
+
const password = flags.password;
|
|
1742
|
+
const name = flags.name;
|
|
1743
|
+
|
|
1744
|
+
if (!email || !password) {
|
|
1745
|
+
print.error('Required: --email <email> --password <password>');
|
|
1746
|
+
print.info('Example: coinpay auth register --email user@example.com --password mysecret --name "My Company"');
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
try {
|
|
1751
|
+
const client = createUnauthenticatedClient();
|
|
1752
|
+
const { registerMerchant } = await import('../src/auth.js');
|
|
1753
|
+
const result = await registerMerchant(client, { email, password, name });
|
|
1754
|
+
|
|
1755
|
+
if (result.success) {
|
|
1756
|
+
// Store the JWT token in config
|
|
1757
|
+
config.jwtToken = result.token;
|
|
1758
|
+
saveConfig(config);
|
|
1759
|
+
|
|
1760
|
+
print.success('Registration successful!');
|
|
1761
|
+
print.info(`Merchant ID: ${result.merchant.id}`);
|
|
1762
|
+
print.info(`Email: ${result.merchant.email}`);
|
|
1763
|
+
if (result.merchant.name) {
|
|
1764
|
+
print.info(`Name: ${result.merchant.name}`);
|
|
1765
|
+
}
|
|
1766
|
+
print.success('JWT token saved to config');
|
|
1767
|
+
} else {
|
|
1768
|
+
print.error(result.error || 'Registration failed');
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
if (flags.json) {
|
|
1772
|
+
print.json(result);
|
|
1773
|
+
}
|
|
1774
|
+
} catch (error) {
|
|
1775
|
+
print.error(error.message);
|
|
1776
|
+
}
|
|
1777
|
+
break;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
case 'login': {
|
|
1781
|
+
const email = flags.email;
|
|
1782
|
+
const password = flags.password;
|
|
1783
|
+
|
|
1784
|
+
if (!email || !password) {
|
|
1785
|
+
print.error('Required: --email <email> --password <password>');
|
|
1786
|
+
print.info('Example: coinpay auth login --email user@example.com --password mysecret');
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
try {
|
|
1791
|
+
const client = createUnauthenticatedClient();
|
|
1792
|
+
const { loginMerchant } = await import('../src/auth.js');
|
|
1793
|
+
const result = await loginMerchant(client, { email, password });
|
|
1794
|
+
|
|
1795
|
+
if (result.success) {
|
|
1796
|
+
// Store the JWT token in config
|
|
1797
|
+
config.jwtToken = result.token;
|
|
1798
|
+
saveConfig(config);
|
|
1799
|
+
|
|
1800
|
+
print.success('Login successful!');
|
|
1801
|
+
print.info(`Merchant ID: ${result.merchant.id}`);
|
|
1802
|
+
print.info(`Email: ${result.merchant.email}`);
|
|
1803
|
+
if (result.merchant.name) {
|
|
1804
|
+
print.info(`Name: ${result.merchant.name}`);
|
|
1805
|
+
}
|
|
1806
|
+
print.success('JWT token saved to config');
|
|
1807
|
+
} else {
|
|
1808
|
+
print.error(result.error || 'Login failed');
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
if (flags.json) {
|
|
1812
|
+
print.json(result);
|
|
1813
|
+
}
|
|
1814
|
+
} catch (error) {
|
|
1815
|
+
print.error(error.message);
|
|
1816
|
+
}
|
|
1817
|
+
break;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
case 'me': {
|
|
1821
|
+
try {
|
|
1822
|
+
// Check if we have a JWT token
|
|
1823
|
+
const jwtToken = config.jwtToken;
|
|
1824
|
+
if (!jwtToken) {
|
|
1825
|
+
print.error('Not authenticated. Run: coinpay auth login');
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
// Create client with JWT token instead of API key
|
|
1830
|
+
const client = new CoinPayClient({ apiKey: jwtToken, baseUrl: getBaseUrl() });
|
|
1831
|
+
const { getMe } = await import('../src/auth.js');
|
|
1832
|
+
const result = await getMe(client);
|
|
1833
|
+
|
|
1834
|
+
print.success('Current merchant:');
|
|
1835
|
+
print.info(`ID: ${result.id}`);
|
|
1836
|
+
print.info(`Email: ${result.email}`);
|
|
1837
|
+
if (result.name) {
|
|
1838
|
+
print.info(`Name: ${result.name}`);
|
|
1839
|
+
}
|
|
1840
|
+
print.info(`Created: ${result.created_at}`);
|
|
1841
|
+
|
|
1842
|
+
if (flags.json) {
|
|
1843
|
+
print.json(result);
|
|
1844
|
+
}
|
|
1845
|
+
} catch (error) {
|
|
1846
|
+
if (error.message.includes('401') || error.message.includes('403')) {
|
|
1847
|
+
print.error('Authentication failed. Please run: coinpay auth login');
|
|
1848
|
+
} else {
|
|
1849
|
+
print.error(error.message);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
break;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
default:
|
|
1856
|
+
print.error(`Unknown auth command: ${subcommand}`);
|
|
1857
|
+
print.info('Available: register, login, me');
|
|
1599
1858
|
process.exit(1);
|
|
1600
1859
|
}
|
|
1601
1860
|
}
|
|
@@ -1781,6 +2040,21 @@ async function handleReputation(subcommand, args, flags) {
|
|
|
1781
2040
|
print.error(result.error || 'Failed to link DID');
|
|
1782
2041
|
}
|
|
1783
2042
|
|
|
2043
|
+
if (flags.json) print.json(result);
|
|
2044
|
+
} else if (didSubcommand === 'me') {
|
|
2045
|
+
// Show current DID
|
|
2046
|
+
const { getMyDid } = await import('../src/reputation.js');
|
|
2047
|
+
const result = await getMyDid(client);
|
|
2048
|
+
|
|
2049
|
+
if (result.did) {
|
|
2050
|
+
print.success(`Your DID: ${result.did}`);
|
|
2051
|
+
console.log(` Public Key: ${result.public_key}`);
|
|
2052
|
+
console.log(` Verified: ${result.verified}`);
|
|
2053
|
+
console.log(` Created: ${result.created_at}`);
|
|
2054
|
+
} else {
|
|
2055
|
+
print.info('No DID found. Run: coinpay reputation did claim');
|
|
2056
|
+
}
|
|
2057
|
+
|
|
1784
2058
|
if (flags.json) print.json(result);
|
|
1785
2059
|
} else {
|
|
1786
2060
|
// Default: show current DID
|
|
@@ -2003,6 +2277,74 @@ async function handleReputation(subcommand, args, flags) {
|
|
|
2003
2277
|
}
|
|
2004
2278
|
}
|
|
2005
2279
|
|
|
2280
|
+
/**
|
|
2281
|
+
* Payout commands
|
|
2282
|
+
*/
|
|
2283
|
+
async function handlePayout(subcommand, args, flags) {
|
|
2284
|
+
const client = createClient();
|
|
2285
|
+
|
|
2286
|
+
switch (subcommand) {
|
|
2287
|
+
case 'create': {
|
|
2288
|
+
const amount = parseInt(flags.amount);
|
|
2289
|
+
if (!amount || amount <= 0) {
|
|
2290
|
+
print.error('--amount is required (in cents, e.g., 5000 for $50.00)');
|
|
2291
|
+
print.info('Example: coinpay payout create --amount 5000 --description "Weekly payout"');
|
|
2292
|
+
process.exit(1);
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
const result = await client.createPayout({
|
|
2296
|
+
amount,
|
|
2297
|
+
currency: flags.currency || 'usd',
|
|
2298
|
+
description: flags.description,
|
|
2299
|
+
metadata: flags.metadata ? JSON.parse(flags.metadata) : undefined,
|
|
2300
|
+
});
|
|
2301
|
+
|
|
2302
|
+
print.success('Payout created');
|
|
2303
|
+
print.json(result);
|
|
2304
|
+
break;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
case 'list': {
|
|
2308
|
+
const result = await client.listPayouts({
|
|
2309
|
+
status: flags.status,
|
|
2310
|
+
dateFrom: flags['date-from'] || flags.dateFrom,
|
|
2311
|
+
dateTo: flags['date-to'] || flags.dateTo,
|
|
2312
|
+
limit: flags.limit ? parseInt(flags.limit) : undefined,
|
|
2313
|
+
offset: flags.offset ? parseInt(flags.offset) : undefined,
|
|
2314
|
+
});
|
|
2315
|
+
|
|
2316
|
+
if (result.payouts && result.payouts.length > 0) {
|
|
2317
|
+
console.log(`\n${colors.bright}Payouts${colors.reset} (${result.pagination?.total || result.payouts.length} total)\n`);
|
|
2318
|
+
for (const p of result.payouts) {
|
|
2319
|
+
const statusColor = p.status === 'paid' ? colors.green : p.status === 'failed' ? colors.red : colors.yellow;
|
|
2320
|
+
console.log(` ${p.id} ${statusColor}${p.status}${colors.reset} $${p.amount_usd || ((p.amount_cents || 0) / 100).toFixed(2)} ${new Date(p.created_at).toLocaleDateString()}`);
|
|
2321
|
+
}
|
|
2322
|
+
console.log();
|
|
2323
|
+
} else {
|
|
2324
|
+
print.info('No payouts found');
|
|
2325
|
+
}
|
|
2326
|
+
break;
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
case 'get': {
|
|
2330
|
+
const id = args[0];
|
|
2331
|
+
if (!id) {
|
|
2332
|
+
print.error('Usage: coinpay payout get <id>');
|
|
2333
|
+
process.exit(1);
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
const result = await client.getPayout(id);
|
|
2337
|
+
print.json(result);
|
|
2338
|
+
break;
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
default:
|
|
2342
|
+
print.error(`Unknown payout command: ${subcommand}`);
|
|
2343
|
+
print.info('Available: create, list, get');
|
|
2344
|
+
process.exit(1);
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2006
2348
|
/**
|
|
2007
2349
|
* Card (Stripe) commands
|
|
2008
2350
|
*/
|
|
@@ -2258,10 +2600,18 @@ async function main() {
|
|
|
2258
2600
|
await handleWebhook(subcommand, args, flags);
|
|
2259
2601
|
break;
|
|
2260
2602
|
|
|
2603
|
+
case 'payout':
|
|
2604
|
+
await handlePayout(subcommand, args, flags);
|
|
2605
|
+
break;
|
|
2606
|
+
|
|
2261
2607
|
case 'card':
|
|
2262
2608
|
await handleCard(subcommand, args, flags);
|
|
2263
2609
|
break;
|
|
2264
2610
|
|
|
2611
|
+
case 'auth':
|
|
2612
|
+
await handleAuth(subcommand, args, flags);
|
|
2613
|
+
break;
|
|
2614
|
+
|
|
2265
2615
|
case 'reputation':
|
|
2266
2616
|
await handleReputation(subcommand, args, flags);
|
|
2267
2617
|
break;
|
package/package.json
CHANGED
package/src/auth.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth SDK Module Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { CoinPayClient } from './client.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Merchant object returned from auth operations
|
|
9
|
+
*/
|
|
10
|
+
export interface Merchant {
|
|
11
|
+
id: string;
|
|
12
|
+
email: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
created_at: string;
|
|
15
|
+
updated_at?: string;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Auth response for register/login
|
|
21
|
+
*/
|
|
22
|
+
export interface AuthResponse {
|
|
23
|
+
success: boolean;
|
|
24
|
+
merchant: Merchant;
|
|
25
|
+
token: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Register params
|
|
30
|
+
*/
|
|
31
|
+
export interface RegisterParams {
|
|
32
|
+
email: string;
|
|
33
|
+
password: string;
|
|
34
|
+
name?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Login params
|
|
39
|
+
*/
|
|
40
|
+
export interface LoginParams {
|
|
41
|
+
email: string;
|
|
42
|
+
password: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Register a new merchant account
|
|
47
|
+
* @param client - CoinPay client (can be unauthenticated)
|
|
48
|
+
* @param params - Registration parameters
|
|
49
|
+
* @returns Promise with registration response
|
|
50
|
+
*/
|
|
51
|
+
export function registerMerchant(
|
|
52
|
+
client: CoinPayClient,
|
|
53
|
+
params: RegisterParams
|
|
54
|
+
): Promise<AuthResponse>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Login to merchant account
|
|
58
|
+
* @param client - CoinPay client (can be unauthenticated)
|
|
59
|
+
* @param params - Login parameters
|
|
60
|
+
* @returns Promise with login response
|
|
61
|
+
*/
|
|
62
|
+
export function loginMerchant(
|
|
63
|
+
client: CoinPayClient,
|
|
64
|
+
params: LoginParams
|
|
65
|
+
): Promise<AuthResponse>;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get current authenticated merchant info
|
|
69
|
+
* @param client - Authenticated CoinPay client
|
|
70
|
+
* @returns Promise with merchant information
|
|
71
|
+
*/
|
|
72
|
+
export function getMe(client: CoinPayClient): Promise<Merchant>;
|
package/src/auth.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth SDK Module
|
|
3
|
+
*
|
|
4
|
+
* Authentication functions for registration, login, and merchant management.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* import { CoinPayClient } from '@profullstack/coinpay';
|
|
8
|
+
* import { registerMerchant, loginMerchant, getMe } from '@profullstack/coinpay/auth';
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Register a new merchant account
|
|
13
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client (can be unauthenticated)
|
|
14
|
+
* @param {Object} params
|
|
15
|
+
* @param {string} params.email - Merchant email
|
|
16
|
+
* @param {string} params.password - Account password
|
|
17
|
+
* @param {string} [params.name] - Optional merchant name
|
|
18
|
+
* @returns {Promise<Object>} { success: boolean, merchant: Object, token: string }
|
|
19
|
+
*/
|
|
20
|
+
export async function registerMerchant(client, { email, password, name }) {
|
|
21
|
+
return client.requestUnauthenticated('/auth/register', {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
body: JSON.stringify({
|
|
24
|
+
email,
|
|
25
|
+
password,
|
|
26
|
+
name,
|
|
27
|
+
}),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Login to merchant account
|
|
33
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client (can be unauthenticated)
|
|
34
|
+
* @param {Object} params
|
|
35
|
+
* @param {string} params.email - Merchant email
|
|
36
|
+
* @param {string} params.password - Account password
|
|
37
|
+
* @returns {Promise<Object>} { success: boolean, merchant: Object, token: string }
|
|
38
|
+
*/
|
|
39
|
+
export async function loginMerchant(client, { email, password }) {
|
|
40
|
+
return client.requestUnauthenticated('/auth/login', {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
body: JSON.stringify({
|
|
43
|
+
email,
|
|
44
|
+
password,
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get current authenticated merchant info
|
|
51
|
+
* @param {import('./client.js').CoinPayClient} client - Authenticated CoinPay client
|
|
52
|
+
* @returns {Promise<Object>} Merchant information
|
|
53
|
+
*/
|
|
54
|
+
export async function getMe(client) {
|
|
55
|
+
return client.request('/auth/me', {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
});
|
|
58
|
+
}
|
package/src/card-payments.js
CHANGED
|
@@ -285,6 +285,128 @@ export function calculateCardPaymentFees(amountCents, tier = 'free') {
|
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Create a card escrow payment
|
|
290
|
+
*
|
|
291
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
292
|
+
* @param {string} businessId - Business ID
|
|
293
|
+
* @param {number} amountUSD - Amount in USD (will be converted to cents)
|
|
294
|
+
* @param {string} description - Payment description
|
|
295
|
+
* @param {Object} [metadata={}] - Custom metadata
|
|
296
|
+
* @returns {Promise<Object>} Escrow payment session with checkout URL
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* import { createCardEscrow } from '@profullstack/coinpay/card-payments';
|
|
300
|
+
*
|
|
301
|
+
* const escrow = await createCardEscrow(client, 'business-id', 100, 'Service payment', {
|
|
302
|
+
* orderId: '123',
|
|
303
|
+
* serviceType: 'web-design'
|
|
304
|
+
* });
|
|
305
|
+
*
|
|
306
|
+
* // Redirect customer to: escrow.checkout_url
|
|
307
|
+
*/
|
|
308
|
+
export async function createCardEscrow(client, businessId, amountUSD, description, metadata = {}) {
|
|
309
|
+
// Convert USD to cents
|
|
310
|
+
const amountCents = Math.round(amountUSD * 100);
|
|
311
|
+
|
|
312
|
+
return client.request('POST', '/api/stripe/payments/create', {
|
|
313
|
+
businessId,
|
|
314
|
+
amount: amountCents,
|
|
315
|
+
currency: 'usd',
|
|
316
|
+
description,
|
|
317
|
+
metadata,
|
|
318
|
+
escrowMode: true,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* List card escrows
|
|
324
|
+
*
|
|
325
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
326
|
+
* @param {Object} [options={}] - Filtering options
|
|
327
|
+
* @param {string} [options.businessId] - Filter by business ID
|
|
328
|
+
* @param {string} [options.status] - Filter by status
|
|
329
|
+
* @param {number} [options.limit] - Maximum number of results
|
|
330
|
+
* @param {number} [options.offset] - Results offset
|
|
331
|
+
* @returns {Promise<Object>} List of card escrows
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* const escrows = await listCardEscrows(client, {
|
|
335
|
+
* businessId: 'business-id',
|
|
336
|
+
* status: 'pending'
|
|
337
|
+
* });
|
|
338
|
+
*/
|
|
339
|
+
export async function listCardEscrows(client, options = {}) {
|
|
340
|
+
const queryParams = new URLSearchParams();
|
|
341
|
+
if (options.businessId) queryParams.set('businessId', options.businessId);
|
|
342
|
+
if (options.status) queryParams.set('status', options.status);
|
|
343
|
+
if (options.limit) queryParams.set('limit', options.limit.toString());
|
|
344
|
+
if (options.offset) queryParams.set('offset', options.offset.toString());
|
|
345
|
+
|
|
346
|
+
const queryString = queryParams.toString();
|
|
347
|
+
const url = `/api/stripe/escrows${queryString ? `?${queryString}` : ''}`;
|
|
348
|
+
|
|
349
|
+
return client.request('GET', url);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Release a card escrow
|
|
354
|
+
*
|
|
355
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
356
|
+
* @param {string} escrowId - Escrow ID to release
|
|
357
|
+
* @returns {Promise<Object>} Release result
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* const result = await releaseCardEscrow(client, 'escrow-123');
|
|
361
|
+
*/
|
|
362
|
+
export async function releaseCardEscrow(client, escrowId) {
|
|
363
|
+
return client.request('POST', '/api/stripe/escrow/release', {
|
|
364
|
+
escrowId,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Refund a card escrow
|
|
370
|
+
*
|
|
371
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
372
|
+
* @param {string} escrowId - Escrow ID to refund
|
|
373
|
+
* @param {Object} [options={}] - Refund options
|
|
374
|
+
* @param {number} [options.amount] - Partial refund amount in cents
|
|
375
|
+
* @param {string} [options.reason] - Refund reason
|
|
376
|
+
* @returns {Promise<Object>} Refund result
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* // Full refund
|
|
380
|
+
* const result = await refundCardEscrow(client, 'escrow-123');
|
|
381
|
+
*
|
|
382
|
+
* // Partial refund
|
|
383
|
+
* const result = await refundCardEscrow(client, 'escrow-123', {
|
|
384
|
+
* amount: 2500, // $25.00 in cents
|
|
385
|
+
* reason: 'partial_delivery'
|
|
386
|
+
* });
|
|
387
|
+
*/
|
|
388
|
+
export async function refundCardEscrow(client, escrowId, options = {}) {
|
|
389
|
+
return client.request('POST', '/api/stripe/escrow/refund', {
|
|
390
|
+
escrowId,
|
|
391
|
+
...options,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Get card escrow status and transaction details
|
|
397
|
+
*
|
|
398
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
399
|
+
* @param {string} escrowId - Escrow/transaction ID
|
|
400
|
+
* @returns {Promise<Object>} Transaction details with escrow status
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* const status = await getCardEscrowStatus(client, 'escrow-123');
|
|
404
|
+
* console.log(status.escrow_status); // 'pending', 'released', 'refunded'
|
|
405
|
+
*/
|
|
406
|
+
export async function getCardEscrowStatus(client, escrowId) {
|
|
407
|
+
return client.request('GET', `/api/stripe/transactions/${escrowId}`);
|
|
408
|
+
}
|
|
409
|
+
|
|
288
410
|
/**
|
|
289
411
|
* Default export with all convenience functions
|
|
290
412
|
*/
|
|
@@ -295,4 +417,9 @@ export default {
|
|
|
295
417
|
getPaymentMethodSupport,
|
|
296
418
|
formatCardAmount,
|
|
297
419
|
calculateCardPaymentFees,
|
|
420
|
+
createCardEscrow,
|
|
421
|
+
listCardEscrows,
|
|
422
|
+
releaseCardEscrow,
|
|
423
|
+
refundCardEscrow,
|
|
424
|
+
getCardEscrowStatus,
|
|
298
425
|
};
|
package/src/client.js
CHANGED
|
@@ -16,15 +16,11 @@ export class CoinPayClient {
|
|
|
16
16
|
/**
|
|
17
17
|
* Create a new CoinPay client
|
|
18
18
|
* @param {Object} options - Client options
|
|
19
|
-
* @param {string} options.apiKey - Your CoinPay API key
|
|
19
|
+
* @param {string} [options.apiKey] - Your CoinPay API key (optional for auth operations)
|
|
20
20
|
* @param {string} [options.baseUrl] - API base URL (default: https://coinpayportal.com/api)
|
|
21
21
|
* @param {number} [options.timeout] - Request timeout in ms (default: 30000)
|
|
22
22
|
*/
|
|
23
|
-
constructor({ apiKey, baseUrl = DEFAULT_BASE_URL, timeout = 30000 }) {
|
|
24
|
-
if (!apiKey) {
|
|
25
|
-
throw new Error('API key is required');
|
|
26
|
-
}
|
|
27
|
-
|
|
23
|
+
constructor({ apiKey, baseUrl = DEFAULT_BASE_URL, timeout = 30000 } = {}) {
|
|
28
24
|
this.#apiKey = apiKey;
|
|
29
25
|
this.#baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
|
|
30
26
|
this.#timeout = timeout;
|
|
@@ -37,6 +33,10 @@ export class CoinPayClient {
|
|
|
37
33
|
* @returns {Promise<Object>} API response
|
|
38
34
|
*/
|
|
39
35
|
async request(endpoint, options = {}) {
|
|
36
|
+
if (!this.#apiKey) {
|
|
37
|
+
throw new Error('API key is required for authenticated requests. Use requestUnauthenticated() for registration/login.');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
40
|
const url = `${this.#baseUrl}${endpoint}`;
|
|
41
41
|
const controller = new AbortController();
|
|
42
42
|
const timeoutId = setTimeout(() => controller.abort(), this.#timeout);
|
|
@@ -72,6 +72,47 @@ export class CoinPayClient {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Make an unauthenticated API request (for registration/login)
|
|
77
|
+
* @param {string} endpoint - API endpoint
|
|
78
|
+
* @param {Object} [options] - Fetch options
|
|
79
|
+
* @returns {Promise<Object>} API response
|
|
80
|
+
*/
|
|
81
|
+
async requestUnauthenticated(endpoint, options = {}) {
|
|
82
|
+
const url = `${this.#baseUrl}${endpoint}`;
|
|
83
|
+
const controller = new AbortController();
|
|
84
|
+
const timeoutId = setTimeout(() => controller.abort(), this.#timeout);
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const response = await fetch(url, {
|
|
88
|
+
...options,
|
|
89
|
+
signal: controller.signal,
|
|
90
|
+
headers: {
|
|
91
|
+
'Content-Type': 'application/json',
|
|
92
|
+
...options.headers,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const data = await response.json();
|
|
97
|
+
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
const error = new Error(data.error || `HTTP ${response.status}`);
|
|
100
|
+
error.status = response.status;
|
|
101
|
+
error.response = data;
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return data;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error.name === 'AbortError') {
|
|
108
|
+
throw new Error(`Request timeout after ${this.#timeout}ms`);
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
111
|
+
} finally {
|
|
112
|
+
clearTimeout(timeoutId);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
75
116
|
/**
|
|
76
117
|
* Create a new payment
|
|
77
118
|
*
|
|
@@ -523,6 +564,43 @@ export class CoinPayClient {
|
|
|
523
564
|
return authenticateEscrow(this, escrowId, token);
|
|
524
565
|
}
|
|
525
566
|
|
|
567
|
+
// ── Stripe Payout Methods ───────────────────────────────
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Create a payout to your connected Stripe account
|
|
571
|
+
*
|
|
572
|
+
* @param {Object} params - Payout parameters
|
|
573
|
+
* @param {number} params.amount - Amount in cents
|
|
574
|
+
* @param {string} [params.currency='usd'] - Currency code
|
|
575
|
+
* @param {string} [params.description] - Payout description
|
|
576
|
+
* @param {Object} [params.metadata] - Custom metadata
|
|
577
|
+
* @returns {Promise<Object>} Created payout
|
|
578
|
+
*/
|
|
579
|
+
async createPayout(params) {
|
|
580
|
+
const { createPayout } = await import('./payouts.js');
|
|
581
|
+
return createPayout(this, params);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* List payouts
|
|
586
|
+
* @param {Object} [filters] - Filter options
|
|
587
|
+
* @returns {Promise<Object>} Payouts list with pagination
|
|
588
|
+
*/
|
|
589
|
+
async listPayouts(filters) {
|
|
590
|
+
const { listPayouts } = await import('./payouts.js');
|
|
591
|
+
return listPayouts(this, filters);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Get payout by ID
|
|
596
|
+
* @param {string} payoutId - Payout ID
|
|
597
|
+
* @returns {Promise<Object>} Payout details
|
|
598
|
+
*/
|
|
599
|
+
async getPayout(payoutId) {
|
|
600
|
+
const { getPayout } = await import('./payouts.js');
|
|
601
|
+
return getPayout(this, payoutId);
|
|
602
|
+
}
|
|
603
|
+
|
|
526
604
|
// ── Stripe Card Payment Methods ────────────────────────
|
|
527
605
|
|
|
528
606
|
/**
|
|
@@ -685,6 +763,64 @@ export class CoinPayClient {
|
|
|
685
763
|
}),
|
|
686
764
|
});
|
|
687
765
|
}
|
|
766
|
+
// ── Escrow Series (Recurring) Methods ────────────────────
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Create a recurring escrow series
|
|
770
|
+
* @param {Object} params
|
|
771
|
+
* @returns {Promise<Object>} Created series
|
|
772
|
+
*/
|
|
773
|
+
async createEscrowSeries(params) {
|
|
774
|
+
return this.request('/escrow/series', {
|
|
775
|
+
method: 'POST',
|
|
776
|
+
body: JSON.stringify(params),
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* List escrow series for a business
|
|
782
|
+
* @param {string} businessId
|
|
783
|
+
* @param {string} [status]
|
|
784
|
+
* @returns {Promise<Object>}
|
|
785
|
+
*/
|
|
786
|
+
async listEscrowSeries(businessId, status) {
|
|
787
|
+
const params = new URLSearchParams({ business_id: businessId });
|
|
788
|
+
if (status) params.set('status', status);
|
|
789
|
+
return this.request(`/escrow/series?${params.toString()}`);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Get escrow series detail with child escrows
|
|
794
|
+
* @param {string} id
|
|
795
|
+
* @returns {Promise<Object>}
|
|
796
|
+
*/
|
|
797
|
+
async getEscrowSeries(id) {
|
|
798
|
+
return this.request(`/escrow/series/${id}`);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Update escrow series (pause/resume/cancel, update amount/interval)
|
|
803
|
+
* @param {string} id
|
|
804
|
+
* @param {Object} params - { status?, amount?, interval? }
|
|
805
|
+
* @returns {Promise<Object>}
|
|
806
|
+
*/
|
|
807
|
+
async updateEscrowSeries(id, params) {
|
|
808
|
+
return this.request(`/escrow/series/${id}`, {
|
|
809
|
+
method: 'PATCH',
|
|
810
|
+
body: JSON.stringify(params),
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Cancel an escrow series
|
|
816
|
+
* @param {string} id
|
|
817
|
+
* @returns {Promise<Object>}
|
|
818
|
+
*/
|
|
819
|
+
async cancelEscrowSeries(id) {
|
|
820
|
+
return this.request(`/escrow/series/${id}`, {
|
|
821
|
+
method: 'DELETE',
|
|
822
|
+
});
|
|
823
|
+
}
|
|
688
824
|
}
|
|
689
825
|
|
|
690
826
|
export default CoinPayClient;
|
package/src/index.js
CHANGED
|
@@ -88,8 +88,18 @@ import {
|
|
|
88
88
|
getBadgeUrl,
|
|
89
89
|
verifyCredential,
|
|
90
90
|
getRevocationList,
|
|
91
|
+
claimDid,
|
|
92
|
+
linkDid,
|
|
93
|
+
getMyDid,
|
|
91
94
|
} from './reputation.js';
|
|
92
95
|
|
|
96
|
+
// Auth exports
|
|
97
|
+
import {
|
|
98
|
+
registerMerchant,
|
|
99
|
+
loginMerchant,
|
|
100
|
+
getMe,
|
|
101
|
+
} from './auth.js';
|
|
102
|
+
|
|
93
103
|
import {
|
|
94
104
|
SwapClient,
|
|
95
105
|
SwapCoins,
|
|
@@ -101,6 +111,42 @@ import {
|
|
|
101
111
|
getSwapHistory,
|
|
102
112
|
} from './swap.js';
|
|
103
113
|
|
|
114
|
+
// Payouts
|
|
115
|
+
import {
|
|
116
|
+
createPayout,
|
|
117
|
+
listPayouts,
|
|
118
|
+
getPayout,
|
|
119
|
+
formatPayoutAmount,
|
|
120
|
+
} from './payouts.js';
|
|
121
|
+
|
|
122
|
+
// Card payments
|
|
123
|
+
import {
|
|
124
|
+
createQuickCardPayment,
|
|
125
|
+
waitForStripeOnboarding,
|
|
126
|
+
createCardPaymentWithOnboardingCheck,
|
|
127
|
+
getPaymentMethodSupport,
|
|
128
|
+
formatCardAmount,
|
|
129
|
+
calculateCardPaymentFees,
|
|
130
|
+
createCardEscrow,
|
|
131
|
+
listCardEscrows,
|
|
132
|
+
releaseCardEscrow,
|
|
133
|
+
refundCardEscrow,
|
|
134
|
+
getCardEscrowStatus,
|
|
135
|
+
} from './card-payments.js';
|
|
136
|
+
|
|
137
|
+
// Subscriptions
|
|
138
|
+
import {
|
|
139
|
+
createSubscriptionPlan,
|
|
140
|
+
listSubscriptionPlans,
|
|
141
|
+
subscribeCustomer,
|
|
142
|
+
cancelSubscription,
|
|
143
|
+
listSubscriptions,
|
|
144
|
+
getSubscription,
|
|
145
|
+
formatSubscriptionAmount,
|
|
146
|
+
PlanInterval,
|
|
147
|
+
SubscriptionStatus,
|
|
148
|
+
} from './subscriptions.js';
|
|
149
|
+
|
|
104
150
|
export {
|
|
105
151
|
// Client
|
|
106
152
|
CoinPayClient,
|
|
@@ -140,6 +186,36 @@ export {
|
|
|
140
186
|
getSwapStatus,
|
|
141
187
|
getSwapHistory,
|
|
142
188
|
|
|
189
|
+
// Payouts
|
|
190
|
+
createPayout,
|
|
191
|
+
listPayouts,
|
|
192
|
+
getPayout,
|
|
193
|
+
formatPayoutAmount,
|
|
194
|
+
|
|
195
|
+
// Card payments
|
|
196
|
+
createQuickCardPayment,
|
|
197
|
+
waitForStripeOnboarding,
|
|
198
|
+
createCardPaymentWithOnboardingCheck,
|
|
199
|
+
getPaymentMethodSupport,
|
|
200
|
+
formatCardAmount,
|
|
201
|
+
calculateCardPaymentFees,
|
|
202
|
+
createCardEscrow,
|
|
203
|
+
listCardEscrows,
|
|
204
|
+
releaseCardEscrow,
|
|
205
|
+
refundCardEscrow,
|
|
206
|
+
getCardEscrowStatus,
|
|
207
|
+
|
|
208
|
+
// Subscriptions
|
|
209
|
+
createSubscriptionPlan,
|
|
210
|
+
listSubscriptionPlans,
|
|
211
|
+
subscribeCustomer,
|
|
212
|
+
cancelSubscription,
|
|
213
|
+
listSubscriptions,
|
|
214
|
+
getSubscription,
|
|
215
|
+
formatSubscriptionAmount,
|
|
216
|
+
PlanInterval,
|
|
217
|
+
SubscriptionStatus,
|
|
218
|
+
|
|
143
219
|
// Constants
|
|
144
220
|
Blockchain,
|
|
145
221
|
Cryptocurrency, // Deprecated, use Blockchain
|
|
@@ -150,8 +226,19 @@ export {
|
|
|
150
226
|
submitReceipt,
|
|
151
227
|
getReputation,
|
|
152
228
|
getCredential,
|
|
229
|
+
getCredentials,
|
|
230
|
+
getReceipts,
|
|
231
|
+
getBadgeUrl,
|
|
153
232
|
verifyCredential,
|
|
154
233
|
getRevocationList,
|
|
234
|
+
claimDid,
|
|
235
|
+
linkDid,
|
|
236
|
+
getMyDid,
|
|
237
|
+
|
|
238
|
+
// Auth
|
|
239
|
+
registerMerchant,
|
|
240
|
+
loginMerchant,
|
|
241
|
+
getMe,
|
|
155
242
|
|
|
156
243
|
// Webhook utilities
|
|
157
244
|
verifyWebhookSignature,
|
package/src/payouts.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CoinPay Payouts - Functions for Stripe Connect payouts
|
|
3
|
+
*
|
|
4
|
+
* Manage payouts to connected Stripe accounts (merchants).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create a payout to a connected Stripe account
|
|
9
|
+
*
|
|
10
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
11
|
+
* @param {Object} params - Payout parameters
|
|
12
|
+
* @param {number} params.amount - Amount in cents (e.g., 5000 = $50.00)
|
|
13
|
+
* @param {string} [params.currency='usd'] - Currency code
|
|
14
|
+
* @param {string} [params.description] - Payout description
|
|
15
|
+
* @param {Object} [params.metadata] - Custom metadata
|
|
16
|
+
* @returns {Promise<Object>} Created payout
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* const payout = await createPayout(client, {
|
|
20
|
+
* amount: 5000,
|
|
21
|
+
* currency: 'usd',
|
|
22
|
+
* description: 'Weekly payout'
|
|
23
|
+
* });
|
|
24
|
+
*/
|
|
25
|
+
export async function createPayout(client, { amount, currency = 'usd', description, metadata }) {
|
|
26
|
+
return client.request('/stripe/payouts', {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
body: JSON.stringify({ amount, currency, description, metadata }),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* List payouts for the authenticated merchant
|
|
34
|
+
*
|
|
35
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
36
|
+
* @param {Object} [filters] - Filter options
|
|
37
|
+
* @param {string} [filters.status] - Filter by status (pending, paid, failed, canceled)
|
|
38
|
+
* @param {string} [filters.dateFrom] - Filter from date (ISO string)
|
|
39
|
+
* @param {string} [filters.dateTo] - Filter to date (ISO string)
|
|
40
|
+
* @param {number} [filters.limit=50] - Number of results
|
|
41
|
+
* @param {number} [filters.offset=0] - Pagination offset
|
|
42
|
+
* @returns {Promise<Object>} List of payouts with pagination
|
|
43
|
+
*/
|
|
44
|
+
export async function listPayouts(client, filters = {}) {
|
|
45
|
+
const params = new URLSearchParams();
|
|
46
|
+
if (filters.status) params.set('status', filters.status);
|
|
47
|
+
if (filters.dateFrom) params.set('date_from', filters.dateFrom);
|
|
48
|
+
if (filters.dateTo) params.set('date_to', filters.dateTo);
|
|
49
|
+
if (filters.limit) params.set('limit', String(filters.limit));
|
|
50
|
+
if (filters.offset) params.set('offset', String(filters.offset));
|
|
51
|
+
|
|
52
|
+
const qs = params.toString();
|
|
53
|
+
return client.request(`/stripe/payouts${qs ? '?' + qs : ''}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get a specific payout by ID
|
|
58
|
+
*
|
|
59
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
60
|
+
* @param {string} payoutId - Payout ID
|
|
61
|
+
* @returns {Promise<Object>} Payout details
|
|
62
|
+
*/
|
|
63
|
+
export async function getPayout(client, payoutId) {
|
|
64
|
+
return client.request(`/stripe/payouts/${payoutId}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Format payout amount from cents to display string
|
|
69
|
+
*
|
|
70
|
+
* @param {number} amountCents - Amount in cents
|
|
71
|
+
* @param {string} [currency='usd'] - Currency code
|
|
72
|
+
* @returns {string} Formatted amount (e.g., "$50.00")
|
|
73
|
+
*/
|
|
74
|
+
export function formatPayoutAmount(amountCents, currency = 'usd') {
|
|
75
|
+
const amount = (amountCents / 100).toFixed(2);
|
|
76
|
+
const symbols = { usd: '$', eur: '€', gbp: '£' };
|
|
77
|
+
const symbol = symbols[currency.toLowerCase()] || currency.toUpperCase() + ' ';
|
|
78
|
+
return `${symbol}${amount}`;
|
|
79
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CoinPay Subscriptions - Stripe subscription billing integration
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for managing subscription plans and customer subscriptions
|
|
5
|
+
* through Stripe's subscription billing system.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Subscription plan intervals
|
|
10
|
+
*/
|
|
11
|
+
export const PlanInterval = {
|
|
12
|
+
DAILY: 'day',
|
|
13
|
+
WEEKLY: 'week',
|
|
14
|
+
MONTHLY: 'month',
|
|
15
|
+
YEARLY: 'year',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Subscription statuses
|
|
20
|
+
*/
|
|
21
|
+
export const SubscriptionStatus = {
|
|
22
|
+
ACTIVE: 'active',
|
|
23
|
+
PAST_DUE: 'past_due',
|
|
24
|
+
CANCELED: 'canceled',
|
|
25
|
+
INCOMPLETE: 'incomplete',
|
|
26
|
+
INCOMPLETE_EXPIRED: 'incomplete_expired',
|
|
27
|
+
TRIALING: 'trialing',
|
|
28
|
+
UNPAID: 'unpaid',
|
|
29
|
+
PAUSED: 'paused',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create a subscription plan (Stripe Price + Product)
|
|
34
|
+
*
|
|
35
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
36
|
+
* @param {Object} params - Plan parameters
|
|
37
|
+
* @param {string} params.businessId - Business ID
|
|
38
|
+
* @param {string} params.name - Plan name
|
|
39
|
+
* @param {string} [params.description] - Plan description
|
|
40
|
+
* @param {number} params.amount - Amount in cents
|
|
41
|
+
* @param {string} [params.currency='usd'] - Currency code
|
|
42
|
+
* @param {string} [params.interval='month'] - Billing interval (day, week, month, year)
|
|
43
|
+
* @param {number} [params.intervalCount=1] - Number of intervals between billings
|
|
44
|
+
* @param {number} [params.trialDays] - Trial period in days
|
|
45
|
+
* @param {Object} [params.metadata] - Custom metadata
|
|
46
|
+
* @returns {Promise<Object>} Created plan
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* const plan = await createSubscriptionPlan(client, {
|
|
50
|
+
* businessId: 'biz-123',
|
|
51
|
+
* name: 'Pro Monthly',
|
|
52
|
+
* amount: 2999,
|
|
53
|
+
* interval: 'month',
|
|
54
|
+
* });
|
|
55
|
+
*/
|
|
56
|
+
export async function createSubscriptionPlan(client, params) {
|
|
57
|
+
const {
|
|
58
|
+
businessId,
|
|
59
|
+
name,
|
|
60
|
+
description,
|
|
61
|
+
amount,
|
|
62
|
+
currency = 'usd',
|
|
63
|
+
interval = 'month',
|
|
64
|
+
intervalCount = 1,
|
|
65
|
+
trialDays,
|
|
66
|
+
metadata = {},
|
|
67
|
+
} = params;
|
|
68
|
+
|
|
69
|
+
if (!businessId || !name || !amount) {
|
|
70
|
+
throw new Error('businessId, name, and amount are required');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return client.request('/stripe/subscriptions/plans', {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
body: JSON.stringify({
|
|
76
|
+
businessId,
|
|
77
|
+
name,
|
|
78
|
+
description,
|
|
79
|
+
amount,
|
|
80
|
+
currency,
|
|
81
|
+
interval,
|
|
82
|
+
intervalCount,
|
|
83
|
+
trialDays,
|
|
84
|
+
metadata,
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* List subscription plans for a business
|
|
91
|
+
*
|
|
92
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
93
|
+
* @param {string} businessId - Business ID
|
|
94
|
+
* @param {Object} [options] - Query options
|
|
95
|
+
* @param {number} [options.limit=20] - Max results
|
|
96
|
+
* @param {boolean} [options.active] - Filter by active status
|
|
97
|
+
* @returns {Promise<Object>} List of plans
|
|
98
|
+
*/
|
|
99
|
+
export async function listSubscriptionPlans(client, businessId, options = {}) {
|
|
100
|
+
const params = new URLSearchParams({ businessId });
|
|
101
|
+
if (options.limit) params.set('limit', String(options.limit));
|
|
102
|
+
if (options.active !== undefined) params.set('active', String(options.active));
|
|
103
|
+
|
|
104
|
+
return client.request(`/stripe/subscriptions/plans?${params}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Subscribe a customer to a plan
|
|
109
|
+
*
|
|
110
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
111
|
+
* @param {Object} params - Subscription parameters
|
|
112
|
+
* @param {string} params.planId - Plan/Price ID
|
|
113
|
+
* @param {string} params.customerEmail - Customer email
|
|
114
|
+
* @param {string} [params.customerId] - Existing Stripe customer ID
|
|
115
|
+
* @param {string} [params.paymentMethodId] - Payment method ID
|
|
116
|
+
* @param {string} [params.successUrl] - Success redirect URL
|
|
117
|
+
* @param {string} [params.cancelUrl] - Cancel redirect URL
|
|
118
|
+
* @param {Object} [params.metadata] - Custom metadata
|
|
119
|
+
* @returns {Promise<Object>} Subscription or checkout session
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* const sub = await subscribeCustomer(client, {
|
|
123
|
+
* planId: 'price_123',
|
|
124
|
+
* customerEmail: 'customer@example.com',
|
|
125
|
+
* successUrl: 'https://mysite.com/success',
|
|
126
|
+
* cancelUrl: 'https://mysite.com/cancel',
|
|
127
|
+
* });
|
|
128
|
+
* // Redirect to sub.checkout_url
|
|
129
|
+
*/
|
|
130
|
+
export async function subscribeCustomer(client, params) {
|
|
131
|
+
const {
|
|
132
|
+
planId,
|
|
133
|
+
customerEmail,
|
|
134
|
+
customerId,
|
|
135
|
+
paymentMethodId,
|
|
136
|
+
successUrl,
|
|
137
|
+
cancelUrl,
|
|
138
|
+
metadata = {},
|
|
139
|
+
} = params;
|
|
140
|
+
|
|
141
|
+
if (!planId || (!customerEmail && !customerId)) {
|
|
142
|
+
throw new Error('planId and either customerEmail or customerId are required');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return client.request('/stripe/subscriptions', {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
body: JSON.stringify({
|
|
148
|
+
planId,
|
|
149
|
+
customerEmail,
|
|
150
|
+
customerId,
|
|
151
|
+
paymentMethodId,
|
|
152
|
+
successUrl,
|
|
153
|
+
cancelUrl,
|
|
154
|
+
metadata,
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Cancel a subscription
|
|
161
|
+
*
|
|
162
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
163
|
+
* @param {string} subscriptionId - Subscription ID
|
|
164
|
+
* @param {Object} [options] - Cancellation options
|
|
165
|
+
* @param {boolean} [options.immediately=false] - Cancel immediately vs at period end
|
|
166
|
+
* @returns {Promise<Object>} Cancelled subscription
|
|
167
|
+
*/
|
|
168
|
+
export async function cancelSubscription(client, subscriptionId, options = {}) {
|
|
169
|
+
if (!subscriptionId) {
|
|
170
|
+
throw new Error('subscriptionId is required');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return client.request(`/stripe/subscriptions/${encodeURIComponent(subscriptionId)}`, {
|
|
174
|
+
method: 'DELETE',
|
|
175
|
+
body: JSON.stringify({
|
|
176
|
+
immediately: options.immediately || false,
|
|
177
|
+
}),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* List subscriptions
|
|
183
|
+
*
|
|
184
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
185
|
+
* @param {Object} [options] - Query options
|
|
186
|
+
* @param {string} [options.businessId] - Filter by business
|
|
187
|
+
* @param {string} [options.customerId] - Filter by customer
|
|
188
|
+
* @param {string} [options.status] - Filter by status
|
|
189
|
+
* @param {number} [options.limit=20] - Max results
|
|
190
|
+
* @param {number} [options.offset=0] - Pagination offset
|
|
191
|
+
* @returns {Promise<Object>} List of subscriptions
|
|
192
|
+
*/
|
|
193
|
+
export async function listSubscriptions(client, options = {}) {
|
|
194
|
+
const params = new URLSearchParams();
|
|
195
|
+
if (options.businessId) params.set('businessId', options.businessId);
|
|
196
|
+
if (options.customerId) params.set('customerId', options.customerId);
|
|
197
|
+
if (options.status) params.set('status', options.status);
|
|
198
|
+
if (options.limit) params.set('limit', String(options.limit));
|
|
199
|
+
if (options.offset) params.set('offset', String(options.offset));
|
|
200
|
+
|
|
201
|
+
return client.request(`/stripe/subscriptions?${params}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get subscription status/details
|
|
206
|
+
*
|
|
207
|
+
* @param {import('./client.js').CoinPayClient} client - CoinPay client instance
|
|
208
|
+
* @param {string} subscriptionId - Subscription ID
|
|
209
|
+
* @returns {Promise<Object>} Subscription details
|
|
210
|
+
*/
|
|
211
|
+
export async function getSubscription(client, subscriptionId) {
|
|
212
|
+
if (!subscriptionId) {
|
|
213
|
+
throw new Error('subscriptionId is required');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return client.request(`/stripe/subscriptions/${encodeURIComponent(subscriptionId)}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Format subscription amount for display
|
|
221
|
+
*
|
|
222
|
+
* @param {number} amount - Amount in cents
|
|
223
|
+
* @param {string} [currency='usd'] - Currency code
|
|
224
|
+
* @param {string} [interval='month'] - Billing interval
|
|
225
|
+
* @returns {string} Formatted amount (e.g., "$29.99/month")
|
|
226
|
+
*/
|
|
227
|
+
export function formatSubscriptionAmount(amount, currency = 'usd', interval = 'month') {
|
|
228
|
+
const dollars = (amount / 100).toFixed(2);
|
|
229
|
+
const symbol = currency.toLowerCase() === 'usd' ? '$' : currency.toUpperCase() + ' ';
|
|
230
|
+
return `${symbol}${dollars}/${interval}`;
|
|
231
|
+
}
|