@profullstack/coinpay 0.1.0 → 0.3.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/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +781 -143
- package/bin/coinpay.js +38 -14
- package/package.json +29 -12
- package/src/client.d.ts +266 -0
- package/src/client.js +170 -23
- package/src/index.d.ts +37 -0
- package/src/index.js +33 -1
- package/src/payments.d.ts +153 -0
- package/src/payments.js +78 -18
- package/src/webhooks.d.ts +134 -0
package/README.md
CHANGED
|
@@ -1,280 +1,918 @@
|
|
|
1
1
|
# @profullstack/coinpay
|
|
2
2
|
|
|
3
|
-
CoinPay SDK & CLI
|
|
3
|
+
> CoinPay SDK & CLI — Accept cryptocurrency payments in your Node.js application.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@profullstack/coinpay)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
|
|
9
|
+
Non-custodial, multi-chain payment processing for **Bitcoin**, **Ethereum**, **Solana**, **Polygon**, **Bitcoin Cash**, and **USDC** (on ETH, POL, SOL).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [How It Works](#how-it-works)
|
|
16
|
+
- [Installation](#installation)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [Supported Blockchains](#supported-blockchains)
|
|
19
|
+
- [API Reference](#api-reference)
|
|
20
|
+
- [CoinPayClient](#coinpayclient)
|
|
21
|
+
- [Payments](#payments)
|
|
22
|
+
- [Payment Status Polling](#payment-status-polling)
|
|
23
|
+
- [QR Codes](#qr-codes)
|
|
24
|
+
- [Exchange Rates](#exchange-rates)
|
|
25
|
+
- [Business Management](#business-management)
|
|
26
|
+
- [Webhooks](#webhooks)
|
|
27
|
+
- [Standalone Functions](#standalone-functions)
|
|
28
|
+
- [Constants](#constants)
|
|
29
|
+
- [CLI Reference](#cli-reference)
|
|
30
|
+
- [Webhook Integration](#webhook-integration)
|
|
31
|
+
- [Error Handling](#error-handling)
|
|
32
|
+
- [Integration Patterns](#integration-patterns)
|
|
33
|
+
- [TypeScript](#typescript)
|
|
34
|
+
- [Environment Variables](#environment-variables)
|
|
35
|
+
- [Examples](#examples)
|
|
36
|
+
- [Testing](#testing)
|
|
37
|
+
- [License](#license)
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## How It Works
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
┌──────────┐ 1. Create payment ┌──────────┐
|
|
45
|
+
│ Your │ ───────────────────────> │ CoinPay │
|
|
46
|
+
│ Server │ <─────────────────────── │ API │
|
|
47
|
+
│ │ Address + QR code │ │
|
|
48
|
+
└────┬─────┘ └────┬─────┘
|
|
49
|
+
│ │
|
|
50
|
+
│ 2. Show address/QR │ 4. Webhook notification
|
|
51
|
+
│ to customer │ (payment confirmed)
|
|
52
|
+
▼ │
|
|
53
|
+
┌──────────┐ 3. Sends crypto ┌────▼─────┐
|
|
54
|
+
│ Customer │ ───────────────────────> │Blockchain│
|
|
55
|
+
│ │ │ Network │
|
|
56
|
+
└──────────┘ └──────────┘
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
1. **Your server** calls the CoinPay API to create a payment request
|
|
60
|
+
2. **CoinPay** generates a unique payment address and QR code — display these to your customer
|
|
61
|
+
3. **Customer** sends cryptocurrency to the address
|
|
62
|
+
4. **CoinPay** monitors the blockchain and notifies you via webhook when payment is confirmed
|
|
63
|
+
5. **Funds** are automatically forwarded to your configured wallet
|
|
64
|
+
|
|
65
|
+
---
|
|
4
66
|
|
|
5
67
|
## Installation
|
|
6
68
|
|
|
7
69
|
```bash
|
|
8
|
-
#
|
|
70
|
+
# pnpm (recommended)
|
|
9
71
|
pnpm add @profullstack/coinpay
|
|
10
72
|
|
|
11
|
-
#
|
|
73
|
+
# npm
|
|
12
74
|
npm install @profullstack/coinpay
|
|
13
75
|
|
|
14
|
-
# Global CLI
|
|
76
|
+
# Global CLI
|
|
15
77
|
pnpm add -g @profullstack/coinpay
|
|
16
78
|
```
|
|
17
79
|
|
|
80
|
+
**Requirements:** Node.js ≥ 20. Zero runtime dependencies — uses built-in `fetch` and `crypto`.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
18
84
|
## Quick Start
|
|
19
85
|
|
|
20
|
-
###
|
|
86
|
+
### 1. Get Your API Key
|
|
87
|
+
|
|
88
|
+
1. Sign up at [coinpayportal.com](https://coinpayportal.com)
|
|
89
|
+
2. Create a business in your dashboard
|
|
90
|
+
3. Configure wallet addresses for each crypto you want to accept
|
|
91
|
+
4. Copy your API key (starts with `cp_live_`)
|
|
92
|
+
|
|
93
|
+
### 2. Create a Payment
|
|
21
94
|
|
|
22
95
|
```javascript
|
|
23
|
-
import { CoinPayClient } from '@profullstack/coinpay';
|
|
96
|
+
import { CoinPayClient, Blockchain } from '@profullstack/coinpay';
|
|
24
97
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
apiKey: 'your-api-key',
|
|
98
|
+
const client = new CoinPayClient({
|
|
99
|
+
apiKey: 'cp_live_your_api_key_here',
|
|
28
100
|
});
|
|
29
101
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
amount: 100,
|
|
102
|
+
const { payment } = await client.createPayment({
|
|
103
|
+
businessId: 'your-business-id',
|
|
104
|
+
amount: 99.99,
|
|
34
105
|
currency: 'USD',
|
|
35
|
-
|
|
106
|
+
blockchain: Blockchain.BTC,
|
|
36
107
|
description: 'Order #12345',
|
|
108
|
+
metadata: { orderId: '12345' },
|
|
37
109
|
});
|
|
38
110
|
|
|
39
|
-
console.log(
|
|
40
|
-
console.log(
|
|
111
|
+
console.log('Send payment to:', payment.payment_address);
|
|
112
|
+
console.log('Amount:', payment.crypto_amount, 'BTC');
|
|
113
|
+
console.log('QR Code:', payment.qr_code);
|
|
41
114
|
```
|
|
42
115
|
|
|
43
|
-
###
|
|
116
|
+
### 3. Handle Payment Confirmation
|
|
44
117
|
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
coinpay config set-key sk_live_xxxxx
|
|
118
|
+
```javascript
|
|
119
|
+
import { createWebhookHandler, WebhookEvent } from '@profullstack/coinpay';
|
|
48
120
|
|
|
49
|
-
|
|
50
|
-
|
|
121
|
+
app.post('/webhook', createWebhookHandler({
|
|
122
|
+
secret: 'your-webhook-secret',
|
|
123
|
+
onEvent: async (event) => {
|
|
124
|
+
if (event.type === WebhookEvent.PAYMENT_COMPLETED) {
|
|
125
|
+
const orderId = event.data.payment.metadata.orderId;
|
|
126
|
+
await markOrderAsPaid(orderId);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
}));
|
|
130
|
+
```
|
|
51
131
|
|
|
52
|
-
|
|
53
|
-
coinpay payment get pay_abc123
|
|
132
|
+
---
|
|
54
133
|
|
|
55
|
-
|
|
56
|
-
coinpay payment list --business-id biz_123
|
|
134
|
+
## Supported Blockchains
|
|
57
135
|
|
|
58
|
-
|
|
59
|
-
|
|
136
|
+
| Blockchain | Code | Type |
|
|
137
|
+
|------------|------|------|
|
|
138
|
+
| Bitcoin | `BTC` | Native |
|
|
139
|
+
| Bitcoin Cash | `BCH` | Native |
|
|
140
|
+
| Ethereum | `ETH` | Native |
|
|
141
|
+
| Polygon | `POL` | Native |
|
|
142
|
+
| Solana | `SOL` | Native |
|
|
143
|
+
| USDC (Ethereum) | `USDC_ETH` | Stablecoin |
|
|
144
|
+
| USDC (Polygon) | `USDC_POL` | Stablecoin |
|
|
145
|
+
| USDC (Solana) | `USDC_SOL` | Stablecoin |
|
|
146
|
+
|
|
147
|
+
Use the `Blockchain` constant to avoid typos:
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
import { Blockchain } from '@profullstack/coinpay';
|
|
151
|
+
|
|
152
|
+
Blockchain.BTC // 'BTC'
|
|
153
|
+
Blockchain.ETH // 'ETH'
|
|
154
|
+
Blockchain.USDC_POL // 'USDC_POL'
|
|
60
155
|
```
|
|
61
156
|
|
|
62
|
-
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## API Reference
|
|
63
160
|
|
|
64
161
|
### CoinPayClient
|
|
65
162
|
|
|
163
|
+
The main class for all API operations.
|
|
164
|
+
|
|
66
165
|
```javascript
|
|
67
166
|
import { CoinPayClient } from '@profullstack/coinpay';
|
|
68
167
|
|
|
69
168
|
const client = new CoinPayClient({
|
|
70
|
-
apiKey: '
|
|
71
|
-
baseUrl: 'https://
|
|
72
|
-
timeout: 30000,
|
|
169
|
+
apiKey: 'cp_live_xxxxx', // Required
|
|
170
|
+
baseUrl: 'https://coinpayportal.com/api', // Optional (default)
|
|
171
|
+
timeout: 30000, // Optional: ms (default: 30s)
|
|
73
172
|
});
|
|
74
173
|
```
|
|
75
174
|
|
|
175
|
+
| Option | Type | Default | Description |
|
|
176
|
+
|--------|------|---------|-------------|
|
|
177
|
+
| `apiKey` | `string` | — | **Required.** Your CoinPay API key |
|
|
178
|
+
| `baseUrl` | `string` | `https://coinpayportal.com/api` | API base URL |
|
|
179
|
+
| `timeout` | `number` | `30000` | Request timeout in milliseconds |
|
|
180
|
+
|
|
181
|
+
**Throws** `Error` if `apiKey` is missing or empty.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
76
185
|
### Payments
|
|
77
186
|
|
|
187
|
+
#### `client.createPayment(params)`
|
|
188
|
+
|
|
189
|
+
Create a new payment request. Generates a unique blockchain address for the customer to pay.
|
|
190
|
+
|
|
78
191
|
```javascript
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
192
|
+
const { payment, usage } = await client.createPayment({
|
|
193
|
+
businessId: 'biz_123', // Required — from your dashboard
|
|
194
|
+
amount: 100.00, // Required — fiat amount
|
|
195
|
+
currency: 'USD', // Optional — fiat currency (default: 'USD')
|
|
196
|
+
blockchain: 'ETH', // Required — see Supported Blockchains
|
|
197
|
+
description: 'Order #123', // Optional — shown to customer
|
|
198
|
+
metadata: { // Optional — your custom data
|
|
199
|
+
orderId: '123',
|
|
200
|
+
customerEmail: 'a@b.com',
|
|
201
|
+
},
|
|
88
202
|
});
|
|
203
|
+
```
|
|
89
204
|
|
|
90
|
-
|
|
91
|
-
const payment = await client.getPayment('pay_abc123');
|
|
205
|
+
**Parameters:**
|
|
92
206
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
207
|
+
| Param | Type | Required | Description |
|
|
208
|
+
|-------|------|----------|-------------|
|
|
209
|
+
| `businessId` | `string` | ✅ | Business ID from your dashboard |
|
|
210
|
+
| `amount` | `number` | ✅ | Amount in fiat currency |
|
|
211
|
+
| `currency` | `string` | — | Fiat currency code (default: `'USD'`). Supports: `USD`, `EUR`, `GBP`, `CAD`, `AUD` |
|
|
212
|
+
| `blockchain` | `string` | ✅ | Blockchain code (e.g., `'BTC'`, `'ETH'`, `'USDC_POL'`) |
|
|
213
|
+
| `description` | `string` | — | Payment description visible to the customer |
|
|
214
|
+
| `metadata` | `object` | — | Arbitrary key-value data attached to the payment |
|
|
215
|
+
|
|
216
|
+
**Returns:**
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
{
|
|
220
|
+
success: true,
|
|
221
|
+
payment: {
|
|
222
|
+
id: 'pay_abc123',
|
|
223
|
+
business_id: 'biz_123',
|
|
224
|
+
amount: 100,
|
|
225
|
+
currency: 'USD',
|
|
226
|
+
blockchain: 'ETH',
|
|
227
|
+
crypto_amount: '0.0456',
|
|
228
|
+
payment_address: '0x1234...5678',
|
|
229
|
+
qr_code: 'data:image/png;base64,...',
|
|
230
|
+
status: 'pending',
|
|
231
|
+
expires_at: '2024-01-01T01:00:00.000Z',
|
|
232
|
+
created_at: '2024-01-01T00:00:00.000Z',
|
|
233
|
+
metadata: { orderId: '123' }
|
|
234
|
+
},
|
|
235
|
+
usage: {
|
|
236
|
+
current: 45,
|
|
237
|
+
limit: 100,
|
|
238
|
+
remaining: 55
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
#### `client.getPayment(paymentId)`
|
|
246
|
+
|
|
247
|
+
Retrieve a payment by its ID.
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
const { payment } = await client.getPayment('pay_abc123');
|
|
251
|
+
|
|
252
|
+
console.log(payment.status); // 'pending', 'confirmed', etc.
|
|
253
|
+
console.log(payment.crypto_amount); // '0.0456'
|
|
254
|
+
console.log(payment.tx_hash); // '0xabc...def' (once detected)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
#### `client.listPayments(params)`
|
|
260
|
+
|
|
261
|
+
List payments for a business with optional filtering and pagination.
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
const { payments } = await client.listPayments({
|
|
265
|
+
businessId: 'biz_123', // Required
|
|
266
|
+
status: 'completed', // Optional — filter by status
|
|
267
|
+
limit: 20, // Optional — results per page (default: 20)
|
|
268
|
+
offset: 0, // Optional — pagination offset (default: 0)
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
### Payment Status Polling
|
|
275
|
+
|
|
276
|
+
#### `client.waitForPayment(paymentId, options?)`
|
|
277
|
+
|
|
278
|
+
Polls `getPayment()` until the payment reaches a terminal status. Useful for simple integrations that don't use webhooks.
|
|
279
|
+
|
|
280
|
+
```javascript
|
|
281
|
+
const { payment } = await client.waitForPayment('pay_abc123', {
|
|
282
|
+
interval: 5000, // Poll every 5s (default)
|
|
283
|
+
timeout: 600000, // Give up after 10 min (default: 1 hour)
|
|
284
|
+
targetStatuses: ['confirmed', 'forwarded', 'expired', 'failed'],
|
|
285
|
+
onStatusChange: (status, payment) => {
|
|
286
|
+
console.log(`Status → ${status}`);
|
|
287
|
+
},
|
|
99
288
|
});
|
|
100
289
|
|
|
101
|
-
|
|
102
|
-
|
|
290
|
+
if (payment.status === 'confirmed' || payment.status === 'forwarded') {
|
|
291
|
+
console.log('Payment successful!');
|
|
292
|
+
}
|
|
103
293
|
```
|
|
104
294
|
|
|
295
|
+
| Option | Type | Default | Description |
|
|
296
|
+
|--------|------|---------|-------------|
|
|
297
|
+
| `interval` | `number` | `5000` | Polling interval in ms |
|
|
298
|
+
| `timeout` | `number` | `3600000` | Max wait time in ms |
|
|
299
|
+
| `targetStatuses` | `string[]` | `['confirmed','forwarded','expired','failed']` | Statuses that stop polling |
|
|
300
|
+
| `onStatusChange` | `function` | — | Callback `(status, payment) => void` |
|
|
301
|
+
|
|
302
|
+
> ⚠️ For production, use [webhooks](#webhook-integration) instead of polling.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
### Payment Statuses
|
|
307
|
+
|
|
308
|
+
| Status | Description |
|
|
309
|
+
|--------|-------------|
|
|
310
|
+
| `pending` | Waiting for customer to send payment |
|
|
311
|
+
| `detected` | Payment detected on blockchain, awaiting confirmations |
|
|
312
|
+
| `confirmed` | Payment confirmed — safe to fulfill the order |
|
|
313
|
+
| `forwarding` | Forwarding funds to your wallet |
|
|
314
|
+
| `forwarded` | Funds successfully sent to your wallet |
|
|
315
|
+
| `expired` | Payment request expired (customer didn't pay in time) |
|
|
316
|
+
| `failed` | Payment failed |
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
### QR Codes
|
|
321
|
+
|
|
322
|
+
#### `client.getPaymentQRUrl(paymentId)`
|
|
323
|
+
|
|
324
|
+
Returns the URL to the QR code image. **Synchronous** — no network request.
|
|
325
|
+
|
|
326
|
+
```javascript
|
|
327
|
+
const url = client.getPaymentQRUrl('pay_abc123');
|
|
328
|
+
// "https://coinpayportal.com/api/payments/pay_abc123/qr"
|
|
329
|
+
|
|
330
|
+
// Use in HTML:
|
|
331
|
+
// <img src={url} alt="Payment QR Code" />
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
#### `client.getPaymentQR(paymentId)`
|
|
335
|
+
|
|
336
|
+
Fetches the QR code as binary PNG data.
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
import fs from 'fs';
|
|
340
|
+
|
|
341
|
+
const imageData = await client.getPaymentQR('pay_abc123');
|
|
342
|
+
fs.writeFileSync('payment-qr.png', Buffer.from(imageData));
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
105
347
|
### Exchange Rates
|
|
106
348
|
|
|
349
|
+
#### `client.getExchangeRate(crypto, fiat?)`
|
|
350
|
+
|
|
351
|
+
Get the exchange rate for a single cryptocurrency.
|
|
352
|
+
|
|
107
353
|
```javascript
|
|
108
|
-
// Get single rate
|
|
109
354
|
const rate = await client.getExchangeRate('BTC', 'USD');
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### `client.getExchangeRates(cryptos, fiat?)`
|
|
358
|
+
|
|
359
|
+
Get rates for multiple cryptocurrencies in one request.
|
|
110
360
|
|
|
111
|
-
|
|
361
|
+
```javascript
|
|
112
362
|
const rates = await client.getExchangeRates(['BTC', 'ETH', 'SOL'], 'USD');
|
|
113
363
|
```
|
|
114
364
|
|
|
115
|
-
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
### Business Management
|
|
368
|
+
|
|
369
|
+
#### `client.createBusiness(params)`
|
|
116
370
|
|
|
117
371
|
```javascript
|
|
118
|
-
|
|
119
|
-
const business = await client.createBusiness({
|
|
372
|
+
const result = await client.createBusiness({
|
|
120
373
|
name: 'My Store',
|
|
121
|
-
webhookUrl: 'https://
|
|
374
|
+
webhookUrl: 'https://mystore.com/webhook',
|
|
122
375
|
walletAddresses: {
|
|
123
376
|
BTC: 'bc1q...',
|
|
124
377
|
ETH: '0x...',
|
|
378
|
+
SOL: '...',
|
|
125
379
|
},
|
|
126
380
|
});
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### `client.getBusiness(businessId)`
|
|
384
|
+
|
|
385
|
+
```javascript
|
|
386
|
+
const result = await client.getBusiness('biz_123');
|
|
387
|
+
```
|
|
127
388
|
|
|
128
|
-
|
|
129
|
-
|
|
389
|
+
#### `client.listBusinesses()`
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
const result = await client.listBusinesses();
|
|
393
|
+
```
|
|
130
394
|
|
|
131
|
-
|
|
132
|
-
const businesses = await client.listBusinesses();
|
|
395
|
+
#### `client.updateBusiness(businessId, params)`
|
|
133
396
|
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
name: 'Updated Name',
|
|
397
|
+
```javascript
|
|
398
|
+
const result = await client.updateBusiness('biz_123', {
|
|
399
|
+
name: 'Updated Store Name',
|
|
400
|
+
webhookUrl: 'https://mystore.com/webhook/v2',
|
|
137
401
|
});
|
|
138
402
|
```
|
|
139
403
|
|
|
404
|
+
---
|
|
405
|
+
|
|
140
406
|
### Webhooks
|
|
141
407
|
|
|
408
|
+
#### `client.getWebhookLogs(businessId, limit?)`
|
|
409
|
+
|
|
410
|
+
Retrieve recent webhook delivery logs.
|
|
411
|
+
|
|
142
412
|
```javascript
|
|
143
|
-
// Get webhook logs
|
|
144
413
|
const logs = await client.getWebhookLogs('biz_123', 50);
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
#### `client.testWebhook(businessId, eventType?)`
|
|
417
|
+
|
|
418
|
+
Send a test webhook event to your configured endpoint.
|
|
145
419
|
|
|
146
|
-
|
|
147
|
-
|
|
420
|
+
```javascript
|
|
421
|
+
await client.testWebhook('biz_123', 'payment.completed');
|
|
148
422
|
```
|
|
149
423
|
|
|
150
|
-
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
### Standalone Functions
|
|
427
|
+
|
|
428
|
+
Convenience functions that auto-create a client. Best for one-off operations.
|
|
151
429
|
|
|
152
430
|
```javascript
|
|
153
|
-
import {
|
|
431
|
+
import { createPayment, getPayment, listPayments } from '@profullstack/coinpay';
|
|
154
432
|
|
|
155
|
-
//
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
433
|
+
// Create payment without instantiating a client
|
|
434
|
+
const result = await createPayment({
|
|
435
|
+
apiKey: 'cp_live_xxxxx',
|
|
436
|
+
businessId: 'biz_123',
|
|
437
|
+
amount: 50,
|
|
438
|
+
blockchain: 'BTC',
|
|
160
439
|
});
|
|
161
440
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
441
|
+
// Or pass an existing client
|
|
442
|
+
const result2 = await createPayment({
|
|
443
|
+
client: existingClient,
|
|
444
|
+
businessId: 'biz_123',
|
|
445
|
+
amount: 50,
|
|
446
|
+
blockchain: 'BTC',
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// Get payment
|
|
450
|
+
const payment = await getPayment({
|
|
451
|
+
apiKey: 'cp_live_xxxxx',
|
|
452
|
+
paymentId: 'pay_abc123',
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// List payments
|
|
456
|
+
const list = await listPayments({
|
|
457
|
+
apiKey: 'cp_live_xxxxx',
|
|
458
|
+
businessId: 'biz_123',
|
|
459
|
+
status: 'completed',
|
|
460
|
+
limit: 10,
|
|
461
|
+
});
|
|
181
462
|
```
|
|
182
463
|
|
|
183
|
-
|
|
464
|
+
---
|
|
184
465
|
|
|
185
|
-
|
|
186
|
-
|-------|-------------|
|
|
187
|
-
| `payment.created` | Payment was created |
|
|
188
|
-
| `payment.pending` | Payment is pending confirmation |
|
|
189
|
-
| `payment.confirming` | Payment is being confirmed |
|
|
190
|
-
| `payment.completed` | Payment completed successfully |
|
|
191
|
-
| `payment.expired` | Payment expired |
|
|
192
|
-
| `payment.failed` | Payment failed |
|
|
193
|
-
| `payment.refunded` | Payment was refunded |
|
|
466
|
+
### Constants
|
|
194
467
|
|
|
195
|
-
|
|
468
|
+
```javascript
|
|
469
|
+
import {
|
|
470
|
+
Blockchain,
|
|
471
|
+
PaymentStatus,
|
|
472
|
+
FiatCurrency,
|
|
473
|
+
WebhookEvent,
|
|
474
|
+
} from '@profullstack/coinpay';
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
#### `Blockchain`
|
|
478
|
+
|
|
479
|
+
| Key | Value | Description |
|
|
480
|
+
|-----|-------|-------------|
|
|
481
|
+
| `BTC` | `'BTC'` | Bitcoin |
|
|
482
|
+
| `BCH` | `'BCH'` | Bitcoin Cash |
|
|
483
|
+
| `ETH` | `'ETH'` | Ethereum |
|
|
484
|
+
| `POL` | `'POL'` | Polygon |
|
|
485
|
+
| `SOL` | `'SOL'` | Solana |
|
|
486
|
+
| `USDC_ETH` | `'USDC_ETH'` | USDC on Ethereum |
|
|
487
|
+
| `USDC_POL` | `'USDC_POL'` | USDC on Polygon |
|
|
488
|
+
| `USDC_SOL` | `'USDC_SOL'` | USDC on Solana |
|
|
489
|
+
|
|
490
|
+
> `Cryptocurrency` is exported as a **deprecated** alias for `Blockchain`.
|
|
491
|
+
|
|
492
|
+
#### `PaymentStatus`
|
|
493
|
+
|
|
494
|
+
| Key | Value |
|
|
495
|
+
|-----|-------|
|
|
496
|
+
| `PENDING` | `'pending'` |
|
|
497
|
+
| `CONFIRMING` | `'confirming'` |
|
|
498
|
+
| `COMPLETED` | `'completed'` |
|
|
499
|
+
| `EXPIRED` | `'expired'` |
|
|
500
|
+
| `FAILED` | `'failed'` |
|
|
501
|
+
| `REFUNDED` | `'refunded'` |
|
|
502
|
+
|
|
503
|
+
#### `FiatCurrency`
|
|
504
|
+
|
|
505
|
+
| Key | Value |
|
|
506
|
+
|-----|-------|
|
|
507
|
+
| `USD` | `'USD'` |
|
|
508
|
+
| `EUR` | `'EUR'` |
|
|
509
|
+
| `GBP` | `'GBP'` |
|
|
510
|
+
| `CAD` | `'CAD'` |
|
|
511
|
+
| `AUD` | `'AUD'` |
|
|
512
|
+
|
|
513
|
+
#### `WebhookEvent`
|
|
514
|
+
|
|
515
|
+
| Key | Value |
|
|
516
|
+
|-----|-------|
|
|
517
|
+
| `PAYMENT_CREATED` | `'payment.created'` |
|
|
518
|
+
| `PAYMENT_PENDING` | `'payment.pending'` |
|
|
519
|
+
| `PAYMENT_CONFIRMING` | `'payment.confirming'` |
|
|
520
|
+
| `PAYMENT_COMPLETED` | `'payment.completed'` |
|
|
521
|
+
| `PAYMENT_EXPIRED` | `'payment.expired'` |
|
|
522
|
+
| `PAYMENT_FAILED` | `'payment.failed'` |
|
|
523
|
+
| `PAYMENT_REFUNDED` | `'payment.refunded'` |
|
|
524
|
+
| `BUSINESS_CREATED` | `'business.created'` |
|
|
525
|
+
| `BUSINESS_UPDATED` | `'business.updated'` |
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## CLI Reference
|
|
530
|
+
|
|
531
|
+
### Installation
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
# Global
|
|
535
|
+
pnpm add -g @profullstack/coinpay
|
|
536
|
+
|
|
537
|
+
# Or use npx
|
|
538
|
+
npx @profullstack/coinpay --help
|
|
539
|
+
```
|
|
196
540
|
|
|
197
541
|
### Configuration
|
|
198
542
|
|
|
199
543
|
```bash
|
|
200
|
-
coinpay config set-key
|
|
201
|
-
coinpay config set-url
|
|
202
|
-
coinpay config show
|
|
544
|
+
coinpay config set-key cp_live_xxxxx # Save API key
|
|
545
|
+
coinpay config set-url http://localhost:3000/api # Custom URL
|
|
546
|
+
coinpay config show # Display config
|
|
203
547
|
```
|
|
204
548
|
|
|
205
549
|
### Payments
|
|
206
550
|
|
|
207
551
|
```bash
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
--
|
|
211
|
-
--
|
|
212
|
-
--
|
|
213
|
-
--description
|
|
552
|
+
# Create a payment
|
|
553
|
+
coinpay payment create \
|
|
554
|
+
--business-id biz_123 \
|
|
555
|
+
--amount 100 \
|
|
556
|
+
--blockchain BTC \
|
|
557
|
+
--description "Order #12345"
|
|
214
558
|
|
|
215
|
-
|
|
216
|
-
coinpay payment
|
|
217
|
-
|
|
559
|
+
# Get payment details
|
|
560
|
+
coinpay payment get pay_abc123
|
|
561
|
+
|
|
562
|
+
# List payments
|
|
563
|
+
coinpay payment list --business-id biz_123 --status pending --limit 10
|
|
564
|
+
|
|
565
|
+
# Get QR code
|
|
566
|
+
coinpay payment qr pay_abc123
|
|
218
567
|
```
|
|
219
568
|
|
|
220
569
|
### Businesses
|
|
221
570
|
|
|
222
571
|
```bash
|
|
223
|
-
coinpay business create --name <name> [--webhook-url <url>]
|
|
224
|
-
coinpay business get <business-id>
|
|
225
572
|
coinpay business list
|
|
226
|
-
coinpay business
|
|
573
|
+
coinpay business get biz_123
|
|
574
|
+
coinpay business create --name "My Store" --webhook-url https://mysite.com/webhook
|
|
575
|
+
coinpay business update biz_123 --name "New Name"
|
|
227
576
|
```
|
|
228
577
|
|
|
229
578
|
### Exchange Rates
|
|
230
579
|
|
|
231
580
|
```bash
|
|
232
|
-
coinpay rates get
|
|
233
|
-
coinpay rates list
|
|
581
|
+
coinpay rates get BTC
|
|
582
|
+
coinpay rates list
|
|
234
583
|
```
|
|
235
584
|
|
|
236
585
|
### Webhooks
|
|
237
586
|
|
|
238
587
|
```bash
|
|
239
|
-
coinpay webhook logs
|
|
240
|
-
coinpay webhook test
|
|
588
|
+
coinpay webhook logs biz_123
|
|
589
|
+
coinpay webhook test biz_123 --event payment.completed
|
|
241
590
|
```
|
|
242
591
|
|
|
243
|
-
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
## Webhook Integration
|
|
595
|
+
|
|
596
|
+
### Webhook Payload
|
|
597
|
+
|
|
598
|
+
When a payment status changes, CoinPay sends a `POST` request to your webhook URL:
|
|
599
|
+
|
|
600
|
+
```json
|
|
601
|
+
{
|
|
602
|
+
"id": "evt_abc123",
|
|
603
|
+
"type": "payment.completed",
|
|
604
|
+
"created_at": "2024-01-01T00:15:00.000Z",
|
|
605
|
+
"business_id": "biz_123",
|
|
606
|
+
"data": {
|
|
607
|
+
"payment": {
|
|
608
|
+
"id": "pay_abc123",
|
|
609
|
+
"status": "confirmed",
|
|
610
|
+
"amount": 100.00,
|
|
611
|
+
"currency": "USD",
|
|
612
|
+
"crypto_amount": "0.0456",
|
|
613
|
+
"blockchain": "ETH",
|
|
614
|
+
"tx_hash": "0xabc...def",
|
|
615
|
+
"metadata": { "orderId": "12345" }
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
```
|
|
244
620
|
|
|
245
|
-
|
|
246
|
-
- **BCH** - Bitcoin Cash
|
|
247
|
-
- **ETH** - Ethereum
|
|
248
|
-
- **MATIC** - Polygon
|
|
249
|
-
- **SOL** - Solana
|
|
621
|
+
### Signature Verification
|
|
250
622
|
|
|
251
|
-
|
|
623
|
+
Every webhook includes an `X-CoinPay-Signature` header in the format `t=<timestamp>,v1=<hmac-sha256>`. Always verify signatures before processing events.
|
|
252
624
|
|
|
253
|
-
|
|
625
|
+
#### Using the Middleware (Express)
|
|
254
626
|
|
|
255
|
-
|
|
627
|
+
```javascript
|
|
628
|
+
import express from 'express';
|
|
629
|
+
import { createWebhookHandler, WebhookEvent } from '@profullstack/coinpay';
|
|
630
|
+
|
|
631
|
+
const app = express();
|
|
632
|
+
|
|
633
|
+
app.post('/webhook',
|
|
634
|
+
express.text({ type: 'application/json' }),
|
|
635
|
+
createWebhookHandler({
|
|
636
|
+
secret: process.env.COINPAY_WEBHOOK_SECRET,
|
|
637
|
+
onEvent: async (event) => {
|
|
638
|
+
switch (event.type) {
|
|
639
|
+
case WebhookEvent.PAYMENT_COMPLETED:
|
|
640
|
+
await fulfillOrder(event.data.payment.metadata.orderId);
|
|
641
|
+
break;
|
|
642
|
+
case WebhookEvent.PAYMENT_EXPIRED:
|
|
643
|
+
await cancelOrder(event.data.payment.metadata.orderId);
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
onError: (error) => {
|
|
648
|
+
console.error('Webhook error:', error);
|
|
649
|
+
},
|
|
650
|
+
})
|
|
651
|
+
);
|
|
652
|
+
```
|
|
256
653
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
654
|
+
#### Manual Verification
|
|
655
|
+
|
|
656
|
+
```javascript
|
|
657
|
+
import { verifyWebhookSignature, parseWebhookPayload } from '@profullstack/coinpay';
|
|
658
|
+
|
|
659
|
+
const isValid = verifyWebhookSignature({
|
|
660
|
+
payload: rawBody, // Raw request body string
|
|
661
|
+
signature: req.headers['x-coinpay-signature'], // Signature header
|
|
662
|
+
secret: process.env.COINPAY_WEBHOOK_SECRET, // Your secret
|
|
663
|
+
tolerance: 300, // Optional: seconds (default: 300)
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
if (isValid) {
|
|
667
|
+
const event = parseWebhookPayload(rawBody);
|
|
668
|
+
// event.id, event.type, event.data, event.createdAt, event.businessId
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
#### Generating Test Signatures
|
|
673
|
+
|
|
674
|
+
```javascript
|
|
675
|
+
import { generateWebhookSignature } from '@profullstack/coinpay';
|
|
676
|
+
|
|
677
|
+
const signature = generateWebhookSignature({
|
|
678
|
+
payload: JSON.stringify(testEvent),
|
|
679
|
+
secret: 'whsec_test_secret',
|
|
680
|
+
timestamp: Math.floor(Date.now() / 1000), // Optional
|
|
681
|
+
});
|
|
682
|
+
// "t=1705312500,v1=a3f2b1..."
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### Webhook Events
|
|
686
|
+
|
|
687
|
+
| Event | When |
|
|
688
|
+
|-------|------|
|
|
689
|
+
| `payment.created` | Payment request created |
|
|
690
|
+
| `payment.pending` | Awaiting blockchain detection |
|
|
691
|
+
| `payment.confirming` | Transaction detected, awaiting confirmations |
|
|
692
|
+
| `payment.completed` | **Payment confirmed — safe to fulfill** |
|
|
693
|
+
| `payment.expired` | Customer didn't pay in time |
|
|
694
|
+
| `payment.failed` | Payment failed |
|
|
695
|
+
| `payment.refunded` | Payment was refunded |
|
|
696
|
+
| `business.created` | New business created |
|
|
697
|
+
| `business.updated` | Business settings updated |
|
|
698
|
+
|
|
699
|
+
---
|
|
261
700
|
|
|
262
701
|
## Error Handling
|
|
263
702
|
|
|
703
|
+
All API errors include a `status` code and optional `response` object:
|
|
704
|
+
|
|
264
705
|
```javascript
|
|
265
706
|
try {
|
|
266
707
|
const payment = await client.createPayment({ ... });
|
|
267
708
|
} catch (error) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
709
|
+
console.log(error.message); // Human-readable message
|
|
710
|
+
console.log(error.status); // HTTP status code (401, 400, 429, etc.)
|
|
711
|
+
console.log(error.response); // Full error response from the API
|
|
712
|
+
|
|
713
|
+
switch (error.status) {
|
|
714
|
+
case 400:
|
|
715
|
+
// Invalid request — check parameters
|
|
716
|
+
break;
|
|
717
|
+
case 401:
|
|
718
|
+
// Invalid API key
|
|
719
|
+
break;
|
|
720
|
+
case 404:
|
|
721
|
+
// Resource not found
|
|
722
|
+
break;
|
|
723
|
+
case 429:
|
|
724
|
+
// Rate limit or transaction limit exceeded
|
|
725
|
+
console.log('Usage:', error.response?.usage);
|
|
726
|
+
break;
|
|
274
727
|
}
|
|
275
728
|
}
|
|
276
729
|
```
|
|
277
730
|
|
|
731
|
+
**Timeout errors** throw a standard `Error` with message `"Request timeout after {ms}ms"`.
|
|
732
|
+
|
|
733
|
+
**Constructor errors** throw if `apiKey` is missing: `"API key is required"`.
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
## Integration Patterns
|
|
738
|
+
|
|
739
|
+
### E-commerce Checkout
|
|
740
|
+
|
|
741
|
+
```javascript
|
|
742
|
+
import { CoinPayClient, createWebhookHandler, WebhookEvent } from '@profullstack/coinpay';
|
|
743
|
+
|
|
744
|
+
const client = new CoinPayClient({ apiKey: process.env.COINPAY_API_KEY });
|
|
745
|
+
|
|
746
|
+
// Checkout endpoint
|
|
747
|
+
app.post('/checkout', async (req, res) => {
|
|
748
|
+
const { orderId, amount, blockchain } = req.body;
|
|
749
|
+
|
|
750
|
+
const { payment } = await client.createPayment({
|
|
751
|
+
businessId: process.env.COINPAY_BUSINESS_ID,
|
|
752
|
+
amount,
|
|
753
|
+
blockchain,
|
|
754
|
+
description: `Order #${orderId}`,
|
|
755
|
+
metadata: { orderId },
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
await db.orders.update(orderId, {
|
|
759
|
+
paymentId: payment.id,
|
|
760
|
+
paymentAddress: payment.payment_address,
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
res.json({
|
|
764
|
+
paymentAddress: payment.payment_address,
|
|
765
|
+
cryptoAmount: payment.crypto_amount,
|
|
766
|
+
qrCode: payment.qr_code,
|
|
767
|
+
expiresAt: payment.expires_at,
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// Webhook
|
|
772
|
+
app.post('/webhook', express.text({ type: 'application/json' }),
|
|
773
|
+
createWebhookHandler({
|
|
774
|
+
secret: process.env.COINPAY_WEBHOOK_SECRET,
|
|
775
|
+
onEvent: async (event) => {
|
|
776
|
+
if (event.type === WebhookEvent.PAYMENT_COMPLETED) {
|
|
777
|
+
const { orderId } = event.data.payment.metadata;
|
|
778
|
+
await db.orders.update(orderId, { status: 'paid' });
|
|
779
|
+
await sendConfirmationEmail(orderId);
|
|
780
|
+
}
|
|
781
|
+
},
|
|
782
|
+
})
|
|
783
|
+
);
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
### Stablecoin Subscriptions
|
|
787
|
+
|
|
788
|
+
Use USDC for predictable pricing — no volatility:
|
|
789
|
+
|
|
790
|
+
```javascript
|
|
791
|
+
const { payment } = await client.createPayment({
|
|
792
|
+
businessId: BUSINESS_ID,
|
|
793
|
+
amount: 9.99,
|
|
794
|
+
blockchain: Blockchain.USDC_POL, // USDC on Polygon — low fees
|
|
795
|
+
description: 'Monthly subscription',
|
|
796
|
+
metadata: { userId: user.id, period: '2024-01' },
|
|
797
|
+
});
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
### Direct API (fetch / cURL)
|
|
801
|
+
|
|
802
|
+
```bash
|
|
803
|
+
# Create payment
|
|
804
|
+
curl -X POST https://coinpayportal.com/api/payments/create \
|
|
805
|
+
-H "Authorization: Bearer cp_live_your_api_key" \
|
|
806
|
+
-H "Content-Type: application/json" \
|
|
807
|
+
-d '{
|
|
808
|
+
"business_id": "your-business-id",
|
|
809
|
+
"amount": 50.00,
|
|
810
|
+
"currency": "USD",
|
|
811
|
+
"blockchain": "ETH"
|
|
812
|
+
}'
|
|
813
|
+
|
|
814
|
+
# Check payment status
|
|
815
|
+
curl https://coinpayportal.com/api/payments/pay_abc123 \
|
|
816
|
+
-H "Authorization: Bearer cp_live_your_api_key"
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
## TypeScript
|
|
822
|
+
|
|
823
|
+
Full TypeScript support via `.d.ts` declaration files — no build step required.
|
|
824
|
+
|
|
825
|
+
```typescript
|
|
826
|
+
import {
|
|
827
|
+
CoinPayClient,
|
|
828
|
+
Blockchain,
|
|
829
|
+
PaymentStatus,
|
|
830
|
+
WebhookEvent,
|
|
831
|
+
} from '@profullstack/coinpay';
|
|
832
|
+
|
|
833
|
+
import type {
|
|
834
|
+
CoinPayClientOptions,
|
|
835
|
+
PaymentParams,
|
|
836
|
+
Payment,
|
|
837
|
+
CreatePaymentResponse,
|
|
838
|
+
WaitForPaymentOptions,
|
|
839
|
+
VerifyWebhookParams,
|
|
840
|
+
ParsedWebhookEvent,
|
|
841
|
+
} from '@profullstack/coinpay';
|
|
842
|
+
|
|
843
|
+
const client = new CoinPayClient({ apiKey: 'cp_live_xxxxx' });
|
|
844
|
+
|
|
845
|
+
const { payment }: CreatePaymentResponse = await client.createPayment({
|
|
846
|
+
businessId: 'biz_123',
|
|
847
|
+
amount: 100,
|
|
848
|
+
blockchain: Blockchain.ETH,
|
|
849
|
+
});
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
### Subpath Imports
|
|
853
|
+
|
|
854
|
+
```typescript
|
|
855
|
+
// Import only what you need
|
|
856
|
+
import { Blockchain, PaymentStatus } from '@profullstack/coinpay/payments';
|
|
857
|
+
import { verifyWebhookSignature, WebhookEvent } from '@profullstack/coinpay/webhooks';
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
---
|
|
861
|
+
|
|
862
|
+
## Environment Variables
|
|
863
|
+
|
|
864
|
+
| Variable | Description |
|
|
865
|
+
|----------|-------------|
|
|
866
|
+
| `COINPAY_API_KEY` | API key (overrides config file in CLI) |
|
|
867
|
+
| `COINPAY_BASE_URL` | Custom API URL (for development) |
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
## Examples
|
|
872
|
+
|
|
873
|
+
See the [`examples/`](./examples/) directory for runnable code:
|
|
874
|
+
|
|
875
|
+
| Example | Description |
|
|
876
|
+
|---------|-------------|
|
|
877
|
+
| [`01-quick-start.js`](./examples/01-quick-start.js) | Create a payment and check status |
|
|
878
|
+
| [`02-create-payment.js`](./examples/02-create-payment.js) | All blockchain types, metadata, multi-currency |
|
|
879
|
+
| [`03-check-payment-status.js`](./examples/03-check-payment-status.js) | One-time check and `waitForPayment` polling |
|
|
880
|
+
| [`04-list-payments.js`](./examples/04-list-payments.js) | Filtering and pagination |
|
|
881
|
+
| [`05-exchange-rates.js`](./examples/05-exchange-rates.js) | Single and batch rate lookups |
|
|
882
|
+
| [`06-webhook-handler.js`](./examples/06-webhook-handler.js) | Express webhook server |
|
|
883
|
+
| [`07-ecommerce-checkout.js`](./examples/07-ecommerce-checkout.js) | Complete checkout → webhook → fulfillment flow |
|
|
884
|
+
| [`08-business-management.js`](./examples/08-business-management.js) | Create, list, and update businesses |
|
|
885
|
+
| [`09-error-handling.js`](./examples/09-error-handling.js) | Auth, validation, rate-limit, and timeout errors |
|
|
886
|
+
|
|
887
|
+
```bash
|
|
888
|
+
COINPAY_API_KEY=cp_live_xxx COINPAY_BUSINESS_ID=biz_xxx node examples/01-quick-start.js
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
## Testing
|
|
894
|
+
|
|
895
|
+
```bash
|
|
896
|
+
# Run tests
|
|
897
|
+
pnpm test
|
|
898
|
+
|
|
899
|
+
# Watch mode
|
|
900
|
+
pnpm test:watch
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
Tests use [Vitest](https://vitest.dev/) with mocked `fetch` — no API key needed.
|
|
904
|
+
|
|
905
|
+
---
|
|
906
|
+
|
|
907
|
+
## Support
|
|
908
|
+
|
|
909
|
+
- **Docs:** [docs.coinpayportal.com](https://docs.coinpayportal.com)
|
|
910
|
+
- **Dashboard:** [coinpayportal.com](https://coinpayportal.com)
|
|
911
|
+
- **Email:** support@coinpayportal.com
|
|
912
|
+
- **Issues:** [github.com/profullstack/coinpayportal/issues](https://github.com/profullstack/coinpayportal/issues)
|
|
913
|
+
|
|
914
|
+
---
|
|
915
|
+
|
|
278
916
|
## License
|
|
279
917
|
|
|
280
|
-
MIT
|
|
918
|
+
[MIT](./LICENSE) © Profullstack, Inc.
|