@pulsebyshiga/node 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -1,18 +1,15 @@
1
1
  # @pulsebyshiga/node
2
2
 
3
- Official Node.js SDK for the Pulse API: quotes, offramp and onramp orders, bank
4
- name-enquiry, collection sessions, and webhook verification. Idempotent requests,
5
- automatic retries, and typed errors are built in.
3
+ The official Node.js SDK for the Pulse API quotes, offramp and onramp orders, bank
4
+ name-enquiry, collection sessions, and webhook verification. Idempotent requests, automatic
5
+ retries, and fully-typed responses and errors are built in.
6
6
 
7
- > **Status: pre-release (0.1.0).** The quote/offramp/onramp/banks surface is reconciled
8
- > 1:1 against the live engine contract
9
- > ([Swagger](https://engine-api.shiga.io/swagger/public/index.html)). The collection-session
10
- > and webhook surface is the product extension landing in the engine — see
11
- > [docs/engine-gap.md](../../docs/engine-gap.md).
7
+ Server-side only: your `sk_*` key and user identity (BVN/NIN) never leave your backend. The
8
+ browser and mobile SDKs receive only a short-lived, single-order session token (`cs_*`).
12
9
 
13
- **New to the SDK?** The [backend integration guide](../../docs/backend-integration.md) walks
14
- through the whole server-side pattern service methods, typed-error handling, webhooks, and
15
- handing the `cs_` session token to the browser component.
10
+ > **Pre-release (0.2.0).** The public API may still change. Quotes, offramp/onramp orders, and
11
+ > bank name-enquiry map 1:1 to the live engine contract
12
+ > ([Swagger](https://engine-api.shiga.io/swagger/public/index.html)).
16
13
 
17
14
  ## Requirements
18
15
 
@@ -26,7 +23,7 @@ npm install @pulsebyshiga/node
26
23
  # pnpm add @pulsebyshiga/node · yarn add @pulsebyshiga/node
27
24
  ```
28
25
 
29
- ## Usage
26
+ ## Quickstart
30
27
 
31
28
  ```ts
32
29
  import Pulse, { useApiKey } from '@pulsebyshiga/node';
@@ -34,21 +31,28 @@ import Pulse, { useApiKey } from '@pulsebyshiga/node';
34
31
  const pulse = new Pulse(useApiKey(process.env.PULSE_API_KEY), {
35
32
  webhookSecret: process.env.PULSE_WEBHOOK_SECRET,
36
33
  });
34
+
35
+ // Lock a price, then create an order against it.
36
+ const quote = await pulse.quotes.create({
37
+ amount: '100.50',
38
+ source_currency: 'USDC',
39
+ destination_currency: 'NGN',
40
+ network: 'BASE',
41
+ });
37
42
  ```
38
43
 
39
44
  `useApiKey(...)` declares the server credential (a bare string still works for back-compat).
40
- The key is sent as `X-API-Key` (the engine's auth scheme) and as a `Bearer` token (the
41
- session surface's scheme) — one key, both means. Default host is
42
- `https://engine-api.shiga.io`; override with `baseUrl` for local development or a future
43
- sandbox.
45
+ The key is sent as `X-API-Key` (the engine's scheme) and as a `Bearer` token (the session
46
+ surface's scheme) — one key, both means. The default host is `https://engine-api.shiga.io`;
47
+ override it with `baseUrl` for local development or a future sandbox.
44
48
 
45
- ### Quote-first flow (engine)
49
+ ## Core concept — quote first
46
50
 
47
- Everything starts with a locked price; orders are created from the `quote.id` before
48
- `expires_at` passes.
51
+ Everything starts with a locked price. Create a quote, then create an order from `quote.id`
52
+ before `expires_at` passes. Amounts are decimal strings; networks are UPPERCASE.
49
53
 
50
- **Just the rate (no order):** `rates.preview` wraps a quote for display nothing settles
51
- and no order is created.
54
+ **Just the rate, no order** `rates.preview` wraps a quote for display; nothing settles and no
55
+ order is created:
52
56
 
53
57
  ```ts
54
58
  const rate = await pulse.rates.preview({
@@ -61,7 +65,9 @@ const rate = await pulse.rates.preview({
61
65
  // Proceed to an order with quoteId to transact at exactly this locked rate.
62
66
  ```
63
67
 
64
- **Offramp — crypto in, fiat out** (the collection direction):
68
+ ## Offramp — crypto in, fiat out
69
+
70
+ The collection direction. Verify the payout account (name enquiry), then create the order:
65
71
 
66
72
  ```ts
67
73
  const quote = await pulse.quotes.create({
@@ -71,7 +77,6 @@ const quote = await pulse.quotes.create({
71
77
  network: 'BASE', // BASE | POLYGON | ARBITRUM | OPTIMISM | ETHEREUM | BSC
72
78
  });
73
79
 
74
- // Verify the payout account first (name enquiry).
75
80
  const account = await pulse.banks.resolve({ account_number: '0123456789', bank_code: '000013' });
76
81
 
77
82
  const order = await pulse.offramp.create({
@@ -88,7 +93,7 @@ const order = await pulse.offramp.create({
88
93
  // Poll pulse.offramp.retrieve(order.id) until a terminal status.
89
94
  ```
90
95
 
91
- **Onramp — fiat in, crypto out:**
96
+ ## Onramp — fiat in, crypto out
92
97
 
93
98
  ```ts
94
99
  const quote = await pulse.quotes.create({
@@ -108,14 +113,15 @@ const order = await pulse.onramp.create({
108
113
  // Poll pulse.onramp.retrieve(order.id) until a terminal status.
109
114
  ```
110
115
 
111
- **Banks:** `pulse.banks.list()` returns supported banks (name + NIP code).
116
+ ## Banks
112
117
 
113
- ## Product surface collection sessions (landing in the engine)
118
+ - `pulse.banks.list()`supported banks (name + NIP code).
119
+ - `pulse.banks.resolve({ account_number, bank_code })` — name enquiry; run it before creating an offramp order.
114
120
 
115
- The embedded components run on this surface; the dev mock implements it today and
116
- [docs/engine-gap.md](../../docs/engine-gap.md) tracks its adoption into the engine.
121
+ ## Collection sessions
117
122
 
118
- ### Create a collection session
123
+ The surface the embedded components run on. Create a session server-side and hand the `cs_*`
124
+ token to your frontend — nothing else.
119
125
 
120
126
  ```ts
121
127
  // Naira gate — funds settle to the user's named virtual account.
@@ -143,11 +149,10 @@ const usdSession = await pulse.collectionSessions.create({
143
149
  // Hand session.session_token (cs_*) to your frontend. Nothing else.
144
150
  ```
145
151
 
146
- **Direction.** Sessions default to `offramp` (crypto in → fiat out). Pass
147
- `direction: 'onramp'` for the reverse — the user pays a virtual bank account
148
- (carried on the order as `pay_account`) and receives crypto at a wallet. The
149
- embedded component renders the matching UI automatically (bank-transfer
150
- instructions instead of a deposit address/QR).
152
+ **Direction.** Sessions default to `offramp` (crypto in → fiat out). Pass `direction: 'onramp'`
153
+ for the reverse — the user pays a virtual bank account (carried on the order as `pay_account`)
154
+ and receives crypto at a wallet. The embedded component renders the matching UI automatically
155
+ (bank-transfer instructions instead of a deposit address/QR).
151
156
 
152
157
  ```ts
153
158
  const onrampSession = await pulse.collectionSessions.create({
@@ -161,7 +166,7 @@ const onrampSession = await pulse.collectionSessions.create({
161
166
  });
162
167
  ```
163
168
 
164
- ### Query orders
169
+ **Query orders.**
165
170
 
166
171
  ```ts
167
172
  const order = await pulse.collectionOrders.retrieve(orderId); // ground truth for one order
@@ -235,14 +240,9 @@ embed's `onSuccess` are UX signals and can be spoofed; the signed webhook cannot
235
240
 
236
241
  ## Reliability
237
242
 
238
- - **Idempotency** — every POST carries an `Idempotency-Key` (yours via `idempotencyKey`,
239
- otherwise a generated UUID), so retries can never double-create an order.
240
- - **Retries** — network errors, HTTP 429 and 5xx are retried with exponential backoff
241
- (2 retries, 250 ms base delay by default). Other 4xx responses throw immediately.
242
- - **Identification, not telemetry** — every request carries a static
243
- `X-Pulse-SDK: pulse-node/<version> node/<version> (<platform>)` header so the API can
244
- measure adoption and error rates per SDK version server-side. The SDK never emits
245
- telemetry from your infrastructure.
243
+ - **Idempotency** — every POST carries an `Idempotency-Key` (yours via `idempotencyKey`, otherwise a generated UUID), so retries can never double-create an order.
244
+ - **Retries** network errors, HTTP 429, and 5xx are retried with exponential backoff (2 retries, 250 ms base delay by default). Other 4xx responses throw immediately.
245
+ - **Identification, not telemetry** — every request carries a static `X-Pulse-SDK: pulse-node/<version> node/<version> (<platform>)` header so the API can measure adoption and error rates per SDK version. The SDK never emits telemetry from your infrastructure.
246
246
 
247
247
  ## Configuration
248
248
 
@@ -252,7 +252,7 @@ new Pulse(apiKey, options?)
252
252
 
253
253
  | Option | Type | Default | Description |
254
254
  | ------------------- | --------------------- | ----------------------- | ------------------------------------------------------------------- |
255
- | `baseUrl` | `string` | derived from key prefix | API host override (local mock, staging). |
255
+ | `baseUrl` | `string` | derived from key prefix | API host override (local development, staging). |
256
256
  | `maxRetries` | `number` | `2` | Retry count for network errors / 429 / 5xx. |
257
257
  | `retryDelayMs` | `number` | `250` | Base backoff delay, doubled per attempt. |
258
258
  | `fetchImpl` | `FetchLike` | global `fetch` | Injectable transport for tests/instrumentation. |
@@ -286,16 +286,21 @@ try {
286
286
  ## TypeScript
287
287
 
288
288
  Written in TypeScript; every request, response, webhook event, and error is fully typed and
289
- exported (`CollectionOrder`, `WebhookEvent`, `PartnerConfig`, `Network`, …). The webhook
290
- event union discriminates on `type`, so a `switch` narrows `event.data` automatically.
289
+ exported (`CollectionOrder`, `WebhookEvent`, `PartnerConfig`, `Network`, …). The webhook event
290
+ union discriminates on `type`, so a `switch` narrows `event.data` automatically.
291
291
 
292
292
  ## Security model
293
293
 
294
- - `sk_*` keys and BVN/NIN are **server-to-server only** — never ship them to a browser or
295
- mobile app. Clients receive only the short-lived, single-order `session_token` (`cs_*`).
294
+ - `sk_*` keys and BVN/NIN are **server-to-server only** — never ship them to a browser or mobile app. Clients receive only the short-lived, single-order `session_token` (`cs_*`).
296
295
  - Verify every webhook before acting on it; verification is constant-time and replay-bounded.
297
296
  - Credit users exclusively from the signed `disbursement.completed` event.
298
297
 
298
+ ## Related packages
299
+
300
+ - [`@pulsebyshiga/collect-js`](https://www.npmjs.com/package/@pulsebyshiga/collect-js) — framework-agnostic web loader for the payment embed.
301
+ - [`@pulsebyshiga/collect-react`](https://www.npmjs.com/package/@pulsebyshiga/collect-react) — React bindings.
302
+ - [`@pulsebyshiga/collect-react-native`](https://www.npmjs.com/package/@pulsebyshiga/collect-react-native) — React Native bindings.
303
+
299
304
  ## License
300
305
 
301
- [MIT](../../LICENSE) © Shiga Digital
306
+ MIT © Shiga Digital
package/dist/version.d.ts CHANGED
@@ -3,4 +3,4 @@
3
3
  * a source constant avoids JSON-import interop differences across Node
4
4
  * versions this SDK supports.
5
5
  */
6
- export declare const SDK_VERSION = "0.2.0";
6
+ export declare const SDK_VERSION = "0.2.1";
package/dist/version.js CHANGED
@@ -3,4 +3,4 @@
3
3
  * a source constant avoids JSON-import interop differences across Node
4
4
  * versions this SDK supports.
5
5
  */
6
- export const SDK_VERSION = '0.2.0';
6
+ export const SDK_VERSION = '0.2.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulsebyshiga/node",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Pulse Collect backend SDK — collection sessions, orders, and webhook signature verification",
5
5
  "license": "MIT",
6
6
  "author": "Shiga Digital",