@profullstack/coinpay 0.2.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 +686 -323
- package/bin/coinpay.js +2 -2
- package/package.json +29 -12
- package/src/client.d.ts +266 -0
- package/src/client.js +1 -1
- package/src/index.d.ts +37 -0
- package/src/payments.d.ts +153 -0
- package/src/payments.js +4 -4
- package/src/webhooks.d.ts +134 -0
package/README.md
CHANGED
|
@@ -1,142 +1,221 @@
|
|
|
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
|
|
4
42
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
+
```
|
|
8
58
|
|
|
9
59
|
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. **
|
|
60
|
+
2. **CoinPay** generates a unique payment address and QR code — display these to your customer
|
|
61
|
+
3. **Customer** sends cryptocurrency to the address
|
|
12
62
|
4. **CoinPay** monitors the blockchain and notifies you via webhook when payment is confirmed
|
|
13
|
-
5. **Funds** are automatically forwarded to your configured wallet
|
|
63
|
+
5. **Funds** are automatically forwarded to your configured wallet
|
|
64
|
+
|
|
65
|
+
---
|
|
14
66
|
|
|
15
67
|
## Installation
|
|
16
68
|
|
|
17
69
|
```bash
|
|
18
|
-
#
|
|
70
|
+
# pnpm (recommended)
|
|
19
71
|
pnpm add @profullstack/coinpay
|
|
20
72
|
|
|
21
|
-
#
|
|
73
|
+
# npm
|
|
22
74
|
npm install @profullstack/coinpay
|
|
23
75
|
|
|
24
|
-
# Global CLI
|
|
76
|
+
# Global CLI
|
|
25
77
|
pnpm add -g @profullstack/coinpay
|
|
26
78
|
```
|
|
27
79
|
|
|
80
|
+
**Requirements:** Node.js ≥ 20. Zero runtime dependencies — uses built-in `fetch` and `crypto`.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
28
84
|
## Quick Start
|
|
29
85
|
|
|
30
86
|
### 1. Get Your API Key
|
|
31
87
|
|
|
32
88
|
1. Sign up at [coinpayportal.com](https://coinpayportal.com)
|
|
33
89
|
2. Create a business in your dashboard
|
|
34
|
-
3. Configure
|
|
90
|
+
3. Configure wallet addresses for each crypto you want to accept
|
|
35
91
|
4. Copy your API key (starts with `cp_live_`)
|
|
36
92
|
|
|
37
|
-
### 2. Create a Payment
|
|
93
|
+
### 2. Create a Payment
|
|
38
94
|
|
|
39
95
|
```javascript
|
|
40
|
-
import { CoinPayClient } from '@profullstack/coinpay';
|
|
96
|
+
import { CoinPayClient, Blockchain } from '@profullstack/coinpay';
|
|
41
97
|
|
|
42
|
-
|
|
43
|
-
const coinpay = new CoinPayClient({
|
|
98
|
+
const client = new CoinPayClient({
|
|
44
99
|
apiKey: 'cp_live_your_api_key_here',
|
|
45
100
|
});
|
|
46
101
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
metadata: { // Your custom data
|
|
55
|
-
orderId: '12345',
|
|
56
|
-
customerEmail: 'customer@example.com'
|
|
57
|
-
}
|
|
102
|
+
const { payment } = await client.createPayment({
|
|
103
|
+
businessId: 'your-business-id',
|
|
104
|
+
amount: 99.99,
|
|
105
|
+
currency: 'USD',
|
|
106
|
+
blockchain: Blockchain.BTC,
|
|
107
|
+
description: 'Order #12345',
|
|
108
|
+
metadata: { orderId: '12345' },
|
|
58
109
|
});
|
|
59
110
|
|
|
60
|
-
|
|
61
|
-
console.log('
|
|
62
|
-
console.log('
|
|
63
|
-
console.log('QR Code:', payment.payment.qr_code);
|
|
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);
|
|
64
114
|
```
|
|
65
115
|
|
|
66
|
-
### 3.
|
|
116
|
+
### 3. Handle Payment Confirmation
|
|
67
117
|
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"description": "Order #12345",
|
|
78
|
-
"metadata": {
|
|
79
|
-
"orderId": "12345",
|
|
80
|
-
"customerEmail": "customer@example.com"
|
|
118
|
+
```javascript
|
|
119
|
+
import { createWebhookHandler, WebhookEvent } from '@profullstack/coinpay';
|
|
120
|
+
|
|
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);
|
|
81
127
|
}
|
|
82
|
-
}
|
|
128
|
+
},
|
|
129
|
+
}));
|
|
83
130
|
```
|
|
84
131
|
|
|
85
|
-
|
|
132
|
+
---
|
|
86
133
|
|
|
87
|
-
|
|
88
|
-
# Configure your API key (one-time setup)
|
|
89
|
-
coinpay config set-key cp_live_your_api_key_here
|
|
134
|
+
## Supported Blockchains
|
|
90
135
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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 |
|
|
98
146
|
|
|
99
|
-
|
|
147
|
+
Use the `Blockchain` constant to avoid typos:
|
|
100
148
|
|
|
101
|
-
|
|
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 |
|
|
149
|
+
```javascript
|
|
150
|
+
import { Blockchain } from '@profullstack/coinpay';
|
|
111
151
|
|
|
112
|
-
|
|
152
|
+
Blockchain.BTC // 'BTC'
|
|
153
|
+
Blockchain.ETH // 'ETH'
|
|
154
|
+
Blockchain.USDC_POL // 'USDC_POL'
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## API Reference
|
|
113
160
|
|
|
114
161
|
### CoinPayClient
|
|
115
162
|
|
|
163
|
+
The main class for all API operations.
|
|
164
|
+
|
|
116
165
|
```javascript
|
|
117
166
|
import { CoinPayClient } from '@profullstack/coinpay';
|
|
118
167
|
|
|
119
168
|
const client = new CoinPayClient({
|
|
120
|
-
apiKey: 'cp_live_xxxxx',
|
|
121
|
-
baseUrl: 'https://coinpayportal.com/api',
|
|
122
|
-
timeout: 30000,
|
|
169
|
+
apiKey: 'cp_live_xxxxx', // Required
|
|
170
|
+
baseUrl: 'https://coinpayportal.com/api', // Optional (default)
|
|
171
|
+
timeout: 30000, // Optional: ms (default: 30s)
|
|
123
172
|
});
|
|
124
173
|
```
|
|
125
174
|
|
|
126
|
-
|
|
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
|
+
|
|
185
|
+
### Payments
|
|
186
|
+
|
|
187
|
+
#### `client.createPayment(params)`
|
|
188
|
+
|
|
189
|
+
Create a new payment request. Generates a unique blockchain address for the customer to pay.
|
|
127
190
|
|
|
128
191
|
```javascript
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
},
|
|
137
202
|
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Parameters:**
|
|
206
|
+
|
|
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 |
|
|
138
215
|
|
|
139
|
-
|
|
216
|
+
**Returns:**
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
140
219
|
{
|
|
141
220
|
success: true,
|
|
142
221
|
payment: {
|
|
@@ -150,7 +229,8 @@ const payment = await client.createPayment({
|
|
|
150
229
|
qr_code: 'data:image/png;base64,...',
|
|
151
230
|
status: 'pending',
|
|
152
231
|
expires_at: '2024-01-01T01:00:00.000Z',
|
|
153
|
-
created_at: '2024-01-01T00:00:00.000Z'
|
|
232
|
+
created_at: '2024-01-01T00:00:00.000Z',
|
|
233
|
+
metadata: { orderId: '123' }
|
|
154
234
|
},
|
|
155
235
|
usage: {
|
|
156
236
|
current: 45,
|
|
@@ -160,250 +240,310 @@ const payment = await client.createPayment({
|
|
|
160
240
|
}
|
|
161
241
|
```
|
|
162
242
|
|
|
163
|
-
|
|
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
|
+
---
|
|
164
258
|
|
|
165
|
-
|
|
259
|
+
#### `client.listPayments(params)`
|
|
166
260
|
|
|
167
|
-
|
|
261
|
+
List payments for a business with optional filtering and pagination.
|
|
168
262
|
|
|
169
|
-
|
|
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.
|
|
170
279
|
|
|
171
280
|
```javascript
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
|
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'],
|
|
180
285
|
onStatusChange: (status, payment) => {
|
|
181
|
-
console.log(`Status
|
|
182
|
-
}
|
|
286
|
+
console.log(`Status → ${status}`);
|
|
287
|
+
},
|
|
183
288
|
});
|
|
184
289
|
|
|
185
|
-
if (payment.
|
|
290
|
+
if (payment.status === 'confirmed' || payment.status === 'forwarded') {
|
|
186
291
|
console.log('Payment successful!');
|
|
187
|
-
} else {
|
|
188
|
-
console.log('Payment failed or expired');
|
|
189
292
|
}
|
|
190
293
|
```
|
|
191
294
|
|
|
192
|
-
|
|
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` |
|
|
193
301
|
|
|
194
|
-
|
|
302
|
+
> ⚠️ For production, use [webhooks](#webhook-integration) instead of polling.
|
|
195
303
|
|
|
196
|
-
|
|
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
|
-
```
|
|
304
|
+
---
|
|
209
305
|
|
|
210
|
-
|
|
306
|
+
### Payment Statuses
|
|
211
307
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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 |
|
|
220
317
|
|
|
221
|
-
|
|
318
|
+
---
|
|
222
319
|
|
|
223
|
-
|
|
320
|
+
### QR Codes
|
|
224
321
|
|
|
225
|
-
|
|
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"
|
|
322
|
+
#### `client.getPaymentQRUrl(paymentId)`
|
|
229
323
|
|
|
230
|
-
|
|
231
|
-
// <img src={qrUrl} alt="Payment QR Code" />
|
|
324
|
+
Returns the URL to the QR code image. **Synchronous** — no network request.
|
|
232
325
|
|
|
233
|
-
|
|
234
|
-
const
|
|
326
|
+
```javascript
|
|
327
|
+
const url = client.getPaymentQRUrl('pay_abc123');
|
|
328
|
+
// "https://coinpayportal.com/api/payments/pay_abc123/qr"
|
|
235
329
|
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
fs.writeFileSync('payment-qr.png', Buffer.from(imageData));
|
|
330
|
+
// Use in HTML:
|
|
331
|
+
// <img src={url} alt="Payment QR Code" />
|
|
239
332
|
```
|
|
240
333
|
|
|
241
|
-
|
|
334
|
+
#### `client.getPaymentQR(paymentId)`
|
|
335
|
+
|
|
336
|
+
Fetches the QR code as binary PNG data.
|
|
242
337
|
|
|
243
338
|
```javascript
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
offset: 0, // Optional pagination
|
|
249
|
-
});
|
|
339
|
+
import fs from 'fs';
|
|
340
|
+
|
|
341
|
+
const imageData = await client.getPaymentQR('pay_abc123');
|
|
342
|
+
fs.writeFileSync('payment-qr.png', Buffer.from(imageData));
|
|
250
343
|
```
|
|
251
344
|
|
|
345
|
+
---
|
|
346
|
+
|
|
252
347
|
### Exchange Rates
|
|
253
348
|
|
|
349
|
+
#### `client.getExchangeRate(crypto, fiat?)`
|
|
350
|
+
|
|
351
|
+
Get the exchange rate for a single cryptocurrency.
|
|
352
|
+
|
|
254
353
|
```javascript
|
|
255
|
-
// Get single rate
|
|
256
354
|
const rate = await client.getExchangeRate('BTC', 'USD');
|
|
257
|
-
|
|
355
|
+
```
|
|
258
356
|
|
|
259
|
-
|
|
357
|
+
#### `client.getExchangeRates(cryptos, fiat?)`
|
|
358
|
+
|
|
359
|
+
Get rates for multiple cryptocurrencies in one request.
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
260
362
|
const rates = await client.getExchangeRates(['BTC', 'ETH', 'SOL'], 'USD');
|
|
261
363
|
```
|
|
262
364
|
|
|
263
|
-
|
|
365
|
+
---
|
|
264
366
|
|
|
265
|
-
###
|
|
367
|
+
### Business Management
|
|
368
|
+
|
|
369
|
+
#### `client.createBusiness(params)`
|
|
266
370
|
|
|
267
371
|
```javascript
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
372
|
+
const result = await client.createBusiness({
|
|
373
|
+
name: 'My Store',
|
|
374
|
+
webhookUrl: 'https://mystore.com/webhook',
|
|
375
|
+
walletAddresses: {
|
|
376
|
+
BTC: 'bc1q...',
|
|
377
|
+
ETH: '0x...',
|
|
378
|
+
SOL: '...',
|
|
274
379
|
},
|
|
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
|
-
}),
|
|
286
380
|
});
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### `client.getBusiness(businessId)`
|
|
287
384
|
|
|
288
|
-
|
|
289
|
-
|
|
385
|
+
```javascript
|
|
386
|
+
const result = await client.getBusiness('biz_123');
|
|
290
387
|
```
|
|
291
388
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
}'
|
|
389
|
+
#### `client.listBusinesses()`
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
const result = await client.listBusinesses();
|
|
304
393
|
```
|
|
305
394
|
|
|
306
|
-
|
|
395
|
+
#### `client.updateBusiness(businessId, params)`
|
|
307
396
|
|
|
308
397
|
```javascript
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
},
|
|
398
|
+
const result = await client.updateBusiness('biz_123', {
|
|
399
|
+
name: 'Updated Store Name',
|
|
400
|
+
webhookUrl: 'https://mystore.com/webhook/v2',
|
|
313
401
|
});
|
|
314
|
-
const data = await response.json();
|
|
315
402
|
```
|
|
316
403
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
### Webhooks
|
|
407
|
+
|
|
408
|
+
#### `client.getWebhookLogs(businessId, limit?)`
|
|
409
|
+
|
|
410
|
+
Retrieve recent webhook delivery logs.
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
const logs = await client.getWebhookLogs('biz_123', 50);
|
|
320
414
|
```
|
|
321
415
|
|
|
322
|
-
|
|
416
|
+
#### `client.testWebhook(businessId, eventType?)`
|
|
323
417
|
|
|
324
|
-
|
|
418
|
+
Send a test webhook event to your configured endpoint.
|
|
325
419
|
|
|
326
420
|
```javascript
|
|
327
|
-
|
|
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
|
-
}
|
|
421
|
+
await client.testWebhook('biz_123', 'payment.completed');
|
|
348
422
|
```
|
|
349
423
|
|
|
350
|
-
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
### Standalone Functions
|
|
427
|
+
|
|
428
|
+
Convenience functions that auto-create a client. Best for one-off operations.
|
|
351
429
|
|
|
352
430
|
```javascript
|
|
353
|
-
import {
|
|
431
|
+
import { createPayment, getPayment, listPayments } from '@profullstack/coinpay';
|
|
354
432
|
|
|
355
|
-
//
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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',
|
|
360
439
|
});
|
|
361
440
|
|
|
362
|
-
//
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
+
});
|
|
380
462
|
```
|
|
381
463
|
|
|
382
|
-
|
|
464
|
+
---
|
|
383
465
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
466
|
+
### Constants
|
|
467
|
+
|
|
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
|
+
---
|
|
393
528
|
|
|
394
529
|
## CLI Reference
|
|
395
530
|
|
|
396
|
-
###
|
|
531
|
+
### Installation
|
|
397
532
|
|
|
398
533
|
```bash
|
|
399
|
-
#
|
|
400
|
-
|
|
534
|
+
# Global
|
|
535
|
+
pnpm add -g @profullstack/coinpay
|
|
401
536
|
|
|
402
|
-
#
|
|
403
|
-
coinpay
|
|
537
|
+
# Or use npx
|
|
538
|
+
npx @profullstack/coinpay --help
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Configuration
|
|
404
542
|
|
|
405
|
-
|
|
406
|
-
coinpay config
|
|
543
|
+
```bash
|
|
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
|
|
407
547
|
```
|
|
408
548
|
|
|
409
549
|
### Payments
|
|
@@ -422,134 +562,357 @@ coinpay payment get pay_abc123
|
|
|
422
562
|
# List payments
|
|
423
563
|
coinpay payment list --business-id biz_123 --status pending --limit 10
|
|
424
564
|
|
|
425
|
-
# Get QR code
|
|
426
|
-
coinpay payment qr pay_abc123
|
|
565
|
+
# Get QR code
|
|
566
|
+
coinpay payment qr pay_abc123
|
|
427
567
|
```
|
|
428
568
|
|
|
429
569
|
### Businesses
|
|
430
570
|
|
|
431
571
|
```bash
|
|
432
|
-
# List your businesses
|
|
433
572
|
coinpay business list
|
|
434
|
-
|
|
435
|
-
# Get business details
|
|
436
573
|
coinpay business get biz_123
|
|
437
|
-
|
|
438
|
-
# Create a business
|
|
439
574
|
coinpay business create --name "My Store" --webhook-url https://mysite.com/webhook
|
|
575
|
+
coinpay business update biz_123 --name "New Name"
|
|
440
576
|
```
|
|
441
577
|
|
|
442
578
|
### Exchange Rates
|
|
443
579
|
|
|
444
580
|
```bash
|
|
445
|
-
# Get rate for a cryptocurrency
|
|
446
581
|
coinpay rates get BTC
|
|
447
|
-
|
|
448
|
-
# Get all rates
|
|
449
582
|
coinpay rates list
|
|
450
583
|
```
|
|
451
584
|
|
|
452
|
-
|
|
585
|
+
### Webhooks
|
|
453
586
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
587
|
+
```bash
|
|
588
|
+
coinpay webhook logs biz_123
|
|
589
|
+
coinpay webhook test biz_123 --event payment.completed
|
|
590
|
+
```
|
|
591
|
+
|
|
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
|
+
```
|
|
620
|
+
|
|
621
|
+
### Signature Verification
|
|
622
|
+
|
|
623
|
+
Every webhook includes an `X-CoinPay-Signature` header in the format `t=<timestamp>,v1=<hmac-sha256>`. Always verify signatures before processing events.
|
|
624
|
+
|
|
625
|
+
#### Using the Middleware (Express)
|
|
626
|
+
|
|
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
|
+
```
|
|
653
|
+
|
|
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
|
+
---
|
|
458
700
|
|
|
459
701
|
## Error Handling
|
|
460
702
|
|
|
703
|
+
All API errors include a `status` code and optional `response` object:
|
|
704
|
+
|
|
461
705
|
```javascript
|
|
462
706
|
try {
|
|
463
707
|
const payment = await client.createPayment({ ... });
|
|
464
708
|
} catch (error) {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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;
|
|
474
727
|
}
|
|
475
728
|
}
|
|
476
729
|
```
|
|
477
730
|
|
|
478
|
-
|
|
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
|
|
479
738
|
|
|
480
739
|
### E-commerce Checkout
|
|
481
740
|
|
|
482
741
|
```javascript
|
|
483
|
-
|
|
742
|
+
import { CoinPayClient, createWebhookHandler, WebhookEvent } from '@profullstack/coinpay';
|
|
743
|
+
|
|
744
|
+
const client = new CoinPayClient({ apiKey: process.env.COINPAY_API_KEY });
|
|
745
|
+
|
|
746
|
+
// Checkout endpoint
|
|
484
747
|
app.post('/checkout', async (req, res) => {
|
|
485
|
-
const { orderId, amount,
|
|
486
|
-
|
|
487
|
-
const payment = await
|
|
748
|
+
const { orderId, amount, blockchain } = req.body;
|
|
749
|
+
|
|
750
|
+
const { payment } = await client.createPayment({
|
|
488
751
|
businessId: process.env.COINPAY_BUSINESS_ID,
|
|
489
752
|
amount,
|
|
490
|
-
blockchain
|
|
753
|
+
blockchain,
|
|
491
754
|
description: `Order #${orderId}`,
|
|
492
|
-
metadata: { orderId }
|
|
755
|
+
metadata: { orderId },
|
|
493
756
|
});
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
paymentAddress: payment.payment.payment_address
|
|
757
|
+
|
|
758
|
+
await db.orders.update(orderId, {
|
|
759
|
+
paymentId: payment.id,
|
|
760
|
+
paymentAddress: payment.payment_address,
|
|
499
761
|
});
|
|
500
|
-
|
|
762
|
+
|
|
501
763
|
res.json({
|
|
502
|
-
paymentAddress: payment.
|
|
503
|
-
|
|
504
|
-
qrCode: payment.
|
|
505
|
-
expiresAt: payment.
|
|
764
|
+
paymentAddress: payment.payment_address,
|
|
765
|
+
cryptoAmount: payment.crypto_amount,
|
|
766
|
+
qrCode: payment.qr_code,
|
|
767
|
+
expiresAt: payment.expires_at,
|
|
506
768
|
});
|
|
507
769
|
});
|
|
508
770
|
|
|
509
|
-
// Webhook
|
|
510
|
-
app.post('/webhook
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
}
|
|
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
|
+
);
|
|
520
784
|
```
|
|
521
785
|
|
|
522
|
-
###
|
|
786
|
+
### Stablecoin Subscriptions
|
|
787
|
+
|
|
788
|
+
Use USDC for predictable pricing — no volatility:
|
|
523
789
|
|
|
524
790
|
```javascript
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
businessId: process.env.COINPAY_BUSINESS_ID,
|
|
791
|
+
const { payment } = await client.createPayment({
|
|
792
|
+
businessId: BUSINESS_ID,
|
|
528
793
|
amount: 9.99,
|
|
529
|
-
blockchain:
|
|
794
|
+
blockchain: Blockchain.USDC_POL, // USDC on Polygon — low fees
|
|
530
795
|
description: 'Monthly subscription',
|
|
531
|
-
metadata: {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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,
|
|
536
849
|
});
|
|
537
850
|
```
|
|
538
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
|
+
|
|
539
893
|
## Testing
|
|
540
894
|
|
|
541
|
-
|
|
895
|
+
```bash
|
|
896
|
+
# Run tests
|
|
897
|
+
pnpm test
|
|
542
898
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
+
---
|
|
546
906
|
|
|
547
907
|
## Support
|
|
548
908
|
|
|
549
|
-
-
|
|
550
|
-
-
|
|
551
|
-
-
|
|
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
|
+
---
|
|
552
915
|
|
|
553
916
|
## License
|
|
554
917
|
|
|
555
|
-
MIT
|
|
918
|
+
[MIT](./LICENSE) © Profullstack, Inc.
|