payweave 0.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/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/_tsup-dts-rollup.d.ts +19470 -0
- package/dist/chunk-4R5OGOCC.js +94 -0
- package/dist/chunk-4R5OGOCC.js.map +1 -0
- package/dist/chunk-535Q3GUN.js +175 -0
- package/dist/chunk-535Q3GUN.js.map +1 -0
- package/dist/chunk-67NRYGRC.js +192 -0
- package/dist/chunk-67NRYGRC.js.map +1 -0
- package/dist/chunk-CGQ73I4I.js +568 -0
- package/dist/chunk-CGQ73I4I.js.map +1 -0
- package/dist/chunk-CJEYDHY6.js +18 -0
- package/dist/chunk-CJEYDHY6.js.map +1 -0
- package/dist/chunk-CR63JAAS.js +3 -0
- package/dist/chunk-CR63JAAS.js.map +1 -0
- package/dist/chunk-E633MKVX.js +1507 -0
- package/dist/chunk-E633MKVX.js.map +1 -0
- package/dist/chunk-FBPOCXRZ.js +1135 -0
- package/dist/chunk-FBPOCXRZ.js.map +1 -0
- package/dist/chunk-HTRGMZFH.js +267 -0
- package/dist/chunk-HTRGMZFH.js.map +1 -0
- package/dist/chunk-IUVGM3K7.js +765 -0
- package/dist/chunk-IUVGM3K7.js.map +1 -0
- package/dist/chunk-JD4OZHRI.js +326 -0
- package/dist/chunk-JD4OZHRI.js.map +1 -0
- package/dist/chunk-NV3MZF7L.js +139 -0
- package/dist/chunk-NV3MZF7L.js.map +1 -0
- package/dist/chunk-NXQBP35U.js +13 -0
- package/dist/chunk-NXQBP35U.js.map +1 -0
- package/dist/chunk-QJHXDZYC.js +412 -0
- package/dist/chunk-QJHXDZYC.js.map +1 -0
- package/dist/chunk-TWZK62TZ.js +83 -0
- package/dist/chunk-TWZK62TZ.js.map +1 -0
- package/dist/cli/index.js +35381 -0
- package/dist/core/index.d.ts +85 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/db/drizzle/index.d.ts +6 -0
- package/dist/db/drizzle/index.js +1532 -0
- package/dist/db/drizzle/index.js.map +1 -0
- package/dist/db/index.d.ts +43 -0
- package/dist/db/index.js +3 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/mongodb/index.d.ts +3 -0
- package/dist/db/mongodb/index.js +934 -0
- package/dist/db/mongodb/index.js.map +1 -0
- package/dist/db/mysql/index.d.ts +1 -0
- package/dist/db/mysql/index.js +12 -0
- package/dist/db/mysql/index.js.map +1 -0
- package/dist/db/postgres/index.d.ts +5 -0
- package/dist/db/postgres/index.js +791 -0
- package/dist/db/postgres/index.js.map +1 -0
- package/dist/db/prisma/index.d.ts +1 -0
- package/dist/db/prisma/index.js +12 -0
- package/dist/db/prisma/index.js.map +1 -0
- package/dist/db/sqlite/index.d.ts +5 -0
- package/dist/db/sqlite/index.js +750 -0
- package/dist/db/sqlite/index.js.map +1 -0
- package/dist/express/index.d.ts +1 -0
- package/dist/express/index.js +3 -0
- package/dist/express/index.js.map +1 -0
- package/dist/fastify/index.d.ts +1 -0
- package/dist/fastify/index.js +3 -0
- package/dist/fastify/index.js.map +1 -0
- package/dist/flutterwave/index.d.ts +27 -0
- package/dist/flutterwave/index.js +9 -0
- package/dist/flutterwave/index.js.map +1 -0
- package/dist/index.d.ts +85 -0
- package/dist/index.js +3677 -0
- package/dist/index.js.map +1 -0
- package/dist/next/index.d.ts +1 -0
- package/dist/next/index.js +3 -0
- package/dist/next/index.js.map +1 -0
- package/dist/paystack/index.d.ts +33 -0
- package/dist/paystack/index.js +9 -0
- package/dist/paystack/index.js.map +1 -0
- package/dist/products/index.d.ts +22 -0
- package/dist/products/index.js +7 -0
- package/dist/products/index.js.map +1 -0
- package/dist/testing/index.d.ts +11 -0
- package/dist/testing/index.js +69 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/unified/index.d.ts +41 -0
- package/dist/unified/index.js +6 -0
- package/dist/unified/index.js.map +1 -0
- package/dist/webhooks/index.d.ts +8 -0
- package/dist/webhooks/index.js +8 -0
- package/dist/webhooks/index.js.map +1 -0
- package/package.json +164 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Payweave contributors
|
|
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
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# payweave
|
|
2
|
+
|
|
3
|
+
> **One SDK, every provider — woven together.**
|
|
4
|
+
|
|
5
|
+
A unified, fully-typed TypeScript SDK for **Stripe**, **Paystack**, and **Flutterwave**,
|
|
6
|
+
with a database layer, plans/features, metered usage, and a CLI built in. One config,
|
|
7
|
+
one client, full endpoint coverage, and webhook verification done correctly out of the box.
|
|
8
|
+
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
> **Status:** pre-release.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why Payweave
|
|
19
|
+
|
|
20
|
+
- **One config, any provider.** Pass the provider(s) you want as keys on the config object and get a fully-typed client back.
|
|
21
|
+
- **Compile-time provider narrowing.** Namespaces exist only for the providers you configured — enforced by the type system.
|
|
22
|
+
- **Two surfaces, no lock-in.** Provider-native calls when you want full control; a normalized unified layer when you want portability. Every response carries `raw`, so the abstraction never traps you.
|
|
23
|
+
- **Webhooks as a first-class, security-critical citizen.** Correct signature scheme per provider, raw-body verification, constant-time comparison, fail-closed, auto-detected across every configured provider.
|
|
24
|
+
- **Money that can't drift.** Always integer minor units in the unified layer; the adapters convert so you never fat-finger a decimal.
|
|
25
|
+
- **Typed errors that tell you whose fault it is** — yours (validation/auth), the customer's (declined), or transient (network/5xx).
|
|
26
|
+
- **Billing built in, optional.** Plans, features, metered usage, and a database layer (SQLite, Postgres, MySQL, MongoDB, Drizzle, Prisma) for when you need more than a payment call.
|
|
27
|
+
- **Tiny footprint.** ESM-only, `zod` as the *only* runtime dependency, Node ≥ 20.19.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install payweave
|
|
33
|
+
# or: pnpm add payweave / yarn add payweave / bun add payweave
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
> ESM-only. Requires Node ≥ 20.19 (native `require(esm)` interop); no CJS build is shipped.
|
|
37
|
+
|
|
38
|
+
## Quickstart
|
|
39
|
+
|
|
40
|
+
### 1. Initialize — providers are config keys, narrowed at compile time
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { createPayweave } from "payweave";
|
|
44
|
+
|
|
45
|
+
const payweave = createPayweave({
|
|
46
|
+
paystack: { secretKey: process.env.PAYSTACK_SECRET_KEY! },
|
|
47
|
+
flutterwave: { secretKey: process.env.FLW_SECRET_KEY! }, // v3 by default
|
|
48
|
+
defaultProvider: "paystack",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
payweave.providers; // ["paystack", "flutterwave"]
|
|
52
|
+
payweave.environment; // "test" | "live" — inferred from the key prefixes
|
|
53
|
+
// payweave.stripe; // ❌ compile-time error: not configured
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Test vs live is a **config change, not a code change** — it's inferred from the key prefix
|
|
57
|
+
(`sk_test_` / `sk_live_` for Paystack, `FLWSECK_TEST-` / `FLWSECK-` for Flutterwave v3,
|
|
58
|
+
`sk_test_` / `sk_live_` for Stripe).
|
|
59
|
+
|
|
60
|
+
### 2. Surface A — provider-native, every endpoint 1:1
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// Paystack — amounts in kobo (its native minor units)
|
|
64
|
+
const tx = await payweave.paystack.transactions.initialize({
|
|
65
|
+
email: "ada@example.com",
|
|
66
|
+
amount: 500_000, // ₦5,000
|
|
67
|
+
currency: "NGN",
|
|
68
|
+
});
|
|
69
|
+
console.log(tx.authorization_url);
|
|
70
|
+
|
|
71
|
+
// Flutterwave v3 — amounts in naira (its native major units)
|
|
72
|
+
const link = await payweave.flutterwave.payments.create({
|
|
73
|
+
tx_ref: "order_8123",
|
|
74
|
+
amount: 5000, // ₦5,000
|
|
75
|
+
currency: "NGN",
|
|
76
|
+
redirect_url: "https://app.example.com/pay/callback",
|
|
77
|
+
customer: { email: "ada@example.com" },
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Pagination endpoints expose an async iterator:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
for await (const t of payweave.paystack.transactions.iterate({ perPage: 100 })) {
|
|
85
|
+
// ...
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 3. Surface B — the unified layer (portable, always minor units)
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const checkout = await payweave.checkout.create({
|
|
93
|
+
amount: { value: 500_000, currency: "NGN" }, // ALWAYS minor units; the adapter converts
|
|
94
|
+
customer: { email: "ada@example.com" },
|
|
95
|
+
reference: "order_8123", // → Paystack `reference` / Flutterwave `tx_ref`
|
|
96
|
+
redirectUrl: "https://app.example.com/pay/callback",
|
|
97
|
+
provider: "flutterwave", // optional per-call override of defaultProvider
|
|
98
|
+
});
|
|
99
|
+
// → { checkoutUrl, reference, providerRef, raw }
|
|
100
|
+
|
|
101
|
+
const result = await payweave.verify({ reference: "order_8123" });
|
|
102
|
+
// → { status: "success" | "failed" | "pending" | "abandoned" | "reversed",
|
|
103
|
+
// amount: { value, currency }, customer, paidAt, channel, raw }
|
|
104
|
+
|
|
105
|
+
await payweave.banks.list({ country: "NG" });
|
|
106
|
+
await payweave.banks.resolveAccount({ accountNumber: "0123456789", bankCode: "058" });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`payweave.capabilities()` tells you which unified ops each configured provider supports
|
|
110
|
+
(e.g. Stripe doesn't support `transfers`/`banks` — calling them throws a typed error
|
|
111
|
+
before any request is sent).
|
|
112
|
+
|
|
113
|
+
### 4. Webhooks — verified, typed, normalized, multi-provider
|
|
114
|
+
|
|
115
|
+
Verification **must** run on the exact raw bytes — never parse-then-re-stringify. One
|
|
116
|
+
handler works for every configured provider — the provider is auto-detected from the
|
|
117
|
+
signature header.
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import express from "express";
|
|
121
|
+
|
|
122
|
+
const app = express();
|
|
123
|
+
|
|
124
|
+
// Capture the RAW body for the webhook route only.
|
|
125
|
+
app.post("/webhooks/payments", express.raw({ type: "*/*" }), (req, res) => {
|
|
126
|
+
let event;
|
|
127
|
+
try {
|
|
128
|
+
event = payweave.webhooks.constructEvent({ rawBody: req.body, headers: req.headers });
|
|
129
|
+
} catch (err) {
|
|
130
|
+
return res.sendStatus(400); // bad signature → PayweaveWebhookVerificationError
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
res.sendStatus(200); // ack fast, then process asynchronously
|
|
134
|
+
|
|
135
|
+
// event.type → provider-native name, e.g. "charge.success"
|
|
136
|
+
// event.unifiedType → normalized, e.g. "payment.succeeded"
|
|
137
|
+
// event.dedupeKey → stable idempotency key (providers redeliver)
|
|
138
|
+
switch (event.unifiedType) {
|
|
139
|
+
case "payment.succeeded":
|
|
140
|
+
// Never grant value from the webhook alone — re-verify first:
|
|
141
|
+
// await payweave.verify({ reference }) and check amount + currency + status.
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Signature schemes handled for you: Paystack `HMAC-SHA512` (`x-paystack-signature`),
|
|
148
|
+
Flutterwave v3 `verif-hash`, Flutterwave v4 `HMAC-SHA256` (`flutterwave-signature`),
|
|
149
|
+
Stripe `HMAC-SHA256` (`stripe-signature`). The Flutterwave dashboard **secret hash** is
|
|
150
|
+
not your API key — pass it as `webhookSecret`.
|
|
151
|
+
|
|
152
|
+
## Error handling
|
|
153
|
+
|
|
154
|
+
Every failure is a typed subclass of `PayweaveError`, so you can branch on cause:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import {
|
|
158
|
+
PayweaveValidationError, // 400/422 or local Zod validation (your fault)
|
|
159
|
+
PayweaveAuthError, // 401/403 (bad key)
|
|
160
|
+
PayweaveNotFoundError, // 404 (unknown reference/recipient)
|
|
161
|
+
PayweaveRateLimitError, // 429 (exposes retryAfterMs)
|
|
162
|
+
PayweaveProviderError, // 5xx / provider processing failure
|
|
163
|
+
PayweaveNetworkError, // timeout/DNS/reset (isRetryable = true)
|
|
164
|
+
} from "payweave";
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
await payweave.verify({ reference: "unknown" });
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (err instanceof PayweaveNotFoundError) {
|
|
170
|
+
// handle a genuinely missing transaction
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`error.toJSON()` is always safe to log — secret keys, `Authorization`, PANs, CVV and
|
|
176
|
+
PINs are redacted. Bare `POST`s are **never** auto-retried (a charge is never silently
|
|
177
|
+
re-sent); GETs and idempotency-keyed requests retry with jittered backoff and honor
|
|
178
|
+
`Retry-After`.
|
|
179
|
+
|
|
180
|
+
## Plans, features & metered usage (optional)
|
|
181
|
+
|
|
182
|
+
Configure a `database` adapter and a `products` array to get subscriptions and usage
|
|
183
|
+
tracking without hand-rolling billing state:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import { createPayweave, feature, plan } from "payweave";
|
|
187
|
+
import { sqliteAdapter } from "payweave/db/sqlite";
|
|
188
|
+
|
|
189
|
+
const payweave = createPayweave({
|
|
190
|
+
stripe: { secretKey: process.env.STRIPE_SECRET_KEY! },
|
|
191
|
+
database: sqliteAdapter({ url: "file:./payweave.db" }),
|
|
192
|
+
products: [
|
|
193
|
+
plan("free", { includes: [feature("seats", { type: "boolean" })] }),
|
|
194
|
+
plan("pro", {
|
|
195
|
+
price: { amount: 29, currency: "USD", interval: "month" },
|
|
196
|
+
includes: [
|
|
197
|
+
feature("seats", { type: "boolean" }),
|
|
198
|
+
feature("api-calls", { type: "metered", limit: 10_000, resetInterval: "month" }),
|
|
199
|
+
],
|
|
200
|
+
}),
|
|
201
|
+
],
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
await payweave.sync(); // push plans/prices to Stripe
|
|
205
|
+
await payweave.subscribe({ customerId: "user_1", planId: "pro" });
|
|
206
|
+
await payweave.check({ customerId: "user_1", featureId: "api-calls", consume: true });
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Webhooks keep billing state in sync automatically via `event.apply()` inside
|
|
210
|
+
`constructEvent`. See `payweave/products` and `payweave/db/*` for the full surface.
|
|
211
|
+
|
|
212
|
+
## Testing without the network
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { signWebhook } from "payweave/testing";
|
|
216
|
+
|
|
217
|
+
// Produce a validly-signed body+headers pair for your handler tests:
|
|
218
|
+
const { rawBody, headers } = signWebhook("paystack", payload, webhookSecret);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`payweave/testing` also ships fixture loaders and an MSW server helper so you can test
|
|
222
|
+
your integration entirely offline.
|
|
223
|
+
|
|
224
|
+
## CLI
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npx payweave init # scaffold a payweave.ts config for your framework
|
|
228
|
+
npx payweave push # run migrations, then sync products/plans to your providers
|
|
229
|
+
npx payweave status # validate config, database connectivity, migration status
|
|
230
|
+
npx payweave listen # relay webhooks to your local dev server
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Subpath exports
|
|
234
|
+
|
|
235
|
+
| Import | Contents |
|
|
236
|
+
| --- | --- |
|
|
237
|
+
| `payweave` | Facade: `createPayweave`, `feature`, `plan`, error classes |
|
|
238
|
+
| `payweave/core` | `HttpClient`, errors, `Money`, config, retry, redaction |
|
|
239
|
+
| `payweave/paystack` | Paystack Surface A adapter |
|
|
240
|
+
| `payweave/flutterwave` | Flutterwave Surface A adapter (v3 + v4) |
|
|
241
|
+
| `payweave/unified` | Normalized cross-provider layer + event/status mappings |
|
|
242
|
+
| `payweave/webhooks` | Signature verification + `constructEvent` |
|
|
243
|
+
| `payweave/products` | `feature`, `plan`, period math, billing types |
|
|
244
|
+
| `payweave/db`, `payweave/db/{sqlite,postgres,mysql,mongodb,drizzle,prisma}` | Database adapters |
|
|
245
|
+
| `payweave/testing` | `signWebhook`, fixtures, MSW helpers |
|
|
246
|
+
| `payweave/express`, `payweave/next`, `payweave/fastify` | Framework adapters (in progress) |
|
|
247
|
+
|
|
248
|
+
Every subpath is independently tree-shakeable — importing `payweave/webhooks` in an edge
|
|
249
|
+
function does not pull in the resource modules or any database driver.
|
|
250
|
+
|
|
251
|
+
## Development
|
|
252
|
+
|
|
253
|
+
This package lives in the Payweave Turborepo. Run everything from the repo root:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
pnpm install
|
|
257
|
+
pnpm turbo build --filter=payweave
|
|
258
|
+
pnpm --filter payweave test # vitest + coverage
|
|
259
|
+
pnpm --filter payweave test:types # compile-time type assertions
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
See the root [`AGENTS.md`](../../AGENTS.md) for contribution rules.
|
|
263
|
+
|
|
264
|
+
## Security
|
|
265
|
+
|
|
266
|
+
Webhook verification is timing-safe and fails closed. Secrets never appear in logs, errors,
|
|
267
|
+
or fixtures. Found a vulnerability? Please disclose it privately rather than opening a public
|
|
268
|
+
issue.
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
[MIT](./LICENSE)
|