@profullstack/coinpay 0.1.0 → 0.2.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/README.md +394 -119
- package/bin/coinpay.js +38 -14
- package/package.json +1 -1
- package/src/client.js +170 -23
- package/src/index.js +33 -1
- package/src/payments.js +77 -17
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# @profullstack/coinpay
|
|
2
2
|
|
|
3
|
-
CoinPay SDK & CLI -
|
|
3
|
+
CoinPay SDK & CLI - Accept cryptocurrency payments in your application.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
CoinPay allows merchants to accept cryptocurrency payments from their customers. When a customer wants to pay:
|
|
8
|
+
|
|
9
|
+
1. **Your server** calls the CoinPay API to create a payment request
|
|
10
|
+
2. **CoinPay** generates a unique payment address and QR code
|
|
11
|
+
3. **Your customer** sends cryptocurrency to that address
|
|
12
|
+
4. **CoinPay** monitors the blockchain and notifies you via webhook when payment is confirmed
|
|
13
|
+
5. **Funds** are automatically forwarded to your configured wallet (minus a small fee)
|
|
4
14
|
|
|
5
15
|
## Installation
|
|
6
16
|
|
|
@@ -17,48 +27,88 @@ pnpm add -g @profullstack/coinpay
|
|
|
17
27
|
|
|
18
28
|
## Quick Start
|
|
19
29
|
|
|
20
|
-
###
|
|
30
|
+
### 1. Get Your API Key
|
|
31
|
+
|
|
32
|
+
1. Sign up at [coinpayportal.com](https://coinpayportal.com)
|
|
33
|
+
2. Create a business in your dashboard
|
|
34
|
+
3. Configure your wallet addresses for each cryptocurrency you want to accept
|
|
35
|
+
4. Copy your API key (starts with `cp_live_`)
|
|
36
|
+
|
|
37
|
+
### 2. Create a Payment (SDK)
|
|
21
38
|
|
|
22
39
|
```javascript
|
|
23
40
|
import { CoinPayClient } from '@profullstack/coinpay';
|
|
24
41
|
|
|
25
|
-
// Initialize
|
|
42
|
+
// Initialize with your API key
|
|
26
43
|
const coinpay = new CoinPayClient({
|
|
27
|
-
apiKey: '
|
|
44
|
+
apiKey: 'cp_live_your_api_key_here',
|
|
28
45
|
});
|
|
29
46
|
|
|
30
|
-
// Create a payment
|
|
47
|
+
// Create a payment when customer checks out
|
|
31
48
|
const payment = await coinpay.createPayment({
|
|
32
|
-
businessId: '
|
|
33
|
-
amount:
|
|
34
|
-
currency: 'USD',
|
|
35
|
-
|
|
36
|
-
description: 'Order #12345',
|
|
49
|
+
businessId: 'your-business-id', // From your dashboard
|
|
50
|
+
amount: 99.99, // Amount in fiat currency
|
|
51
|
+
currency: 'USD', // Fiat currency (default: USD)
|
|
52
|
+
blockchain: 'BTC', // Cryptocurrency to accept
|
|
53
|
+
description: 'Order #12345', // Shown to customer
|
|
54
|
+
metadata: { // Your custom data
|
|
55
|
+
orderId: '12345',
|
|
56
|
+
customerEmail: 'customer@example.com'
|
|
57
|
+
}
|
|
37
58
|
});
|
|
38
59
|
|
|
39
|
-
|
|
40
|
-
console.log(
|
|
60
|
+
// Display to customer
|
|
61
|
+
console.log('Send payment to:', payment.payment.payment_address);
|
|
62
|
+
console.log('Amount:', payment.payment.crypto_amount, payment.payment.blockchain);
|
|
63
|
+
console.log('QR Code:', payment.payment.qr_code);
|
|
41
64
|
```
|
|
42
65
|
|
|
43
|
-
###
|
|
66
|
+
### 3. Create a Payment (cURL)
|
|
44
67
|
|
|
45
68
|
```bash
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
curl -X POST https://coinpayportal.com/api/payments/create \
|
|
70
|
+
-H "Authorization: Bearer cp_live_your_api_key_here" \
|
|
71
|
+
-H "Content-Type: application/json" \
|
|
72
|
+
-d '{
|
|
73
|
+
"business_id": "your-business-id",
|
|
74
|
+
"amount": 99.99,
|
|
75
|
+
"currency": "USD",
|
|
76
|
+
"blockchain": "BTC",
|
|
77
|
+
"description": "Order #12345",
|
|
78
|
+
"metadata": {
|
|
79
|
+
"orderId": "12345",
|
|
80
|
+
"customerEmail": "customer@example.com"
|
|
81
|
+
}
|
|
82
|
+
}'
|
|
83
|
+
```
|
|
48
84
|
|
|
49
|
-
|
|
50
|
-
coinpay payment create --business-id biz_123 --amount 100 --currency USD --crypto BTC
|
|
85
|
+
### 4. Create a Payment (CLI)
|
|
51
86
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# List payments
|
|
56
|
-
coinpay payment list --business-id biz_123
|
|
87
|
+
```bash
|
|
88
|
+
# Configure your API key (one-time setup)
|
|
89
|
+
coinpay config set-key cp_live_your_api_key_here
|
|
57
90
|
|
|
58
|
-
#
|
|
59
|
-
coinpay
|
|
91
|
+
# Create a payment
|
|
92
|
+
coinpay payment create \
|
|
93
|
+
--business-id your-business-id \
|
|
94
|
+
--amount 99.99 \
|
|
95
|
+
--blockchain BTC \
|
|
96
|
+
--description "Order #12345"
|
|
60
97
|
```
|
|
61
98
|
|
|
99
|
+
## Supported Blockchains
|
|
100
|
+
|
|
101
|
+
| Blockchain | Code | Description |
|
|
102
|
+
|------------|------|-------------|
|
|
103
|
+
| Bitcoin | `BTC` | Native Bitcoin |
|
|
104
|
+
| Bitcoin Cash | `BCH` | Bitcoin Cash |
|
|
105
|
+
| Ethereum | `ETH` | Native Ether |
|
|
106
|
+
| Polygon | `MATIC` | Native MATIC |
|
|
107
|
+
| Solana | `SOL` | Native SOL |
|
|
108
|
+
| USDC (Ethereum) | `USDC_ETH` | USDC on Ethereum |
|
|
109
|
+
| USDC (Polygon) | `USDC_MATIC` | USDC on Polygon |
|
|
110
|
+
| USDC (Solana) | `USDC_SOL` | USDC on Solana |
|
|
111
|
+
|
|
62
112
|
## SDK API Reference
|
|
63
113
|
|
|
64
114
|
### CoinPayClient
|
|
@@ -67,39 +117,136 @@ coinpay rates get BTC
|
|
|
67
117
|
import { CoinPayClient } from '@profullstack/coinpay';
|
|
68
118
|
|
|
69
119
|
const client = new CoinPayClient({
|
|
70
|
-
apiKey: '
|
|
71
|
-
baseUrl: 'https://
|
|
72
|
-
timeout: 30000,
|
|
120
|
+
apiKey: 'cp_live_xxxxx', // Required: Your API key
|
|
121
|
+
baseUrl: 'https://coinpayportal.com/api', // Optional: API URL
|
|
122
|
+
timeout: 30000, // Optional: Request timeout (ms)
|
|
73
123
|
});
|
|
74
124
|
```
|
|
75
125
|
|
|
76
|
-
### Payments
|
|
126
|
+
### Creating Payments
|
|
77
127
|
|
|
78
128
|
```javascript
|
|
79
129
|
// Create a payment
|
|
80
130
|
const payment = await client.createPayment({
|
|
81
|
-
businessId: 'biz_123',
|
|
82
|
-
amount: 100,
|
|
83
|
-
currency: 'USD',
|
|
84
|
-
|
|
85
|
-
description: 'Order #
|
|
86
|
-
metadata:
|
|
87
|
-
|
|
131
|
+
businessId: 'biz_123', // Required: Your business ID
|
|
132
|
+
amount: 100, // Required: Amount in fiat
|
|
133
|
+
currency: 'USD', // Optional: Fiat currency (default: USD)
|
|
134
|
+
blockchain: 'ETH', // Required: Blockchain to use
|
|
135
|
+
description: 'Order #123', // Optional: Description for customer
|
|
136
|
+
metadata: { ... }, // Optional: Your custom data
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Response structure
|
|
140
|
+
{
|
|
141
|
+
success: true,
|
|
142
|
+
payment: {
|
|
143
|
+
id: 'pay_abc123',
|
|
144
|
+
business_id: 'biz_123',
|
|
145
|
+
amount: 100,
|
|
146
|
+
currency: 'USD',
|
|
147
|
+
blockchain: 'ETH',
|
|
148
|
+
crypto_amount: '0.0456',
|
|
149
|
+
payment_address: '0x1234...5678',
|
|
150
|
+
qr_code: 'data:image/png;base64,...',
|
|
151
|
+
status: 'pending',
|
|
152
|
+
expires_at: '2024-01-01T01:00:00.000Z',
|
|
153
|
+
created_at: '2024-01-01T00:00:00.000Z'
|
|
154
|
+
},
|
|
155
|
+
usage: {
|
|
156
|
+
current: 45,
|
|
157
|
+
limit: 100,
|
|
158
|
+
remaining: 55
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Checking Payment Status
|
|
164
|
+
|
|
165
|
+
There are two ways to know when a payment is complete:
|
|
166
|
+
|
|
167
|
+
#### Option 1: Polling (Simple)
|
|
168
|
+
|
|
169
|
+
Use `getPayment()` to check status, or `waitForPayment()` to poll until complete:
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
// Check status once
|
|
173
|
+
const result = await client.getPayment('pay_abc123');
|
|
174
|
+
console.log(result.payment.status);
|
|
175
|
+
|
|
176
|
+
// Or wait for payment to complete (polls automatically)
|
|
177
|
+
const payment = await client.waitForPayment('pay_abc123', {
|
|
178
|
+
interval: 5000, // Check every 5 seconds
|
|
179
|
+
timeout: 600000, // Give up after 10 minutes
|
|
180
|
+
onStatusChange: (status, payment) => {
|
|
181
|
+
console.log(`Status changed to: ${status}`);
|
|
182
|
+
}
|
|
88
183
|
});
|
|
89
184
|
|
|
90
|
-
|
|
91
|
-
|
|
185
|
+
if (payment.payment.status === 'confirmed' || payment.payment.status === 'forwarded') {
|
|
186
|
+
console.log('Payment successful!');
|
|
187
|
+
} else {
|
|
188
|
+
console.log('Payment failed or expired');
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Option 2: Webhooks (Recommended for Production)
|
|
193
|
+
|
|
194
|
+
Configure a webhook URL in your business settings to receive real-time notifications:
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
// Your webhook endpoint receives POST requests like:
|
|
198
|
+
{
|
|
199
|
+
"event": "payment.confirmed",
|
|
200
|
+
"data": {
|
|
201
|
+
"payment": {
|
|
202
|
+
"id": "pay_abc123",
|
|
203
|
+
"status": "confirmed",
|
|
204
|
+
"metadata": { "orderId": "12345" }
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
See [Webhook Integration](#webhook-integration) for full details.
|
|
211
|
+
|
|
212
|
+
**Payment Statuses:**
|
|
213
|
+
- `pending` - Waiting for payment
|
|
214
|
+
- `detected` - Payment detected, waiting for confirmations
|
|
215
|
+
- `confirmed` - Payment confirmed on blockchain
|
|
216
|
+
- `forwarding` - Forwarding to your wallet
|
|
217
|
+
- `forwarded` - Successfully sent to your wallet
|
|
218
|
+
- `expired` - Payment request expired
|
|
219
|
+
- `failed` - Payment failed
|
|
220
|
+
|
|
221
|
+
### Getting QR Code
|
|
222
|
+
|
|
223
|
+
The QR code endpoint returns binary PNG image data.
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
// Get QR code URL for use in HTML <img> tags
|
|
227
|
+
const qrUrl = client.getPaymentQRUrl('pay_abc123');
|
|
228
|
+
// Returns: "https://coinpayportal.com/api/payments/pay_abc123/qr"
|
|
229
|
+
|
|
230
|
+
// Use directly in HTML
|
|
231
|
+
// <img src={qrUrl} alt="Payment QR Code" />
|
|
232
|
+
|
|
233
|
+
// Get QR code as binary data (for server-side processing)
|
|
234
|
+
const imageData = await client.getPaymentQR('pay_abc123');
|
|
92
235
|
|
|
93
|
-
//
|
|
236
|
+
// Save to file (Node.js)
|
|
237
|
+
import fs from 'fs';
|
|
238
|
+
fs.writeFileSync('payment-qr.png', Buffer.from(imageData));
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Listing Payments
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
94
244
|
const payments = await client.listPayments({
|
|
95
245
|
businessId: 'biz_123',
|
|
96
|
-
status: 'completed',
|
|
97
|
-
limit: 20,
|
|
98
|
-
offset: 0,
|
|
246
|
+
status: 'completed', // Optional filter
|
|
247
|
+
limit: 20, // Optional (default: 20)
|
|
248
|
+
offset: 0, // Optional pagination
|
|
99
249
|
});
|
|
100
|
-
|
|
101
|
-
// Get payment QR code
|
|
102
|
-
const qr = await client.getPaymentQR('pay_abc123', 'png');
|
|
103
250
|
```
|
|
104
251
|
|
|
105
252
|
### Exchange Rates
|
|
@@ -107,47 +254,100 @@ const qr = await client.getPaymentQR('pay_abc123', 'png');
|
|
|
107
254
|
```javascript
|
|
108
255
|
// Get single rate
|
|
109
256
|
const rate = await client.getExchangeRate('BTC', 'USD');
|
|
257
|
+
// { from: 'BTC', to: 'USD', rate: 43250.00 }
|
|
110
258
|
|
|
111
259
|
// Get multiple rates
|
|
112
260
|
const rates = await client.getExchangeRates(['BTC', 'ETH', 'SOL'], 'USD');
|
|
113
261
|
```
|
|
114
262
|
|
|
115
|
-
|
|
263
|
+
## Direct API Usage (fetch/curl)
|
|
264
|
+
|
|
265
|
+
### Create Payment
|
|
116
266
|
|
|
117
267
|
```javascript
|
|
118
|
-
//
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
ETH: '0x...',
|
|
268
|
+
// Using fetch
|
|
269
|
+
const response = await fetch('https://coinpayportal.com/api/payments/create', {
|
|
270
|
+
method: 'POST',
|
|
271
|
+
headers: {
|
|
272
|
+
'Authorization': 'Bearer cp_live_your_api_key',
|
|
273
|
+
'Content-Type': 'application/json',
|
|
125
274
|
},
|
|
275
|
+
body: JSON.stringify({
|
|
276
|
+
business_id: 'your-business-id',
|
|
277
|
+
amount: 50.00,
|
|
278
|
+
currency: 'USD',
|
|
279
|
+
blockchain: 'ETH',
|
|
280
|
+
description: 'Premium subscription',
|
|
281
|
+
metadata: {
|
|
282
|
+
userId: 'user_123',
|
|
283
|
+
plan: 'premium'
|
|
284
|
+
}
|
|
285
|
+
}),
|
|
126
286
|
});
|
|
127
287
|
|
|
128
|
-
|
|
129
|
-
|
|
288
|
+
const data = await response.json();
|
|
289
|
+
console.log(data.payment.payment_address);
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# Using cURL
|
|
294
|
+
curl -X POST https://coinpayportal.com/api/payments/create \
|
|
295
|
+
-H "Authorization: Bearer cp_live_your_api_key" \
|
|
296
|
+
-H "Content-Type: application/json" \
|
|
297
|
+
-d '{
|
|
298
|
+
"business_id": "your-business-id",
|
|
299
|
+
"amount": 50.00,
|
|
300
|
+
"currency": "USD",
|
|
301
|
+
"blockchain": "ETH",
|
|
302
|
+
"description": "Premium subscription"
|
|
303
|
+
}'
|
|
304
|
+
```
|
|
130
305
|
|
|
131
|
-
|
|
132
|
-
const businesses = await client.listBusinesses();
|
|
306
|
+
### Get Payment Status
|
|
133
307
|
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
308
|
+
```javascript
|
|
309
|
+
const response = await fetch('https://coinpayportal.com/api/payments/pay_abc123', {
|
|
310
|
+
headers: {
|
|
311
|
+
'Authorization': 'Bearer cp_live_your_api_key',
|
|
312
|
+
},
|
|
137
313
|
});
|
|
314
|
+
const data = await response.json();
|
|
138
315
|
```
|
|
139
316
|
|
|
140
|
-
|
|
317
|
+
```bash
|
|
318
|
+
curl https://coinpayportal.com/api/payments/pay_abc123 \
|
|
319
|
+
-H "Authorization: Bearer cp_live_your_api_key"
|
|
320
|
+
```
|
|
141
321
|
|
|
142
|
-
|
|
143
|
-
// Get webhook logs
|
|
144
|
-
const logs = await client.getWebhookLogs('biz_123', 50);
|
|
322
|
+
## Webhook Integration
|
|
145
323
|
|
|
146
|
-
|
|
147
|
-
|
|
324
|
+
When payment status changes, CoinPay sends a POST request to your configured webhook URL:
|
|
325
|
+
|
|
326
|
+
```javascript
|
|
327
|
+
// Webhook payload
|
|
328
|
+
{
|
|
329
|
+
"event": "payment.confirmed",
|
|
330
|
+
"timestamp": "2024-01-01T00:15:00.000Z",
|
|
331
|
+
"data": {
|
|
332
|
+
"payment": {
|
|
333
|
+
"id": "pay_abc123",
|
|
334
|
+
"business_id": "biz_123",
|
|
335
|
+
"amount": 100.00,
|
|
336
|
+
"currency": "USD",
|
|
337
|
+
"crypto_amount": "0.0456",
|
|
338
|
+
"blockchain": "ETH",
|
|
339
|
+
"status": "confirmed",
|
|
340
|
+
"tx_hash": "0xabc...def",
|
|
341
|
+
"metadata": {
|
|
342
|
+
"orderId": "12345"
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
"signature": "sha256_hmac_signature"
|
|
347
|
+
}
|
|
148
348
|
```
|
|
149
349
|
|
|
150
|
-
|
|
350
|
+
### Verifying Webhooks
|
|
151
351
|
|
|
152
352
|
```javascript
|
|
153
353
|
import { verifyWebhookSignature, createWebhookHandler } from '@profullstack/coinpay';
|
|
@@ -163,101 +363,98 @@ const isValid = verifyWebhookSignature({
|
|
|
163
363
|
app.post('/webhook', createWebhookHandler({
|
|
164
364
|
secret: 'your-webhook-secret',
|
|
165
365
|
onEvent: async (event) => {
|
|
166
|
-
console.log('Received event:', event.type);
|
|
167
|
-
|
|
168
366
|
switch (event.type) {
|
|
169
|
-
case 'payment.
|
|
170
|
-
//
|
|
367
|
+
case 'payment.confirmed':
|
|
368
|
+
// Mark order as paid
|
|
369
|
+
await markOrderPaid(event.data.payment.metadata.orderId);
|
|
370
|
+
break;
|
|
371
|
+
case 'payment.forwarded':
|
|
372
|
+
// Funds received in your wallet
|
|
171
373
|
break;
|
|
172
374
|
case 'payment.expired':
|
|
173
375
|
// Handle expired payment
|
|
174
376
|
break;
|
|
175
377
|
}
|
|
176
378
|
},
|
|
177
|
-
onError: (error) => {
|
|
178
|
-
console.error('Webhook error:', error);
|
|
179
|
-
},
|
|
180
379
|
}));
|
|
181
380
|
```
|
|
182
381
|
|
|
183
|
-
|
|
382
|
+
### Webhook Events
|
|
184
383
|
|
|
185
384
|
| Event | Description |
|
|
186
385
|
|-------|-------------|
|
|
187
|
-
| `payment.created` | Payment
|
|
188
|
-
| `payment.
|
|
189
|
-
| `payment.
|
|
190
|
-
| `payment.
|
|
191
|
-
| `payment.
|
|
386
|
+
| `payment.created` | Payment request created |
|
|
387
|
+
| `payment.detected` | Payment detected on blockchain |
|
|
388
|
+
| `payment.confirmed` | Payment confirmed (safe to fulfill order) |
|
|
389
|
+
| `payment.forwarding` | Forwarding funds to your wallet |
|
|
390
|
+
| `payment.forwarded` | Funds sent to your wallet |
|
|
391
|
+
| `payment.expired` | Payment request expired |
|
|
192
392
|
| `payment.failed` | Payment failed |
|
|
193
|
-
| `payment.refunded` | Payment was refunded |
|
|
194
393
|
|
|
195
|
-
## CLI
|
|
394
|
+
## CLI Reference
|
|
196
395
|
|
|
197
396
|
### Configuration
|
|
198
397
|
|
|
199
398
|
```bash
|
|
200
|
-
|
|
201
|
-
coinpay config set-
|
|
202
|
-
|
|
399
|
+
# Set your API key
|
|
400
|
+
coinpay config set-key cp_live_xxxxx
|
|
401
|
+
|
|
402
|
+
# Set custom API URL (for development)
|
|
403
|
+
coinpay config set-url http://localhost:3000/api
|
|
404
|
+
|
|
405
|
+
# Show current configuration
|
|
406
|
+
coinpay config show
|
|
203
407
|
```
|
|
204
408
|
|
|
205
409
|
### Payments
|
|
206
410
|
|
|
207
411
|
```bash
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
--
|
|
211
|
-
--
|
|
212
|
-
--
|
|
213
|
-
--description
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
coinpay payment
|
|
217
|
-
|
|
412
|
+
# Create a payment
|
|
413
|
+
coinpay payment create \
|
|
414
|
+
--business-id biz_123 \
|
|
415
|
+
--amount 100 \
|
|
416
|
+
--blockchain BTC \
|
|
417
|
+
--description "Order #12345"
|
|
418
|
+
|
|
419
|
+
# Get payment details
|
|
420
|
+
coinpay payment get pay_abc123
|
|
421
|
+
|
|
422
|
+
# List payments
|
|
423
|
+
coinpay payment list --business-id biz_123 --status pending --limit 10
|
|
424
|
+
|
|
425
|
+
# Get QR code (saves as PNG file)
|
|
426
|
+
coinpay payment qr pay_abc123 --output payment-qr.png
|
|
218
427
|
```
|
|
219
428
|
|
|
220
429
|
### Businesses
|
|
221
430
|
|
|
222
431
|
```bash
|
|
223
|
-
|
|
224
|
-
coinpay business get <business-id>
|
|
432
|
+
# List your businesses
|
|
225
433
|
coinpay business list
|
|
226
|
-
coinpay business update <business-id> [--name <name>] [--webhook-url <url>]
|
|
227
|
-
```
|
|
228
434
|
|
|
229
|
-
|
|
435
|
+
# Get business details
|
|
436
|
+
coinpay business get biz_123
|
|
230
437
|
|
|
231
|
-
|
|
232
|
-
coinpay
|
|
233
|
-
coinpay rates list [--fiat <currency>]
|
|
438
|
+
# Create a business
|
|
439
|
+
coinpay business create --name "My Store" --webhook-url https://mysite.com/webhook
|
|
234
440
|
```
|
|
235
441
|
|
|
236
|
-
###
|
|
442
|
+
### Exchange Rates
|
|
237
443
|
|
|
238
444
|
```bash
|
|
239
|
-
|
|
240
|
-
coinpay
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## Supported Cryptocurrencies
|
|
244
|
-
|
|
245
|
-
- **BTC** - Bitcoin
|
|
246
|
-
- **BCH** - Bitcoin Cash
|
|
247
|
-
- **ETH** - Ethereum
|
|
248
|
-
- **MATIC** - Polygon
|
|
249
|
-
- **SOL** - Solana
|
|
250
|
-
|
|
251
|
-
## Supported Fiat Currencies
|
|
445
|
+
# Get rate for a cryptocurrency
|
|
446
|
+
coinpay rates get BTC
|
|
252
447
|
|
|
253
|
-
|
|
448
|
+
# Get all rates
|
|
449
|
+
coinpay rates list
|
|
450
|
+
```
|
|
254
451
|
|
|
255
452
|
## Environment Variables
|
|
256
453
|
|
|
257
454
|
| Variable | Description |
|
|
258
455
|
|----------|-------------|
|
|
259
456
|
| `COINPAY_API_KEY` | API key (overrides config file) |
|
|
260
|
-
| `COINPAY_BASE_URL` | Custom API URL |
|
|
457
|
+
| `COINPAY_BASE_URL` | Custom API URL (for development) |
|
|
261
458
|
|
|
262
459
|
## Error Handling
|
|
263
460
|
|
|
@@ -268,13 +465,91 @@ try {
|
|
|
268
465
|
if (error.status === 401) {
|
|
269
466
|
console.error('Invalid API key');
|
|
270
467
|
} else if (error.status === 400) {
|
|
271
|
-
console.error('Invalid request:', error.response);
|
|
468
|
+
console.error('Invalid request:', error.response?.error);
|
|
469
|
+
} else if (error.status === 429) {
|
|
470
|
+
console.error('Rate limit exceeded or transaction limit reached');
|
|
471
|
+
console.error('Usage:', error.response?.usage);
|
|
272
472
|
} else {
|
|
273
473
|
console.error('Error:', error.message);
|
|
274
474
|
}
|
|
275
475
|
}
|
|
276
476
|
```
|
|
277
477
|
|
|
478
|
+
## Common Integration Patterns
|
|
479
|
+
|
|
480
|
+
### E-commerce Checkout
|
|
481
|
+
|
|
482
|
+
```javascript
|
|
483
|
+
// In your checkout handler
|
|
484
|
+
app.post('/checkout', async (req, res) => {
|
|
485
|
+
const { orderId, amount, cryptocurrency } = req.body;
|
|
486
|
+
|
|
487
|
+
const payment = await coinpay.createPayment({
|
|
488
|
+
businessId: process.env.COINPAY_BUSINESS_ID,
|
|
489
|
+
amount,
|
|
490
|
+
blockchain: cryptocurrency,
|
|
491
|
+
description: `Order #${orderId}`,
|
|
492
|
+
metadata: { orderId }
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// Save payment ID to your order
|
|
496
|
+
await db.orders.update(orderId, {
|
|
497
|
+
paymentId: payment.payment.id,
|
|
498
|
+
paymentAddress: payment.payment.payment_address
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
res.json({
|
|
502
|
+
paymentAddress: payment.payment.payment_address,
|
|
503
|
+
amount: payment.payment.crypto_amount,
|
|
504
|
+
qrCode: payment.payment.qr_code,
|
|
505
|
+
expiresAt: payment.payment.expires_at
|
|
506
|
+
});
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// Webhook handler
|
|
510
|
+
app.post('/webhook/coinpay', createWebhookHandler({
|
|
511
|
+
secret: process.env.COINPAY_WEBHOOK_SECRET,
|
|
512
|
+
onEvent: async (event) => {
|
|
513
|
+
if (event.type === 'payment.confirmed') {
|
|
514
|
+
const { orderId } = event.data.payment.metadata;
|
|
515
|
+
await db.orders.update(orderId, { status: 'paid' });
|
|
516
|
+
await sendOrderConfirmationEmail(orderId);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}));
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Subscription Payments
|
|
523
|
+
|
|
524
|
+
```javascript
|
|
525
|
+
// Create subscription payment
|
|
526
|
+
const payment = await coinpay.createPayment({
|
|
527
|
+
businessId: process.env.COINPAY_BUSINESS_ID,
|
|
528
|
+
amount: 9.99,
|
|
529
|
+
blockchain: 'USDC_MATIC', // Stablecoin for predictable pricing
|
|
530
|
+
description: 'Monthly subscription',
|
|
531
|
+
metadata: {
|
|
532
|
+
userId: user.id,
|
|
533
|
+
subscriptionId: subscription.id,
|
|
534
|
+
period: '2024-01'
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
## Testing
|
|
540
|
+
|
|
541
|
+
For development and testing, you can:
|
|
542
|
+
|
|
543
|
+
1. Use the dashboard's "Create Test Payment" feature
|
|
544
|
+
2. Set a custom `baseUrl` pointing to your local development server
|
|
545
|
+
3. Use testnet addresses (when supported)
|
|
546
|
+
|
|
547
|
+
## Support
|
|
548
|
+
|
|
549
|
+
- Documentation: [docs.coinpayportal.com](https://docs.coinpayportal.com)
|
|
550
|
+
- Email: support@coinpayportal.com
|
|
551
|
+
- Status: [status.coinpayportal.com](https://status.coinpayportal.com)
|
|
552
|
+
|
|
278
553
|
## License
|
|
279
554
|
|
|
280
555
|
MIT
|
package/bin/coinpay.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { CoinPayClient } from '../src/client.js';
|
|
9
|
-
import { PaymentStatus,
|
|
9
|
+
import { PaymentStatus, Blockchain, FiatCurrency } from '../src/payments.js';
|
|
10
10
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
11
11
|
import { homedir } from 'os';
|
|
12
12
|
import { join } from 'path';
|
|
@@ -70,7 +70,7 @@ function getApiKey() {
|
|
|
70
70
|
*/
|
|
71
71
|
function getBaseUrl() {
|
|
72
72
|
const config = loadConfig();
|
|
73
|
-
return process.env.COINPAY_BASE_URL || config.baseUrl || 'https://
|
|
73
|
+
return process.env.COINPAY_BASE_URL || config.baseUrl || 'https://coinpayportal.com/api';
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|
|
@@ -152,15 +152,31 @@ ${colors.cyan}Options:${colors.reset}
|
|
|
152
152
|
--version, -v Show version
|
|
153
153
|
--json Output as JSON
|
|
154
154
|
--business-id <id> Business ID for operations
|
|
155
|
-
--amount <amount> Payment amount
|
|
156
|
-
--currency <code> Fiat currency (USD, EUR, etc.)
|
|
157
|
-
--
|
|
155
|
+
--amount <amount> Payment amount in fiat currency
|
|
156
|
+
--currency <code> Fiat currency (USD, EUR, etc.) - default: USD
|
|
157
|
+
--blockchain <code> Blockchain (BTC, ETH, SOL, MATIC, BCH, USDC_ETH, USDC_MATIC, USDC_SOL)
|
|
158
|
+
--description <text> Payment description
|
|
158
159
|
|
|
159
160
|
${colors.cyan}Examples:${colors.reset}
|
|
160
|
-
|
|
161
|
-
coinpay
|
|
161
|
+
# Configure your API key (get it from your CoinPay dashboard)
|
|
162
|
+
coinpay config set-key cp_live_xxxxx
|
|
163
|
+
|
|
164
|
+
# Create a $100 Bitcoin payment
|
|
165
|
+
coinpay payment create --business-id biz_123 --amount 100 --blockchain BTC
|
|
166
|
+
|
|
167
|
+
# Create a $50 Ethereum payment with description
|
|
168
|
+
coinpay payment create --business-id biz_123 --amount 50 --blockchain ETH --description "Order #12345"
|
|
169
|
+
|
|
170
|
+
# Create a USDC payment on Polygon
|
|
171
|
+
coinpay payment create --business-id biz_123 --amount 25 --blockchain USDC_MATIC
|
|
172
|
+
|
|
173
|
+
# Get payment status
|
|
162
174
|
coinpay payment get pay_abc123
|
|
175
|
+
|
|
176
|
+
# Get exchange rates
|
|
163
177
|
coinpay rates get BTC
|
|
178
|
+
|
|
179
|
+
# List your businesses
|
|
164
180
|
coinpay business list
|
|
165
181
|
|
|
166
182
|
${colors.cyan}Environment Variables:${colors.reset}
|
|
@@ -218,10 +234,11 @@ async function handlePayment(subcommand, args, flags) {
|
|
|
218
234
|
|
|
219
235
|
switch (subcommand) {
|
|
220
236
|
case 'create': {
|
|
221
|
-
const { 'business-id': businessId, amount, currency,
|
|
237
|
+
const { 'business-id': businessId, amount, currency = 'USD', blockchain, description } = flags;
|
|
222
238
|
|
|
223
|
-
if (!businessId || !amount || !
|
|
224
|
-
print.error('Required: --business-id, --amount, --
|
|
239
|
+
if (!businessId || !amount || !blockchain) {
|
|
240
|
+
print.error('Required: --business-id, --amount, --blockchain');
|
|
241
|
+
print.info('Example: coinpay payment create --business-id biz_123 --amount 100 --blockchain BTC');
|
|
225
242
|
return;
|
|
226
243
|
}
|
|
227
244
|
|
|
@@ -229,12 +246,19 @@ async function handlePayment(subcommand, args, flags) {
|
|
|
229
246
|
businessId,
|
|
230
247
|
amount: parseFloat(amount),
|
|
231
248
|
currency,
|
|
232
|
-
|
|
249
|
+
blockchain,
|
|
233
250
|
description,
|
|
234
251
|
});
|
|
235
252
|
|
|
236
253
|
print.success('Payment created');
|
|
237
|
-
|
|
254
|
+
if (payment.payment) {
|
|
255
|
+
print.info(`Payment Address: ${payment.payment.payment_address}`);
|
|
256
|
+
print.info(`Amount: ${payment.payment.crypto_amount} ${payment.payment.blockchain}`);
|
|
257
|
+
print.info(`Expires: ${payment.payment.expires_at}`);
|
|
258
|
+
}
|
|
259
|
+
if (!flags.json) {
|
|
260
|
+
print.json(payment);
|
|
261
|
+
}
|
|
238
262
|
break;
|
|
239
263
|
}
|
|
240
264
|
|
|
@@ -364,7 +388,7 @@ async function handleRates(subcommand, args, flags) {
|
|
|
364
388
|
const { fiat } = flags;
|
|
365
389
|
|
|
366
390
|
if (!crypto) {
|
|
367
|
-
print.error('
|
|
391
|
+
print.error('Blockchain code required (BTC, ETH, SOL, etc.)');
|
|
368
392
|
return;
|
|
369
393
|
}
|
|
370
394
|
|
|
@@ -375,7 +399,7 @@ async function handleRates(subcommand, args, flags) {
|
|
|
375
399
|
|
|
376
400
|
case 'list': {
|
|
377
401
|
const { fiat } = flags;
|
|
378
|
-
const cryptos = Object.values(
|
|
402
|
+
const cryptos = Object.values(Blockchain);
|
|
379
403
|
const rates = await client.getExchangeRates(cryptos, fiat);
|
|
380
404
|
print.json(rates);
|
|
381
405
|
break;
|
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Main client class for interacting with the CoinPay API
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const DEFAULT_BASE_URL = 'https://
|
|
6
|
+
const DEFAULT_BASE_URL = 'https://coinpayportal.com/api';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* CoinPay API Client
|
|
@@ -17,7 +17,7 @@ export class CoinPayClient {
|
|
|
17
17
|
* Create a new CoinPay client
|
|
18
18
|
* @param {Object} options - Client options
|
|
19
19
|
* @param {string} options.apiKey - Your CoinPay API key
|
|
20
|
-
* @param {string} [options.baseUrl] - API base URL (default: https://
|
|
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
23
|
constructor({ apiKey, baseUrl = DEFAULT_BASE_URL, timeout = 30000 }) {
|
|
@@ -47,7 +47,7 @@ export class CoinPayClient {
|
|
|
47
47
|
signal: controller.signal,
|
|
48
48
|
headers: {
|
|
49
49
|
'Content-Type': 'application/json',
|
|
50
|
-
'
|
|
50
|
+
'Authorization': `Bearer ${this.#apiKey}`,
|
|
51
51
|
...options.headers,
|
|
52
52
|
},
|
|
53
53
|
});
|
|
@@ -74,48 +74,142 @@ export class CoinPayClient {
|
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Create a new payment
|
|
77
|
+
*
|
|
78
|
+
* This is the primary method for merchants to create payment requests.
|
|
79
|
+
* When a customer needs to pay, call this method to generate a unique
|
|
80
|
+
* payment address and QR code.
|
|
81
|
+
*
|
|
77
82
|
* @param {Object} params - Payment parameters
|
|
78
|
-
* @param {string} params.businessId - Business ID
|
|
79
|
-
* @param {number} params.amount - Amount in fiat currency
|
|
80
|
-
* @param {string} params.currency - Fiat currency code (USD, EUR, etc.)
|
|
81
|
-
* @param {string} params.
|
|
82
|
-
* @param {string} [params.description] - Payment description
|
|
83
|
-
* @param {
|
|
84
|
-
* @
|
|
85
|
-
*
|
|
83
|
+
* @param {string} params.businessId - Business ID (from your CoinPay dashboard)
|
|
84
|
+
* @param {number} params.amount - Amount in fiat currency (e.g., 100.00)
|
|
85
|
+
* @param {string} [params.currency='USD'] - Fiat currency code (USD, EUR, etc.)
|
|
86
|
+
* @param {string} params.blockchain - Blockchain/cryptocurrency (BTC, ETH, SOL, MATIC, BCH, USDC_ETH, USDC_MATIC, USDC_SOL)
|
|
87
|
+
* @param {string} [params.description] - Payment description (shown to customer)
|
|
88
|
+
* @param {Object} [params.metadata] - Custom metadata (e.g., { orderId: 'ORD-123', customerId: 'CUST-456' })
|
|
89
|
+
* @returns {Promise<Object>} Created payment with address and QR code
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* // Create a $50 payment in Bitcoin
|
|
93
|
+
* const payment = await client.createPayment({
|
|
94
|
+
* businessId: 'your-business-id',
|
|
95
|
+
* amount: 50.00,
|
|
96
|
+
* currency: 'USD',
|
|
97
|
+
* blockchain: 'BTC',
|
|
98
|
+
* description: 'Order #12345',
|
|
99
|
+
* metadata: { orderId: '12345', customerEmail: 'customer@example.com' }
|
|
100
|
+
* });
|
|
101
|
+
*
|
|
102
|
+
* // Display payment.payment_address and payment.qr_code to customer
|
|
86
103
|
*/
|
|
87
104
|
async createPayment({
|
|
88
105
|
businessId,
|
|
89
106
|
amount,
|
|
90
|
-
currency,
|
|
91
|
-
|
|
107
|
+
currency = 'USD',
|
|
108
|
+
blockchain,
|
|
92
109
|
description,
|
|
93
110
|
metadata,
|
|
94
|
-
callbackUrl,
|
|
95
111
|
}) {
|
|
112
|
+
// Map to API field names (snake_case)
|
|
96
113
|
return this.request('/payments/create', {
|
|
97
114
|
method: 'POST',
|
|
98
115
|
body: JSON.stringify({
|
|
99
|
-
businessId,
|
|
116
|
+
business_id: businessId,
|
|
100
117
|
amount,
|
|
101
118
|
currency,
|
|
102
|
-
|
|
119
|
+
blockchain: blockchain?.toUpperCase(),
|
|
103
120
|
description,
|
|
104
121
|
metadata,
|
|
105
|
-
callbackUrl,
|
|
106
122
|
}),
|
|
107
123
|
});
|
|
108
124
|
}
|
|
109
125
|
|
|
110
126
|
/**
|
|
111
127
|
* Get payment by ID
|
|
128
|
+
*
|
|
129
|
+
* Use this to check the current status of a payment. You can poll this
|
|
130
|
+
* endpoint to wait for payment completion, or use webhooks for real-time
|
|
131
|
+
* notifications.
|
|
132
|
+
*
|
|
112
133
|
* @param {string} paymentId - Payment ID
|
|
113
|
-
* @returns {Promise<Object>} Payment details
|
|
134
|
+
* @returns {Promise<Object>} Payment details including status
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const result = await client.getPayment('pay_abc123');
|
|
138
|
+
* console.log(result.payment.status); // 'pending', 'confirmed', 'forwarded', etc.
|
|
114
139
|
*/
|
|
115
140
|
async getPayment(paymentId) {
|
|
116
141
|
return this.request(`/payments/${paymentId}`);
|
|
117
142
|
}
|
|
118
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Wait for payment to reach a terminal status
|
|
146
|
+
*
|
|
147
|
+
* Polls the payment status until it reaches a terminal state (confirmed,
|
|
148
|
+
* forwarded, expired, or failed). Useful for simple integrations that
|
|
149
|
+
* don't use webhooks.
|
|
150
|
+
*
|
|
151
|
+
* For production use, webhooks are recommended over polling.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} paymentId - Payment ID
|
|
154
|
+
* @param {Object} [options] - Polling options
|
|
155
|
+
* @param {number} [options.interval=5000] - Polling interval in ms (default: 5 seconds)
|
|
156
|
+
* @param {number} [options.timeout=3600000] - Maximum wait time in ms (default: 1 hour)
|
|
157
|
+
* @param {string[]} [options.targetStatuses] - Statuses to wait for (default: ['confirmed', 'forwarded', 'expired', 'failed'])
|
|
158
|
+
* @param {Function} [options.onStatusChange] - Callback when status changes
|
|
159
|
+
* @returns {Promise<Object>} Final payment details
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* // Simple usage - wait for payment to complete
|
|
163
|
+
* const payment = await client.waitForPayment('pay_abc123');
|
|
164
|
+
* if (payment.payment.status === 'confirmed' || payment.payment.status === 'forwarded') {
|
|
165
|
+
* console.log('Payment successful!');
|
|
166
|
+
* }
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* // With status change callback
|
|
170
|
+
* const payment = await client.waitForPayment('pay_abc123', {
|
|
171
|
+
* interval: 3000,
|
|
172
|
+
* timeout: 600000, // 10 minutes
|
|
173
|
+
* onStatusChange: (status, payment) => {
|
|
174
|
+
* console.log(`Payment status: ${status}`);
|
|
175
|
+
* }
|
|
176
|
+
* });
|
|
177
|
+
*/
|
|
178
|
+
async waitForPayment(paymentId, options = {}) {
|
|
179
|
+
const {
|
|
180
|
+
interval = 5000,
|
|
181
|
+
timeout = 3600000,
|
|
182
|
+
targetStatuses = ['confirmed', 'forwarded', 'expired', 'failed'],
|
|
183
|
+
onStatusChange,
|
|
184
|
+
} = options;
|
|
185
|
+
|
|
186
|
+
const startTime = Date.now();
|
|
187
|
+
let lastStatus = null;
|
|
188
|
+
|
|
189
|
+
while (Date.now() - startTime < timeout) {
|
|
190
|
+
const result = await this.getPayment(paymentId);
|
|
191
|
+
const currentStatus = result.payment?.status;
|
|
192
|
+
|
|
193
|
+
// Notify on status change
|
|
194
|
+
if (currentStatus !== lastStatus) {
|
|
195
|
+
if (onStatusChange && lastStatus !== null) {
|
|
196
|
+
onStatusChange(currentStatus, result.payment);
|
|
197
|
+
}
|
|
198
|
+
lastStatus = currentStatus;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check if we've reached a target status
|
|
202
|
+
if (targetStatuses.includes(currentStatus)) {
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Wait before next poll
|
|
207
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
throw new Error(`Payment status check timed out after ${timeout}ms`);
|
|
211
|
+
}
|
|
212
|
+
|
|
119
213
|
/**
|
|
120
214
|
* List payments for a business
|
|
121
215
|
* @param {Object} params - Query parameters
|
|
@@ -140,13 +234,66 @@ export class CoinPayClient {
|
|
|
140
234
|
}
|
|
141
235
|
|
|
142
236
|
/**
|
|
143
|
-
* Get payment QR code
|
|
237
|
+
* Get payment QR code URL
|
|
238
|
+
*
|
|
239
|
+
* Returns the URL to the QR code image endpoint. The endpoint returns
|
|
240
|
+
* binary PNG image data that can be used directly in an <img> tag.
|
|
241
|
+
*
|
|
144
242
|
* @param {string} paymentId - Payment ID
|
|
145
|
-
* @
|
|
146
|
-
*
|
|
243
|
+
* @returns {string} URL to the QR code image
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* // Get QR code URL for use in HTML
|
|
247
|
+
* const qrUrl = client.getPaymentQRUrl('pay_abc123');
|
|
248
|
+
* // Use in HTML: <img src={qrUrl} alt="Payment QR Code" />
|
|
147
249
|
*/
|
|
148
|
-
|
|
149
|
-
return this
|
|
250
|
+
getPaymentQRUrl(paymentId) {
|
|
251
|
+
return `${this.#baseUrl}/payments/${paymentId}/qr`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get payment QR code as binary image data
|
|
256
|
+
*
|
|
257
|
+
* Fetches the QR code image as binary data (ArrayBuffer).
|
|
258
|
+
* Useful for server-side processing or saving to file.
|
|
259
|
+
*
|
|
260
|
+
* @param {string} paymentId - Payment ID
|
|
261
|
+
* @returns {Promise<ArrayBuffer>} QR code image as binary data
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* // Get QR code as binary data
|
|
265
|
+
* const imageData = await client.getPaymentQR('pay_abc123');
|
|
266
|
+
* // Save to file (Node.js)
|
|
267
|
+
* fs.writeFileSync('qr.png', Buffer.from(imageData));
|
|
268
|
+
*/
|
|
269
|
+
async getPaymentQR(paymentId) {
|
|
270
|
+
const url = `${this.#baseUrl}/payments/${paymentId}/qr`;
|
|
271
|
+
const controller = new AbortController();
|
|
272
|
+
const timeoutId = setTimeout(() => controller.abort(), this.#timeout);
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const response = await fetch(url, {
|
|
276
|
+
signal: controller.signal,
|
|
277
|
+
headers: {
|
|
278
|
+
'Authorization': `Bearer ${this.#apiKey}`,
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (!response.ok) {
|
|
283
|
+
const error = new Error(`HTTP ${response.status}`);
|
|
284
|
+
error.status = response.status;
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return response.arrayBuffer();
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (error.name === 'AbortError') {
|
|
291
|
+
throw new Error(`Request timeout after ${this.#timeout}ms`);
|
|
292
|
+
}
|
|
293
|
+
throw error;
|
|
294
|
+
} finally {
|
|
295
|
+
clearTimeout(timeoutId);
|
|
296
|
+
}
|
|
150
297
|
}
|
|
151
298
|
|
|
152
299
|
/**
|
package/src/index.js
CHANGED
|
@@ -3,10 +3,31 @@
|
|
|
3
3
|
* Cryptocurrency payment integration for Node.js
|
|
4
4
|
*
|
|
5
5
|
* @module @profullstack/coinpay
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Quick start
|
|
9
|
+
* import { CoinPayClient, Blockchain } from '@profullstack/coinpay';
|
|
10
|
+
*
|
|
11
|
+
* const client = new CoinPayClient({ apiKey: 'cp_live_xxxxx' });
|
|
12
|
+
*
|
|
13
|
+
* const payment = await client.createPayment({
|
|
14
|
+
* businessId: 'your-business-id',
|
|
15
|
+
* amount: 100,
|
|
16
|
+
* blockchain: Blockchain.BTC,
|
|
17
|
+
* description: 'Order #12345'
|
|
18
|
+
* });
|
|
6
19
|
*/
|
|
7
20
|
|
|
8
21
|
import { CoinPayClient } from './client.js';
|
|
9
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
createPayment,
|
|
24
|
+
getPayment,
|
|
25
|
+
listPayments,
|
|
26
|
+
Blockchain,
|
|
27
|
+
Cryptocurrency,
|
|
28
|
+
PaymentStatus,
|
|
29
|
+
FiatCurrency,
|
|
30
|
+
} from './payments.js';
|
|
10
31
|
import {
|
|
11
32
|
verifyWebhookSignature,
|
|
12
33
|
generateWebhookSignature,
|
|
@@ -16,10 +37,21 @@ import {
|
|
|
16
37
|
} from './webhooks.js';
|
|
17
38
|
|
|
18
39
|
export {
|
|
40
|
+
// Client
|
|
19
41
|
CoinPayClient,
|
|
42
|
+
|
|
43
|
+
// Payment functions
|
|
20
44
|
createPayment,
|
|
21
45
|
getPayment,
|
|
22
46
|
listPayments,
|
|
47
|
+
|
|
48
|
+
// Constants
|
|
49
|
+
Blockchain,
|
|
50
|
+
Cryptocurrency, // Deprecated, use Blockchain
|
|
51
|
+
PaymentStatus,
|
|
52
|
+
FiatCurrency,
|
|
53
|
+
|
|
54
|
+
// Webhook utilities
|
|
23
55
|
verifyWebhookSignature,
|
|
24
56
|
generateWebhookSignature,
|
|
25
57
|
parseWebhookPayload,
|
package/src/payments.js
CHANGED
|
@@ -1,33 +1,65 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Payment utilities for CoinPay SDK
|
|
3
|
+
*
|
|
4
|
+
* This module provides helper functions for creating and managing cryptocurrency payments.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // Quick payment creation
|
|
8
|
+
* import { createPayment } from '@coinpay/sdk';
|
|
9
|
+
*
|
|
10
|
+
* const payment = await createPayment({
|
|
11
|
+
* apiKey: 'cp_live_xxxxx',
|
|
12
|
+
* businessId: 'your-business-id',
|
|
13
|
+
* amount: 100.00,
|
|
14
|
+
* blockchain: 'ETH',
|
|
15
|
+
* description: 'Order #12345'
|
|
16
|
+
* });
|
|
3
17
|
*/
|
|
4
18
|
|
|
5
19
|
import { CoinPayClient } from './client.js';
|
|
6
20
|
|
|
7
21
|
/**
|
|
8
22
|
* Create a payment using a client instance or API key
|
|
23
|
+
*
|
|
24
|
+
* This is a convenience function for creating payments without manually
|
|
25
|
+
* instantiating a client. For multiple operations, create a client instance
|
|
26
|
+
* and reuse it.
|
|
27
|
+
*
|
|
9
28
|
* @param {Object} params - Payment parameters
|
|
10
|
-
* @param {string} params.apiKey - API key (if not using client)
|
|
11
|
-
* @param {CoinPayClient} [params.client] - Existing client instance
|
|
12
|
-
* @param {string} params.businessId - Business ID
|
|
13
|
-
* @param {number} params.amount - Amount in fiat currency
|
|
14
|
-
* @param {string} params.currency - Fiat currency code
|
|
15
|
-
* @param {string} params.
|
|
16
|
-
* @param {string} [params.description] - Payment description
|
|
17
|
-
* @param {
|
|
18
|
-
* @
|
|
19
|
-
*
|
|
29
|
+
* @param {string} params.apiKey - API key (required if not using client)
|
|
30
|
+
* @param {CoinPayClient} [params.client] - Existing client instance (optional)
|
|
31
|
+
* @param {string} params.businessId - Business ID from your CoinPay dashboard
|
|
32
|
+
* @param {number} params.amount - Amount in fiat currency (e.g., 100.00)
|
|
33
|
+
* @param {string} [params.currency='USD'] - Fiat currency code (USD, EUR, etc.)
|
|
34
|
+
* @param {string} params.blockchain - Blockchain to use (BTC, ETH, SOL, MATIC, BCH, USDC_ETH, USDC_MATIC, USDC_SOL)
|
|
35
|
+
* @param {string} [params.description] - Payment description shown to customer
|
|
36
|
+
* @param {Object} [params.metadata] - Custom metadata for your records
|
|
37
|
+
* @returns {Promise<Object>} Created payment with address and QR code
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Create a Bitcoin payment
|
|
41
|
+
* const payment = await createPayment({
|
|
42
|
+
* apiKey: 'cp_live_xxxxx',
|
|
43
|
+
* businessId: 'biz_123',
|
|
44
|
+
* amount: 50.00,
|
|
45
|
+
* currency: 'USD',
|
|
46
|
+
* blockchain: 'BTC',
|
|
47
|
+
* description: 'Premium subscription',
|
|
48
|
+
* metadata: { userId: 'user_456', plan: 'premium' }
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* console.log('Payment address:', payment.payment.payment_address);
|
|
52
|
+
* console.log('Amount in BTC:', payment.payment.crypto_amount);
|
|
20
53
|
*/
|
|
21
54
|
export async function createPayment({
|
|
22
55
|
apiKey,
|
|
23
56
|
client,
|
|
24
57
|
businessId,
|
|
25
58
|
amount,
|
|
26
|
-
currency,
|
|
27
|
-
|
|
59
|
+
currency = 'USD',
|
|
60
|
+
blockchain,
|
|
28
61
|
description,
|
|
29
62
|
metadata,
|
|
30
|
-
callbackUrl,
|
|
31
63
|
}) {
|
|
32
64
|
const coinpay = client || new CoinPayClient({ apiKey });
|
|
33
65
|
|
|
@@ -35,10 +67,9 @@ export async function createPayment({
|
|
|
35
67
|
businessId,
|
|
36
68
|
amount,
|
|
37
69
|
currency,
|
|
38
|
-
|
|
70
|
+
blockchain,
|
|
39
71
|
description,
|
|
40
72
|
metadata,
|
|
41
|
-
callbackUrl,
|
|
42
73
|
});
|
|
43
74
|
}
|
|
44
75
|
|
|
@@ -91,16 +122,44 @@ export const PaymentStatus = {
|
|
|
91
122
|
};
|
|
92
123
|
|
|
93
124
|
/**
|
|
94
|
-
* Supported cryptocurrencies
|
|
125
|
+
* Supported blockchains/cryptocurrencies
|
|
126
|
+
*
|
|
127
|
+
* Use these constants when creating payments to ensure valid blockchain values.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* import { Blockchain, createPayment } from '@coinpay/sdk';
|
|
131
|
+
*
|
|
132
|
+
* const payment = await createPayment({
|
|
133
|
+
* apiKey: 'cp_live_xxxxx',
|
|
134
|
+
* businessId: 'biz_123',
|
|
135
|
+
* amount: 100,
|
|
136
|
+
* blockchain: Blockchain.ETH
|
|
137
|
+
* });
|
|
95
138
|
*/
|
|
96
|
-
export const
|
|
139
|
+
export const Blockchain = {
|
|
140
|
+
/** Bitcoin */
|
|
97
141
|
BTC: 'BTC',
|
|
142
|
+
/** Bitcoin Cash */
|
|
98
143
|
BCH: 'BCH',
|
|
144
|
+
/** Ethereum */
|
|
99
145
|
ETH: 'ETH',
|
|
146
|
+
/** Polygon (MATIC) */
|
|
100
147
|
MATIC: 'MATIC',
|
|
148
|
+
/** Solana */
|
|
101
149
|
SOL: 'SOL',
|
|
150
|
+
/** USDC on Ethereum */
|
|
151
|
+
USDC_ETH: 'USDC_ETH',
|
|
152
|
+
/** USDC on Polygon */
|
|
153
|
+
USDC_MATIC: 'USDC_MATIC',
|
|
154
|
+
/** USDC on Solana */
|
|
155
|
+
USDC_SOL: 'USDC_SOL',
|
|
102
156
|
};
|
|
103
157
|
|
|
158
|
+
/**
|
|
159
|
+
* @deprecated Use Blockchain instead
|
|
160
|
+
*/
|
|
161
|
+
export const Cryptocurrency = Blockchain;
|
|
162
|
+
|
|
104
163
|
/**
|
|
105
164
|
* Supported fiat currencies
|
|
106
165
|
*/
|
|
@@ -117,6 +176,7 @@ export default {
|
|
|
117
176
|
getPayment,
|
|
118
177
|
listPayments,
|
|
119
178
|
PaymentStatus,
|
|
179
|
+
Blockchain,
|
|
120
180
|
Cryptocurrency,
|
|
121
181
|
FiatCurrency,
|
|
122
182
|
};
|