pandascrow 0.0.0-stage → 1.0.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 ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0 (2026-10-25)
4
+
5
+ First release.
6
+
7
+ - `Pandascrow` client for the v3 API (sandbox by default, live with `{ sandbox: false }`), with `escrow`, `wallet`, `payouts`, `bank`, `virtualAccounts`, `invoices`, `paymentLinks`, `kyc` and `auth` resources, plus `request()` for anything not wrapped.
8
+ - `metadata` support for escrows, invoices and virtual accounts.
9
+ - `verifyWebhook()` checks the HMAC-SHA256 signature of the raw body, with replay protection. Works on Node.js, Bun, Deno and edge runtimes.
10
+ - `PandascrowError` for every failure, with `status`, `docUrl` and `response`.
11
+ - Typed parameters and responses, ESM + CommonJS builds, no runtime dependencies.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pandascrow Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,209 @@
1
- # Temporary Holding Version
1
+ # Pandascrow JavaScript SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Official Node.js / JavaScript library for the [Pandascrow v3 API](https://pandascrow.readme.io/): escrow, wallets, payouts, bank transfers, virtual accounts, invoices, payment links, KYC and webhook verification.
4
+
5
+ - Written in TypeScript, ships its own types
6
+ - ESM and CommonJS
7
+ - No dependencies: uses the built-in `fetch` (Node.js 18+, Bun, Deno, Cloudflare Workers, Vercel Edge)
8
+
9
+ > Use this SDK on your server only. Your API key gives full access to your Pandascrow account, so never ship it to a browser or mobile app.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install pandascrow
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ ```ts
20
+ import Pandascrow from 'pandascrow';
21
+
22
+ const ps = new Pandascrow(process.env.PANDASCROW_API_KEY!); // sandbox (default)
23
+ // const ps = new Pandascrow(process.env.PANDASCROW_API_KEY!, { sandbox: false }); // live
24
+
25
+ const escrow = await ps.escrow.createOnetime({
26
+ uuid, // the Pandascrow user acting
27
+ initiator_id: uuid,
28
+ initiator_role: 'seller', // 'buyer' | 'seller' | 'broker'
29
+ title: 'Apartment 4B rent',
30
+ description: '12-month rent, 12 Admiralty Way',
31
+ currency: 'NGN',
32
+ amount: 350000,
33
+ buyer_details: { name: 'Tenant', email: 'tenant@example.com' },
34
+ metadata: { property_id: 'PR-204' }, // your own IDs, see below
35
+ });
36
+
37
+ console.log(escrow.payment_url); // send the buyer here to fund the escrow
38
+ ```
39
+
40
+ CommonJS:
41
+
42
+ ```js
43
+ const { Pandascrow } = require('pandascrow');
44
+ const ps = new Pandascrow(process.env.PANDASCROW_API_KEY);
45
+ ```
46
+
47
+ Your API key is the **public key** of your application (Dashboard → Developers). It is sent as the `Token` header. Most calls also take the `uuid` of the Pandascrow user you are acting for.
48
+
49
+ Every method returns a promise of the `data` object from the API response.
50
+
51
+ ## Errors
52
+
53
+ Every failure rejects with a `PandascrowError`: network errors, timeouts, invalid responses, HTTP errors and any response with `status: false`.
54
+
55
+ ```ts
56
+ import { PandascrowError } from 'pandascrow';
57
+
58
+ try {
59
+ await ps.escrow.complete(uuid, escrowId, otp);
60
+ } catch (err) {
61
+ if (err instanceof PandascrowError) {
62
+ err.message; // "Invalid OTP"
63
+ err.status; // 400 (0 for network errors and timeouts)
64
+ err.docUrl; // link to the API reference, when available
65
+ err.response; // full decoded response body
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Metadata
71
+
72
+ Escrows, invoices and virtual accounts accept a `metadata` object with your own identifiers. Pandascrow stores it as-is, returns it when you fetch the record, and includes it as `data.metadata` in every webhook for that record:
73
+
74
+ ```ts
75
+ await ps.escrow.createOnetime({ ..., metadata: { property_id: 'PR-204' } });
76
+
77
+ // later, in your webhook handler
78
+ event.data.metadata?.property_id; // "PR-204"
79
+ ```
80
+
81
+ Limits: up to 50 keys, key names up to 64 characters, 5 levels of nesting, 8 KB in total.
82
+
83
+ ## Resources
84
+
85
+ | Property | Methods |
86
+ |---|---|
87
+ | `escrow` | `createOnetime`, `createMilestone`, `createRecurring`, `create`, `all`, `find`, `complete`, `resendReleaseOtp`, `payNextMilestone`, `completeMilestone`, `regeneratePayment`, `regenerateMilestonePayment`, `dispute`, `disputes`, `joinRecurring` |
88
+ | `wallet` | `list`, `balances`, `transactions`, `accounts`, `payout` |
89
+ | `payouts` | `create`, `all`, `settlementAccounts` |
90
+ | `bank` | `banks`, `validate`, `transfer`, `settlementAccount` |
91
+ | `virtualAccounts` | `createDynamic`, `createStatic`, `reallocate`, `deactivate`, `reactivate`, `confirmPayment` |
92
+ | `invoices` | `create`, `all`, `find`, `pay`, `cancel` |
93
+ | `paymentLinks` | `create`, `all`, `find`, `isSlugAvailable`, `goLive`, `toggleTip`, `payments`, `initiatePayment`, `completePayment`, `addMargin` |
94
+ | `kyc` | `bvn`, `nin`, `phone`, `nuban`, `driversLicense`, `tin`, `cac`, `searchCac`, `email` |
95
+ | `auth` | `login`, `verifyLoginOtp` |
96
+
97
+ Anything not wrapped yet:
98
+
99
+ ```ts
100
+ await ps.request('GET', '/clients', { uuid });
101
+ await ps.request('POST', '/escrow/recurring/update', {}, { uuid, escrow_id: 12 });
102
+ ```
103
+
104
+ ### Examples
105
+
106
+ ```ts
107
+ // Milestone escrow, paid stage by stage
108
+ await ps.escrow.createMilestone({
109
+ uuid, initiator_id: uuid, initiator_role: 'buyer',
110
+ title: 'Website build', description: 'Company website', currency: 'NGN',
111
+ seller_details: { name: 'Dev Studio', email: 'studio@example.com' },
112
+ deposit_option: 'milestone',
113
+ milestones: [
114
+ { title: 'Design', amount: 150000 },
115
+ { title: 'Build', amount: 350000 },
116
+ ],
117
+ });
118
+
119
+ // NGN transfer to a bank account (keep the idempotency key to retry safely)
120
+ await ps.bank.transfer(uuid, 25000,
121
+ { account_number: '0123456789', account_name: 'Ada Obi', bank_code: '058' },
122
+ { idempotencyKey: 'payout-7781' },
123
+ );
124
+
125
+ // Virtual account that accepts any amount, expiring in 24 hours
126
+ await ps.virtualAccounts.createDynamic(uuid, 'Ada Obi', null, { expiryHours: 24, metadata: { customer_id: 'C-9' } });
127
+
128
+ // Invoice
129
+ await ps.invoices.create({
130
+ ownerUuid: uuid,
131
+ client: { full_name: 'Client Co', email: 'billing@client.co' },
132
+ invoice: { currency: 'NGN', due_date: '2026-12-31' },
133
+ items: [{ description: 'Consulting', quantity: 10, unit_price: 20000 }],
134
+ metadata: { order_id: 'A-1001' },
135
+ });
136
+ ```
137
+
138
+ ## Webhooks
139
+
140
+ Pandascrow signs every webhook with HMAC-SHA256 of the **raw request body**, using your application's **secret key**, and sends it in the `X-Pandascrow-Signature` header. `verifyWebhook` checks it and returns the parsed event. Always give it the raw body, not a parsed object.
141
+
142
+ **Express**
143
+
144
+ ```ts
145
+ import express from 'express';
146
+ import { verifyWebhook } from 'pandascrow';
147
+
148
+ app.post('/webhooks/pandascrow', express.raw({ type: 'application/json' }), async (req, res) => {
149
+ try {
150
+ const event = await verifyWebhook(process.env.PANDASCROW_SECRET_KEY!, req.body, req.get('X-Pandascrow-Signature'));
151
+
152
+ switch (event.event) {
153
+ case 'escrow.paid': /* event.data.metadata?.property_id */ break;
154
+ case 'escrow.completed': break;
155
+ case 'invoice.paid': break;
156
+ }
157
+ res.sendStatus(200);
158
+ } catch {
159
+ res.sendStatus(400);
160
+ }
161
+ });
162
+ ```
163
+
164
+ **Next.js (App Router)**
165
+
166
+ ```ts
167
+ // app/api/webhooks/pandascrow/route.ts
168
+ import { verifyWebhook } from 'pandascrow';
169
+
170
+ export async function POST(req: Request) {
171
+ const raw = await req.text();
172
+ try {
173
+ const event = await verifyWebhook(process.env.PANDASCROW_SECRET_KEY!, raw, req.headers.get('x-pandascrow-signature'));
174
+ // handle event.event / event.data
175
+ return new Response('ok');
176
+ } catch {
177
+ return new Response('invalid signature', { status: 400 });
178
+ }
179
+ }
180
+ ```
181
+
182
+ Events older than 5 minutes are rejected; pass `{ tolerance: seconds }` as the fourth argument to change that (`0` turns it off).
183
+
184
+ Events include `escrow.created`, `escrow.paid`, `escrow.completed`, `escrow.milestone.completed`, `escrow.dispute.opened`, `escrow.payout.completed`, `escrow.payout.processing`, `escrow.payout.failed`, `recurring.payment.success`, `invoice.paid` and `wallet.deposit.success`.
185
+
186
+ ## Options
187
+
188
+ ```ts
189
+ new Pandascrow(apiKey, {
190
+ sandbox: true, // false for live
191
+ baseUrl: undefined, // override the host, e.g. a local API
192
+ timeout: 30000, // milliseconds
193
+ headers: {}, // extra headers on every request
194
+ fetch: undefined, // custom fetch (logging, retries, tests)
195
+ });
196
+ ```
197
+
198
+ ## Development
199
+
200
+ ```bash
201
+ npm install
202
+ npm test # unit tests with a fake fetch, never calls the API
203
+ npm run typecheck
204
+ npm run build # dist/ (ESM + CJS + types)
205
+ ```
206
+
207
+ ## License
208
+
209
+ MIT