@zendfi/sdk 0.3.0 → 0.4.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 +0 -290
- package/README.md.old +326 -0
- package/dist/express.d.mts +1 -1
- package/dist/express.d.ts +1 -1
- package/dist/index.d.mts +166 -4
- package/dist/index.d.ts +166 -4
- package/dist/index.js +339 -74
- package/dist/index.mjs +332 -74
- package/dist/nextjs.d.mts +1 -1
- package/dist/nextjs.d.ts +1 -1
- package/dist/{webhook-handler-DG-zic8m.d.mts → webhook-handler-B9ZczHQn.d.mts} +2 -19
- package/dist/{webhook-handler-DG-zic8m.d.ts → webhook-handler-B9ZczHQn.d.ts} +2 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
# @zendfi/sdk
|
|
2
|
-
|
|
3
|
-
> Zero-config TypeScript SDK for ZendFi crypto payments
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@zendfi/sdk)
|
|
6
|
-
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
|
|
8
|
-
## Features
|
|
9
|
-
|
|
10
|
-
- **Zero Configuration** — Auto-detects environment and works with sensible defaults
|
|
11
|
-
- **Type-Safe** — Full TypeScript types for payments, payment links, subscriptions, escrows, invoices and webhook payloads
|
|
12
|
-
- **Auto-Retry** — Built-in retry logic with exponential backoff for network/server errors
|
|
13
|
-
- **Idempotency** — Automatic idempotency key generation for safe retries
|
|
14
|
-
- **Webhook Helpers** — Auto-verified handlers with optional deduplication and framework adapters
|
|
15
|
-
|
|
16
|
-
## Installation
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
npm install @zendfi/sdk
|
|
20
|
-
# or
|
|
21
|
-
pnpm add @zendfi/sdk
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Quick Start
|
|
25
|
-
|
|
26
|
-
### 1. Set your API key
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
# .env.local
|
|
30
|
-
ZENDFI_API_KEY=zfi_test_your_api_key_here
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### 2. Create a payment
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
import { zendfi } from '@zendfi/sdk';
|
|
37
|
-
|
|
38
|
-
// Singleton auto-configured from environment
|
|
39
|
-
const payment = await zendfi.createPayment({
|
|
40
|
-
amount: 50,
|
|
41
|
-
description: 'Premium subscription',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
console.log(payment.checkout_url); // Send this to your customer
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### 3. Or use an explicit client (recommended for server code)
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
import { ZendFiClient } from '@zendfi/sdk';
|
|
51
|
-
|
|
52
|
-
const client = new ZendFiClient({
|
|
53
|
-
apiKey: process.env.ZENDFI_API_KEY
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const payment = await client.createPayment({
|
|
57
|
-
amount: 99.99,
|
|
58
|
-
currency: 'USD',
|
|
59
|
-
description: 'Annual subscription',
|
|
60
|
-
});
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
That's it! The SDK handles everything else automatically. 🎉
|
|
64
|
-
|
|
65
|
-
## Usage
|
|
66
|
-
|
|
67
|
-
### Payments
|
|
68
|
-
|
|
69
|
-
#### Create a Payment
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
const payment = await zendfi.createPayment({
|
|
73
|
-
amount: 99.99,
|
|
74
|
-
currency: 'USD', // Optional, defaults to 'USD'
|
|
75
|
-
token: 'USDC', // Optional, defaults to 'USDC'
|
|
76
|
-
description: 'Annual subscription',
|
|
77
|
-
customer_email: 'customer@example.com',
|
|
78
|
-
redirect_url: 'https://yourapp.com/success',
|
|
79
|
-
metadata: {
|
|
80
|
-
orderId: 'ORD-123',
|
|
81
|
-
customerId: 'CUST-456',
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Redirect customer to checkout
|
|
86
|
-
window.location.href = payment.checkout_url;
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Payment Links
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
const client = new ZendFiClient({
|
|
93
|
-
apiKey: process.env.ZENDFI_API_KEY
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// Create a payment link
|
|
97
|
-
const link = await client.createPaymentLink({
|
|
98
|
-
amount: 20,
|
|
99
|
-
currency: 'USD',
|
|
100
|
-
description: 'Product purchase',
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
console.log(link.hosted_page_url);
|
|
104
|
-
|
|
105
|
-
// List all payment links
|
|
106
|
-
const links = await client.listPaymentLinks();
|
|
107
|
-
console.log(`Total links: ${links.length}`);
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
> **Note:** If you get a 405 error when calling `listPaymentLinks()` in tests, confirm that `ZENDFI_API_URL` / `baseURL` and your API key point to a server that exposes `GET /api/v1/payment-links`.
|
|
111
|
-
|
|
112
|
-
## Webhooks
|
|
113
|
-
|
|
114
|
-
The SDK includes robust webhook processing with signature verification, optional deduplication, and typed handler dispatch.
|
|
115
|
-
|
|
116
|
-
### Next.js App Router (Recommended)
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
// app/api/webhooks/zendfi/route.ts
|
|
120
|
-
import { createNextWebhookHandler } from '@zendfi/sdk/next';
|
|
121
|
-
|
|
122
|
-
export const POST = createNextWebhookHandler({
|
|
123
|
-
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
|
|
124
|
-
handlers: {
|
|
125
|
-
'payment.confirmed': async (payment) => {
|
|
126
|
-
// Payment is already verified and typed
|
|
127
|
-
await db.orders.update({
|
|
128
|
-
where: { id: payment.metadata.orderId },
|
|
129
|
-
data: { status: 'paid' },
|
|
130
|
-
});
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### Next.js Pages Router (Legacy)
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
// pages/api/webhooks/zendfi.ts
|
|
140
|
-
import { createPagesWebhookHandler } from '@zendfi/sdk/next';
|
|
141
|
-
|
|
142
|
-
export default createPagesWebhookHandler({
|
|
143
|
-
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
|
|
144
|
-
handlers: {
|
|
145
|
-
'payment.confirmed': async (payment) => {
|
|
146
|
-
await fulfillOrder(payment.metadata.orderId);
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Important: Disable body parser for signature verification
|
|
152
|
-
export const config = {
|
|
153
|
-
api: { bodyParser: false },
|
|
154
|
-
};
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Express
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
import express from 'express';
|
|
161
|
-
import { createExpressWebhookHandler } from '@zendfi/sdk/express';
|
|
162
|
-
|
|
163
|
-
const app = express();
|
|
164
|
-
|
|
165
|
-
app.post(
|
|
166
|
-
'/api/webhooks/zendfi',
|
|
167
|
-
express.raw({ type: 'application/json' }), // Preserve raw body
|
|
168
|
-
createExpressWebhookHandler({
|
|
169
|
-
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
|
|
170
|
-
handlers: {
|
|
171
|
-
'payment.confirmed': async (payment) => {
|
|
172
|
-
// Handle confirmed payment
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
})
|
|
176
|
-
);
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### Webhook Deduplication
|
|
180
|
-
|
|
181
|
-
By default, the handler uses an in-memory Set for deduplication (suitable for development). For production, supply `isProcessed` and `onProcessed` hooks (or the aliases `checkDuplicate` / `markProcessed`) backed by Redis or your database:
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
export const POST = createNextWebhookHandler({
|
|
185
|
-
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
|
|
186
|
-
isProcessed: async (eventId) => {
|
|
187
|
-
return await redis.exists(`webhook:${eventId}`);
|
|
188
|
-
},
|
|
189
|
-
onProcessed: async (eventId) => {
|
|
190
|
-
await redis.set(`webhook:${eventId}`, '1', 'EX', 86400);
|
|
191
|
-
},
|
|
192
|
-
handlers: {
|
|
193
|
-
'payment.confirmed': async (payment) => {
|
|
194
|
-
// Handle payment
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
When deduplication is enabled, duplicate requests are rejected with HTTP 409.
|
|
201
|
-
|
|
202
|
-
### Manual Webhook Verification
|
|
203
|
-
|
|
204
|
-
If you prefer manual verification:
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
import { verifyNextWebhook } from '@zendfi/sdk/webhooks';
|
|
208
|
-
|
|
209
|
-
export async function POST(request: Request) {
|
|
210
|
-
const payload = await verifyNextWebhook(request);
|
|
211
|
-
if (!payload) {
|
|
212
|
-
return new Response('Invalid signature', { status: 401 });
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Handle verified payload
|
|
216
|
-
return new Response('OK');
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
**Available helpers:**
|
|
221
|
-
- `verifyNextWebhook(request, secret?)` — Next.js App Router
|
|
222
|
-
- `verifyExpressWebhook(req, secret?)` — Express
|
|
223
|
-
- `verifyWebhookSignature(payload, signature, secret)` — Low-level verifier
|
|
224
|
-
|
|
225
|
-
## Configuration & Environment Variables
|
|
226
|
-
|
|
227
|
-
| Variable | Required | Description |
|
|
228
|
-
|----------|----------|-------------|
|
|
229
|
-
| `ZENDFI_API_KEY` | Yes* | Your ZendFi API key (*unless passed to `ZendFiClient`) |
|
|
230
|
-
| `ZENDFI_WEBHOOK_SECRET` | No | Used by webhook adapters for auto-verification |
|
|
231
|
-
| `ZENDFI_API_URL` | No | Override API base URL (useful for local testing) |
|
|
232
|
-
| `ZENDFI_ENVIRONMENT` | No | Optional environment override |
|
|
233
|
-
|
|
234
|
-
## Error Handling
|
|
235
|
-
|
|
236
|
-
The SDK throws typed errors that you can import and check with `instanceof`:
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
import {
|
|
240
|
-
AuthenticationError,
|
|
241
|
-
ValidationError,
|
|
242
|
-
NetworkError
|
|
243
|
-
} from '@zendfi/sdk';
|
|
244
|
-
|
|
245
|
-
try {
|
|
246
|
-
await zendfi.createPayment({ amount: 50 });
|
|
247
|
-
} catch (error) {
|
|
248
|
-
if (error instanceof AuthenticationError) {
|
|
249
|
-
// Handle authentication error
|
|
250
|
-
} else if (error instanceof ValidationError) {
|
|
251
|
-
// Handle validation error
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
## Troubleshooting
|
|
257
|
-
|
|
258
|
-
**Webhook verification failures:**
|
|
259
|
-
- Ensure you're using `express.raw({ type: 'application/json' })` for Express
|
|
260
|
-
- For Next.js Pages Router, set `export const config = { api: { bodyParser: false } }`
|
|
261
|
-
- Middleware consuming the raw body will break signature verification
|
|
262
|
-
|
|
263
|
-
**405 errors on `listPaymentLinks()`:**
|
|
264
|
-
- Verify your `ZENDFI_API_URL` is correct
|
|
265
|
-
- Confirm your API server exposes `GET /api/v1/payment-links`
|
|
266
|
-
|
|
267
|
-
**tsup warnings about types condition:**
|
|
268
|
-
- This is a packaging order issue that doesn't affect runtime behavior
|
|
269
|
-
|
|
270
|
-
## Contributing
|
|
271
|
-
|
|
272
|
-
Run the SDK build and tests locally before opening a PR:
|
|
273
|
-
|
|
274
|
-
```bash
|
|
275
|
-
cd packages/sdk
|
|
276
|
-
pnpm install
|
|
277
|
-
pnpm run build
|
|
278
|
-
pnpm test
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
## Support
|
|
282
|
-
|
|
283
|
-
- **Documentation:** https://docs.zendfi.tech
|
|
284
|
-
- **API Reference:** https://docs.zendfi.tech/api
|
|
285
|
-
- **GitHub Issues:** https://github.com/zendfi/zendfi-toolkit/issues
|
|
286
|
-
- **Email:** dev@zendfi.tech
|
|
287
|
-
|
|
288
|
-
## License
|
|
289
|
-
|
|
290
|
-
MIT © ZendFi
|
package/README.md.old
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# @zendfi/sdk
|
|
2
|
+
|
|
3
|
+
> Zero-config TypeScript SDK for ZendFi crypto payments
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@zendfi/sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Zero Configuration** — Auto-detects environment and works with sensible defaults
|
|
11
|
+
- **Type-Safe** — Full TypeScript types for payments, payment links, subscriptions, escrows, invoices and webhook payloads
|
|
12
|
+
- **Auto-Retry** — Built-in retry logic with exponential backoff for network/server errors
|
|
13
|
+
- **Idempotency** — Automatic idempotency key generation for safe retries
|
|
14
|
+
- **Webhook Helpers** — Auto-verified handlers with optional deduplication and framework adapters
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @zendfi/sdk
|
|
20
|
+
# or
|
|
21
|
+
pnpm add @zendfi/sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Set your API key
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# .env.local or .env
|
|
30
|
+
|
|
31
|
+
# For development (uses Solana devnet)
|
|
32
|
+
ZENDFI_API_KEY=zfi_test_your_test_api_key_here
|
|
33
|
+
|
|
34
|
+
# For production (uses Solana mainnet)
|
|
35
|
+
# ZENDFI_API_KEY=zfi_live_your_live_api_key_here
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**API Key Modes:**
|
|
39
|
+
- `zfi_test_*` keys route to **Solana Devnet** (free test SOL, no real money)
|
|
40
|
+
- `zfi_live_*` keys route to **Solana Mainnet** (real crypto transactions)
|
|
41
|
+
|
|
42
|
+
The SDK automatically detects the mode from your API key prefix.
|
|
43
|
+
|
|
44
|
+
### 2. Create a payment
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { zendfi } from '@zendfi/sdk';
|
|
48
|
+
|
|
49
|
+
// Singleton auto-configured from environment
|
|
50
|
+
const payment = await zendfi.createPayment({
|
|
51
|
+
amount: 50,
|
|
52
|
+
description: 'Premium subscription',
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
console.log(payment.checkout_url); // Send this to your customer
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Or use an explicit client (recommended for server code)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { ZendFiClient } from '@zendfi/sdk';
|
|
62
|
+
|
|
63
|
+
const client = new ZendFiClient({
|
|
64
|
+
apiKey: process.env.ZENDFI_API_KEY
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const payment = await client.createPayment({
|
|
68
|
+
amount: 99.99,
|
|
69
|
+
currency: 'USD',
|
|
70
|
+
description: 'Annual subscription',
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
That's it! The SDK handles everything else automatically. 🎉
|
|
75
|
+
|
|
76
|
+
## Test vs Live Mode
|
|
77
|
+
|
|
78
|
+
ZendFi provides separate API keys for testing and production:
|
|
79
|
+
|
|
80
|
+
| Mode | API Key Prefix | Network | Purpose |
|
|
81
|
+
|------|---------------|---------|---------|
|
|
82
|
+
| **Test** | `zfi_test_` | Solana Devnet | Development & testing with fake SOL |
|
|
83
|
+
| **Live** | `zfi_live_` | Solana Mainnet | Production with real crypto |
|
|
84
|
+
|
|
85
|
+
### Getting Test SOL
|
|
86
|
+
|
|
87
|
+
For testing on devnet:
|
|
88
|
+
1. Use your `zfi_test_` API key
|
|
89
|
+
2. Get free devnet SOL from [sol-faucet.com](https://www.sol-faucet.com/) or `solana airdrop`
|
|
90
|
+
3. All transactions use test tokens (no real value)
|
|
91
|
+
|
|
92
|
+
### Going Live
|
|
93
|
+
|
|
94
|
+
When ready for production:
|
|
95
|
+
1. Switch to your `zfi_live_` API key
|
|
96
|
+
2. All transactions will use real crypto on mainnet
|
|
97
|
+
3. Customers pay with real SOL/USDC/USDT
|
|
98
|
+
|
|
99
|
+
The SDK automatically routes to the correct network based on your API key prefix!
|
|
100
|
+
|
|
101
|
+
## Usage
|
|
102
|
+
|
|
103
|
+
### Payments
|
|
104
|
+
|
|
105
|
+
#### Create a Payment
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const payment = await zendfi.createPayment({
|
|
109
|
+
amount: 99.99,
|
|
110
|
+
currency: 'USD', // Optional, defaults to 'USD'
|
|
111
|
+
token: 'USDC', // Optional, defaults to 'USDC'
|
|
112
|
+
description: 'Annual subscription',
|
|
113
|
+
customer_email: 'customer@example.com',
|
|
114
|
+
redirect_url: 'https://yourapp.com/success',
|
|
115
|
+
metadata: {
|
|
116
|
+
orderId: 'ORD-123',
|
|
117
|
+
customerId: 'CUST-456',
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Redirect customer to checkout
|
|
122
|
+
window.location.href = payment.checkout_url;
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Payment Links
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const client = new ZendFiClient({
|
|
129
|
+
apiKey: process.env.ZENDFI_API_KEY
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Create a payment link
|
|
133
|
+
const link = await client.createPaymentLink({
|
|
134
|
+
amount: 20,
|
|
135
|
+
currency: 'USD',
|
|
136
|
+
description: 'Product purchase',
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
console.log(link.hosted_page_url);
|
|
140
|
+
|
|
141
|
+
// List all payment links
|
|
142
|
+
const links = await client.listPaymentLinks();
|
|
143
|
+
console.log(`Total links: ${links.length}`);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
> **Note:** If you get a 405 error when calling `listPaymentLinks()` in tests, confirm that `ZENDFI_API_URL` / `baseURL` and your API key point to a server that exposes `GET /api/v1/payment-links`.
|
|
147
|
+
|
|
148
|
+
## Webhooks
|
|
149
|
+
|
|
150
|
+
The SDK includes robust webhook processing with signature verification, optional deduplication, and typed handler dispatch.
|
|
151
|
+
|
|
152
|
+
### Next.js App Router (Recommended)
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
// app/api/webhooks/zendfi/route.ts
|
|
156
|
+
import { createNextWebhookHandler } from '@zendfi/sdk/next';
|
|
157
|
+
|
|
158
|
+
export const POST = createNextWebhookHandler({
|
|
159
|
+
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
|
|
160
|
+
handlers: {
|
|
161
|
+
'payment.confirmed': async (payment) => {
|
|
162
|
+
// Payment is already verified and typed
|
|
163
|
+
await db.orders.update({
|
|
164
|
+
where: { id: payment.metadata.orderId },
|
|
165
|
+
data: { status: 'paid' },
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Next.js Pages Router (Legacy)
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// pages/api/webhooks/zendfi.ts
|
|
176
|
+
import { createPagesWebhookHandler } from '@zendfi/sdk/next';
|
|
177
|
+
|
|
178
|
+
export default createPagesWebhookHandler({
|
|
179
|
+
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
|
|
180
|
+
handlers: {
|
|
181
|
+
'payment.confirmed': async (payment) => {
|
|
182
|
+
await fulfillOrder(payment.metadata.orderId);
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Important: Disable body parser for signature verification
|
|
188
|
+
export const config = {
|
|
189
|
+
api: { bodyParser: false },
|
|
190
|
+
};
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Express
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import express from 'express';
|
|
197
|
+
import { createExpressWebhookHandler } from '@zendfi/sdk/express';
|
|
198
|
+
|
|
199
|
+
const app = express();
|
|
200
|
+
|
|
201
|
+
app.post(
|
|
202
|
+
'/api/webhooks/zendfi',
|
|
203
|
+
express.raw({ type: 'application/json' }), // Preserve raw body
|
|
204
|
+
createExpressWebhookHandler({
|
|
205
|
+
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
|
|
206
|
+
handlers: {
|
|
207
|
+
'payment.confirmed': async (payment) => {
|
|
208
|
+
// Handle confirmed payment
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Webhook Deduplication
|
|
216
|
+
|
|
217
|
+
By default, the handler uses an in-memory Set for deduplication (suitable for development). For production, supply `isProcessed` and `onProcessed` hooks (or the aliases `checkDuplicate` / `markProcessed`) backed by Redis or your database:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
export const POST = createNextWebhookHandler({
|
|
221
|
+
secret: process.env.ZENDFI_WEBHOOK_SECRET!,
|
|
222
|
+
isProcessed: async (eventId) => {
|
|
223
|
+
return await redis.exists(`webhook:${eventId}`);
|
|
224
|
+
},
|
|
225
|
+
onProcessed: async (eventId) => {
|
|
226
|
+
await redis.set(`webhook:${eventId}`, '1', 'EX', 86400);
|
|
227
|
+
},
|
|
228
|
+
handlers: {
|
|
229
|
+
'payment.confirmed': async (payment) => {
|
|
230
|
+
// Handle payment
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
When deduplication is enabled, duplicate requests are rejected with HTTP 409.
|
|
237
|
+
|
|
238
|
+
### Manual Webhook Verification
|
|
239
|
+
|
|
240
|
+
If you prefer manual verification:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { verifyNextWebhook } from '@zendfi/sdk/webhooks';
|
|
244
|
+
|
|
245
|
+
export async function POST(request: Request) {
|
|
246
|
+
const payload = await verifyNextWebhook(request);
|
|
247
|
+
if (!payload) {
|
|
248
|
+
return new Response('Invalid signature', { status: 401 });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Handle verified payload
|
|
252
|
+
return new Response('OK');
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Available helpers:**
|
|
257
|
+
- `verifyNextWebhook(request, secret?)` — Next.js App Router
|
|
258
|
+
- `verifyExpressWebhook(req, secret?)` — Express
|
|
259
|
+
- `verifyWebhookSignature(payload, signature, secret)` — Low-level verifier
|
|
260
|
+
|
|
261
|
+
## Configuration & Environment Variables
|
|
262
|
+
|
|
263
|
+
| Variable | Required | Description |
|
|
264
|
+
|----------|----------|-------------|
|
|
265
|
+
| `ZENDFI_API_KEY` | Yes* | Your ZendFi API key (*unless passed to `ZendFiClient`) |
|
|
266
|
+
| `ZENDFI_WEBHOOK_SECRET` | No | Used by webhook adapters for auto-verification |
|
|
267
|
+
| `ZENDFI_API_URL` | No | Override API base URL (useful for local testing) |
|
|
268
|
+
| `ZENDFI_ENVIRONMENT` | No | Optional environment override |
|
|
269
|
+
|
|
270
|
+
## Error Handling
|
|
271
|
+
|
|
272
|
+
The SDK throws typed errors that you can import and check with `instanceof`:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
import {
|
|
276
|
+
AuthenticationError,
|
|
277
|
+
ValidationError,
|
|
278
|
+
NetworkError
|
|
279
|
+
} from '@zendfi/sdk';
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
await zendfi.createPayment({ amount: 50 });
|
|
283
|
+
} catch (error) {
|
|
284
|
+
if (error instanceof AuthenticationError) {
|
|
285
|
+
// Handle authentication error
|
|
286
|
+
} else if (error instanceof ValidationError) {
|
|
287
|
+
// Handle validation error
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Troubleshooting
|
|
293
|
+
|
|
294
|
+
**Webhook verification failures:**
|
|
295
|
+
- Ensure you're using `express.raw({ type: 'application/json' })` for Express
|
|
296
|
+
- For Next.js Pages Router, set `export const config = { api: { bodyParser: false } }`
|
|
297
|
+
- Middleware consuming the raw body will break signature verification
|
|
298
|
+
|
|
299
|
+
**405 errors on `listPaymentLinks()`:**
|
|
300
|
+
- Verify your `ZENDFI_API_URL` is correct
|
|
301
|
+
- Confirm your API server exposes `GET /api/v1/payment-links`
|
|
302
|
+
|
|
303
|
+
**tsup warnings about types condition:**
|
|
304
|
+
- This is a packaging order issue that doesn't affect runtime behavior
|
|
305
|
+
|
|
306
|
+
## Contributing
|
|
307
|
+
|
|
308
|
+
Run the SDK build and tests locally before opening a PR:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
cd packages/sdk
|
|
312
|
+
pnpm install
|
|
313
|
+
pnpm run build
|
|
314
|
+
pnpm test
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Support
|
|
318
|
+
|
|
319
|
+
- **Documentation:** https://docs.zendfi.tech
|
|
320
|
+
- **API Reference:** https://docs.zendfi.tech/api
|
|
321
|
+
- **GitHub Issues:** https://github.com/zendfi/zendfi-toolkit/issues
|
|
322
|
+
- **Email:** dev@zendfi.tech
|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
MIT © ZendFi
|
package/dist/express.d.mts
CHANGED
package/dist/express.d.ts
CHANGED