@havenpay/server 1.0.0 → 1.0.1

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.
Files changed (2) hide show
  1. package/README.md +190 -96
  2. package/package.json +8 -7
package/README.md CHANGED
@@ -1,160 +1,254 @@
1
- # Havenpay Server SDK
1
+ # @havenpay/server
2
2
 
3
- Backend-only TypeScript SDK for Havenpay merchant integrations.
3
+ Server-side TypeScript SDK for Havenpay.
4
4
 
5
- This package is for trusted server runtimes only. It uses secret API keys, server-side webhook verification, and management APIs that must never be imported by `@havenpay/web` or `@havenpay/react-native`.
5
+ `@havenpay/server` is the backend SDK for secret-key operations: PaymentIntent creation, project management, API key lifecycle, account lifecycle, provider account credential-reference management, refund requests, ledger-backed balance transaction reads, webhook endpoint management, event replay, and raw-body webhook signature verification.
6
6
 
7
- ## Implemented Surface
7
+ Use it only from trusted backend infrastructure. Never bundle it into browsers, React Native apps, edge pages that expose client code, or public UI packages.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ bun add @havenpay/server
13
+ ```
14
+
15
+ ```sh
16
+ npm install @havenpay/server
17
+ ```
18
+
19
+ ## Use it for
20
+
21
+ - Creating PaymentIntents from a merchant server.
22
+ - Returning a short-lived payment `clientSecret` to browser or mobile clients.
23
+ - Managing projects, API keys, connected accounts, webhook endpoints, and events.
24
+ - Verifying Havenpay webhook signatures against the exact raw request body.
25
+ - Managing provider account encrypted credential references.
26
+ - Reading balance transactions and creating refund requests through server-only APIs.
27
+
28
+ ## Quickstart
8
29
 
9
30
  ```ts
31
+ import { randomUUID } from 'node:crypto'
10
32
  import { Havenpay } from '@havenpay/server'
11
33
 
12
- const _havenpay = new Havenpay({
13
- secretKey: process.env.HAVENPAY_SECRET_KEY!,
34
+ const havenpay = new Havenpay({
35
+ secretKey: process.env.HAVENPAY_SECRET_KEY,
14
36
  apiVersion: '2026-05-24.haven',
37
+ retry: {
38
+ maxAttempts: 3,
39
+ initialDelayMs: 100,
40
+ maxDelayMs: 2000,
41
+ },
42
+ timeoutMs: 30000,
15
43
  })
16
44
 
17
- const _intent = await havenpay.paymentIntents.create({
45
+ const intent = await havenpay.paymentIntents.create({
18
46
  projectId: 'proj_...',
19
47
  amount: 1000,
20
48
  currency: 'usd',
21
- customer: { phone: '+263771234567' },
22
- mobileMoney: { provider: 'fake', phone: '+263771234567' },
23
- idempotencyKey: crypto.randomUUID(),
49
+ customer: {
50
+ phone: '+263771234567',
51
+ },
52
+ mobileMoney: {
53
+ provider: 'omari',
54
+ phone: '+263771234567',
55
+ },
56
+ metadata: {
57
+ orderId: 'order_123',
58
+ },
59
+ idempotencyKey: randomUUID(),
60
+ })
61
+
62
+ return Response.json({
63
+ paymentIntentId: intent.id,
64
+ clientSecret: intent.clientSecret,
24
65
  })
25
66
  ```
26
67
 
27
- Available resources:
68
+ Send `clientSecret` only to the client that is completing this one payment. Keep `secretKey` on the server.
28
69
 
29
- - `accounts.create/list/update`
30
- - `apiKeys.create/list/revoke`
31
- - `balanceTransactions.list`
32
- - `events.list/replay`
33
- - `paymentIntents.create/retrieve`
34
- - `projects.create/list/update/archive/getConfig`
35
- - `providerAccounts.create/list/rotate/revoke`
36
- - `refunds.create/retrieve/list`
37
- - `webhookEndpoints.create/list/update/delete`
38
- - `webhooks.constructEvent`
70
+ ## Webhook verification
39
71
 
40
- ## Error Handling
72
+ Webhook verification requires the exact raw body bytes sent by Havenpay. Do not parse JSON before signature verification.
41
73
 
42
- Non-2xx REST responses throw `HavenpayApiError`. The class extends `Error` and preserves the legacy message format:
74
+ ```ts
75
+ import { Havenpay } from '@havenpay/server'
43
76
 
44
- ```text
45
- Havenpay API Error: <status> <body>
77
+ const havenpay = new Havenpay({
78
+ secretKey: process.env.HAVENPAY_SECRET_KEY,
79
+ })
80
+
81
+ const event = havenpay.webhooks.constructEvent({
82
+ rawBody,
83
+ signature: request.headers.get('Havenpay-Signature') ?? '',
84
+ secret: process.env.HAVENPAY_WEBHOOK_SECRET,
85
+ })
86
+
87
+ switch (event.type) {
88
+ case 'payment_intent.succeeded':
89
+ // Fulfil the order in an idempotent server-side transaction.
90
+ break
91
+ case 'payment_intent.failed':
92
+ // Mark the order as failed only after validating the event payload.
93
+ break
94
+ }
46
95
  ```
47
96
 
48
- It also exposes:
97
+ ## Main resources
49
98
 
50
- - `status`
51
- - `requestId`
52
- - `body`
53
- - `error`
54
- - `type`
55
- - `code`
99
+ ```text
100
+ havenpay.accounts.create(...)
101
+ havenpay.accounts.list(...)
102
+ havenpay.accounts.update(...)
56
103
 
57
- ```ts
58
- import { HavenpayApiError } from '@havenpay/server'
104
+ havenpay.projects.create(...)
105
+ havenpay.projects.list(...)
106
+ havenpay.projects.update(...)
107
+ havenpay.projects.archive(...)
108
+ havenpay.projects.getConfig(...)
59
109
 
60
- try {
61
- await havenpay.paymentIntents.retrieve('pi_missing')
62
- }
63
- catch (error) {
64
- if (error instanceof HavenpayApiError) {
65
- console.error(error.status, error.requestId, error.code)
66
- }
67
- }
68
- ```
110
+ havenpay.apiKeys.create(...)
111
+ havenpay.apiKeys.list(...)
112
+ havenpay.apiKeys.revoke(...)
69
113
 
70
- Configured request timeouts throw `HavenpayTimeoutError`:
114
+ havenpay.paymentIntents.create(...)
115
+ havenpay.paymentIntents.retrieve(...)
71
116
 
72
- ```ts
73
- import { HavenpayTimeoutError } from '@havenpay/server'
117
+ havenpay.refunds.create(...)
118
+ havenpay.refunds.retrieve(...)
119
+ havenpay.refunds.list(...)
74
120
 
75
- try {
76
- await havenpay.paymentIntents.retrieve('pi_slow')
77
- }
78
- catch (error) {
79
- if (error instanceof HavenpayTimeoutError) {
80
- console.error(error.timeoutMs)
81
- }
82
- }
121
+ havenpay.providerAccounts.create(...)
122
+ havenpay.providerAccounts.list(...)
123
+ havenpay.providerAccounts.rotate(...)
124
+ havenpay.providerAccounts.revoke(...)
125
+
126
+ havenpay.balanceTransactions.list(...)
127
+
128
+ havenpay.webhookEndpoints.create(...)
129
+ havenpay.webhookEndpoints.list(...)
130
+ havenpay.webhookEndpoints.update(...)
131
+ havenpay.webhookEndpoints.rotateSecret(...)
132
+ havenpay.webhookEndpoints.delete(...)
133
+
134
+ havenpay.events.list(...)
135
+ havenpay.events.replay(...)
136
+
137
+ havenpay.webhooks.constructEvent(...)
83
138
  ```
84
139
 
85
- ## Retry Policy
140
+ ## Provider account credential references
86
141
 
87
- Retries are disabled by default. Configure `retry` to retry network failures and HTTP 5xx responses with exponential backoff:
142
+ Provider account APIs accept encrypted credential references and key fingerprints. They must not accept or return raw provider secret material.
88
143
 
89
144
  ```ts
90
- const _havenpay = new Havenpay({
91
- secretKey: process.env.HAVENPAY_SECRET_KEY!,
92
- retry: {
93
- maxAttempts: 3,
94
- initialDelayMs: 100,
95
- maxDelayMs: 2000,
145
+ await havenpay.providerAccounts.create({
146
+ projectId: 'proj_...',
147
+ provider: 'omari',
148
+ encryptedCredentialRef: 'kms://havenpay/provider-credentials/...',
149
+ keyFingerprint: 'fp_...',
150
+ metadata: {
151
+ label: 'Primary test credential',
96
152
  },
97
153
  })
98
154
  ```
99
155
 
100
- - Safe reads may retry.
101
- - Mutating requests retry only when they carry an `Idempotency-Key`.
102
- - Do not retry validation, authentication, authorization, tenant-scope, or not-found errors without changing the request or credentials.
156
+ Live provider capability should be enabled only when the provider adapter, credentials, contract tests, and operational reconciliation path prove that lane.
103
157
 
104
- ## API-Version Pinning
158
+ ## Error handling
105
159
 
106
- Set `apiVersion` to send `X-Haven-API-Version` on server SDK REST requests:
160
+ Non-2xx API responses throw `HavenpayApiError`.
107
161
 
108
162
  ```ts
109
- const _havenpay = new Havenpay({
110
- secretKey: process.env.HAVENPAY_SECRET_KEY!,
111
- apiVersion: '2026-05-24.haven',
112
- })
163
+ import type { Havenpay } from '@havenpay/server'
164
+ import { HavenpayApiError } from '@havenpay/server'
165
+
166
+ async function _retrievePaymentIntent(havenpay: Havenpay) {
167
+ try {
168
+ return await havenpay.paymentIntents.retrieve('pi_...')
169
+ }
170
+ catch (error) {
171
+ if (error instanceof HavenpayApiError) {
172
+ console.error(error.status, error.code, error.requestId)
173
+ }
174
+
175
+ throw error
176
+ }
177
+ }
113
178
  ```
114
179
 
115
- ## Timeouts
180
+ Errors expose request IDs and typed API metadata when the API returns the standard error envelope. Do not log request bodies, API keys, idempotency keys, webhook secrets, provider credentials, or client secrets.
116
181
 
117
- Set `timeoutMs` to apply a per-attempt timeout to server SDK REST requests:
182
+ ## Retry and timeout policy
118
183
 
119
- ```ts
120
- const _havenpay = new Havenpay({
121
- secretKey: process.env.HAVENPAY_SECRET_KEY!,
122
- timeoutMs: 30000,
123
- })
124
- ```
184
+ Retries are disabled unless `retry` is configured.
125
185
 
126
- Caller-provided abort signals are preserved by the shared request helper and are not converted into timeout errors.
186
+ - Network failures and HTTP 5xx responses may retry.
187
+ - Safe reads may retry.
188
+ - Mutating requests retry only when they include an `Idempotency-Key`.
189
+ - Validation, authentication, authorization, tenant-scope, and not-found errors should not be retried without changing the request or credentials.
190
+ - `timeoutMs` applies per request attempt.
127
191
 
128
192
  ## Observability
129
193
 
130
- Set `onRequestEvent` to receive redacted server SDK request lifecycle events:
194
+ `onRequestEvent` emits redacted server-side request lifecycle events.
131
195
 
132
196
  ```ts
133
197
  const _havenpay = new Havenpay({
134
- secretKey: process.env.HAVENPAY_SECRET_KEY!,
198
+ secretKey: process.env.HAVENPAY_SECRET_KEY,
135
199
  onRequestEvent: (event) => {
136
- console.info(event.type, event.operationName, event.requestId, event.status)
200
+ console.info({
201
+ type: event.type,
202
+ operationName: event.operationName,
203
+ requestId: event.requestId,
204
+ status: event.status,
205
+ })
137
206
  },
138
207
  })
139
208
  ```
140
209
 
141
- Events include method, path, generated operation name, attempt, status, error kind, and API response request ID only. They do not include headers, request bodies, API keys, idempotency keys, provider credentials, webhook secrets, or client secrets.
210
+ Events include method, path, operation name, attempt number, status, error kind, and API response request ID only. They do not include headers, request bodies, API keys, idempotency keys, provider credentials, webhook secrets, or client secrets.
211
+
212
+ ## Security boundary
213
+
214
+ This is a server-only package. It may use Node/Bun server capabilities and secret-key authentication. It must never be imported by:
215
+
216
+ - `@havenpay/web`
217
+ - `@havenpay/web-elements`
218
+ - `@havenpay/react-native`
219
+ - browser bundles
220
+ - mobile client bundles
221
+
222
+ Keep these values server-side:
142
223
 
143
- ## Webhook Verification
224
+ - `HAVENPAY_SECRET_KEY`
225
+ - webhook signing secrets
226
+ - provider credential references and fingerprints
227
+ - account admin tokens
228
+ - raw webhook request bodies
229
+ - raw provider response payloads
144
230
 
145
- `webhooks.constructEvent` verifies the exact raw request body with the endpoint webhook secret:
231
+ ## Package boundaries
232
+
233
+ Use:
234
+
235
+ - `@havenpay/server` for trusted backend operations.
236
+ - `@havenpay/web` for browser payment-sheet flows.
237
+ - `@havenpay/web-elements` for optional browser custom elements.
238
+ - `@havenpay/react-native` for Expo and bare React Native payment flows.
239
+ - `@havenpay/core` for shared public types.
240
+
241
+ Client apps should receive only a public `projectId` and the short-lived payment `clientSecret` required for one payment flow.
242
+
243
+ ## API-version pinning
244
+
245
+ Pass `apiVersion` to send `X-Haven-API-Version` with SDK requests:
146
246
 
147
247
  ```ts
148
- const _event = havenpay.webhooks.constructEvent({
149
- rawBody,
150
- signature: request.headers.get('havenpay-signature') ?? '',
151
- secret: process.env.HAVENPAY_WEBHOOK_SECRET!,
248
+ const _havenpay = new Havenpay({
249
+ secretKey: process.env.HAVENPAY_SECRET_KEY,
250
+ apiVersion: '2026-05-24.haven',
152
251
  })
153
252
  ```
154
253
 
155
- ## Boundaries
156
-
157
- - Do not use this package in browser or React Native code.
158
- - Do not pass provider credentials or webhook secrets to client SDKs.
159
- - Do not claim live provider support unless the provider adapter and tests prove it.
160
- - Public client integrations should use `projectId` plus a short-lived payment `clientSecret` through the Web or React Native SDKs.
254
+ Pinning keeps backend integrations explicit as Havenpay evolves.
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@havenpay/server",
3
3
  "type": "module",
4
- "version": "1.0.0",
5
- "description": "Official Server SDK for Havenpay",
4
+ "version": "1.0.1",
5
+ "description": "Server-side TypeScript SDK for Havenpay payment, project, webhook, and account APIs",
6
6
  "author": "7Haven",
7
7
  "license": "MIT",
8
- "homepage": "https://pay.7haven.online",
8
+ "homepage": "https://github.com/7haveeen/haven-pay#readme",
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/7haveeen/haven-pay.git",
@@ -16,9 +16,11 @@
16
16
  "havenpay",
17
17
  "payments",
18
18
  "zimbabwe",
19
+ "mobile-money",
20
+ "typescript",
21
+ "server-sdk",
19
22
  "omari",
20
23
  "onemoney",
21
- "mobile-money",
22
24
  "payment-gateway"
23
25
  ],
24
26
  "sideEffects": false,
@@ -36,8 +38,7 @@
36
38
  "dist"
37
39
  ],
38
40
  "publishConfig": {
39
- "access": "public",
40
- "provenance": true
41
+ "access": "public"
41
42
  },
42
43
  "engines": {
43
44
  "node": ">=18.0.0"
@@ -52,7 +53,7 @@
52
53
  "prepublishOnly": "bun run build && bun run typecheck"
53
54
  },
54
55
  "dependencies": {
55
- "@havenpay/core": "1.0.0"
56
+ "@havenpay/core": "1.0.1"
56
57
  },
57
58
  "devDependencies": {
58
59
  "@types/node": "24.9.1",