@siyegs/pay-kit 0.1.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 +150 -0
- package/dist/index.cjs +487 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +196 -0
- package/dist/index.d.ts +196 -0
- package/dist/index.js +457 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
|
|
4
|
+
// src/errors.ts
|
|
5
|
+
function isRetryableError(err) {
|
|
6
|
+
if (!(err instanceof PayKitError)) return false;
|
|
7
|
+
if (err.code === "network_error") return true;
|
|
8
|
+
if (typeof err.statusCode === "number") {
|
|
9
|
+
return err.statusCode >= 500 || err.statusCode === 429;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
var PayKitError = class extends Error {
|
|
14
|
+
code;
|
|
15
|
+
provider;
|
|
16
|
+
statusCode;
|
|
17
|
+
raw;
|
|
18
|
+
constructor(message, options) {
|
|
19
|
+
super(message, options.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
20
|
+
this.name = "PayKitError";
|
|
21
|
+
this.code = options.code;
|
|
22
|
+
this.provider = options.provider;
|
|
23
|
+
this.statusCode = options.statusCode;
|
|
24
|
+
this.raw = options.raw;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/providers/paystack.ts
|
|
29
|
+
import { createHmac } from "crypto";
|
|
30
|
+
|
|
31
|
+
// src/internal.ts
|
|
32
|
+
import { timingSafeEqual } from "crypto";
|
|
33
|
+
async function providerRequest(ctx, provider, url, init) {
|
|
34
|
+
let res;
|
|
35
|
+
try {
|
|
36
|
+
res = await ctx.fetch(url, {
|
|
37
|
+
...init,
|
|
38
|
+
headers: {
|
|
39
|
+
Authorization: `Bearer ${ctx.secretKey}`,
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
Accept: "application/json",
|
|
42
|
+
...init.headers ?? {}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
} catch (err) {
|
|
46
|
+
throw new PayKitError(`Network error calling ${provider}`, {
|
|
47
|
+
code: "network_error",
|
|
48
|
+
provider,
|
|
49
|
+
cause: err
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
let body = {};
|
|
53
|
+
try {
|
|
54
|
+
body = await res.json();
|
|
55
|
+
} catch {
|
|
56
|
+
body = {};
|
|
57
|
+
}
|
|
58
|
+
if (!res.ok || body.status === false || body.status === "error") {
|
|
59
|
+
const message = typeof body.message === "string" ? body.message : `${provider} request failed (${res.status})`;
|
|
60
|
+
throw new PayKitError(message, {
|
|
61
|
+
code: "provider_error",
|
|
62
|
+
provider,
|
|
63
|
+
statusCode: res.status,
|
|
64
|
+
raw: body
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return body;
|
|
68
|
+
}
|
|
69
|
+
function safeEqual(a, b) {
|
|
70
|
+
const ba = Buffer.from(a);
|
|
71
|
+
const bb = Buffer.from(b);
|
|
72
|
+
if (ba.length !== bb.length) return false;
|
|
73
|
+
return timingSafeEqual(ba, bb);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/providers/paystack.ts
|
|
77
|
+
var PAYSTACK_BASE = "https://api.paystack.co";
|
|
78
|
+
function mapStatus(raw) {
|
|
79
|
+
switch (raw) {
|
|
80
|
+
case "success":
|
|
81
|
+
return "success";
|
|
82
|
+
case "failed":
|
|
83
|
+
return "failed";
|
|
84
|
+
case "abandoned":
|
|
85
|
+
return "abandoned";
|
|
86
|
+
default:
|
|
87
|
+
return "pending";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function mapEventType(event) {
|
|
91
|
+
return typeof event === "string" && event.length > 0 ? event : "unknown";
|
|
92
|
+
}
|
|
93
|
+
function mapRefundStatus(raw) {
|
|
94
|
+
switch (raw) {
|
|
95
|
+
case "processed":
|
|
96
|
+
case "success":
|
|
97
|
+
return "processed";
|
|
98
|
+
case "failed":
|
|
99
|
+
return "failed";
|
|
100
|
+
default:
|
|
101
|
+
return "pending";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function createPaystackProvider(ctx) {
|
|
105
|
+
const base = ctx.baseUrl ?? PAYSTACK_BASE;
|
|
106
|
+
return {
|
|
107
|
+
name: "paystack",
|
|
108
|
+
async initialize(params) {
|
|
109
|
+
const reference = params.reference ?? ctx.generateReference();
|
|
110
|
+
const body = await providerRequest(ctx, "paystack", `${base}/transaction/initialize`, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
body: JSON.stringify({
|
|
113
|
+
amount: params.amount,
|
|
114
|
+
email: params.email,
|
|
115
|
+
currency: params.currency ?? "NGN",
|
|
116
|
+
reference,
|
|
117
|
+
callback_url: params.callbackUrl,
|
|
118
|
+
metadata: params.metadata
|
|
119
|
+
})
|
|
120
|
+
});
|
|
121
|
+
const data = body.data ?? {};
|
|
122
|
+
return {
|
|
123
|
+
reference: String(data.reference ?? reference),
|
|
124
|
+
authorizationUrl: String(data.authorization_url ?? ""),
|
|
125
|
+
accessCode: data.access_code ? String(data.access_code) : void 0,
|
|
126
|
+
raw: body
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
async verify(reference) {
|
|
130
|
+
const body = await providerRequest(
|
|
131
|
+
ctx,
|
|
132
|
+
"paystack",
|
|
133
|
+
`${base}/transaction/verify/${encodeURIComponent(reference)}`,
|
|
134
|
+
{ method: "GET" }
|
|
135
|
+
);
|
|
136
|
+
const data = body.data ?? {};
|
|
137
|
+
const customer = data.customer ?? {};
|
|
138
|
+
return {
|
|
139
|
+
reference: String(data.reference ?? reference),
|
|
140
|
+
status: mapStatus(data.status),
|
|
141
|
+
amount: Number(data.amount ?? 0),
|
|
142
|
+
currency: String(data.currency ?? ""),
|
|
143
|
+
paidAt: data.paid_at ? String(data.paid_at) : void 0,
|
|
144
|
+
channel: data.channel ? String(data.channel) : void 0,
|
|
145
|
+
customer: { email: customer.email ? String(customer.email) : void 0 },
|
|
146
|
+
raw: body
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
async refund(reference, options) {
|
|
150
|
+
const body = await providerRequest(ctx, "paystack", `${base}/refund`, {
|
|
151
|
+
method: "POST",
|
|
152
|
+
body: JSON.stringify({
|
|
153
|
+
transaction: reference,
|
|
154
|
+
...options?.amount !== void 0 ? { amount: options.amount } : {}
|
|
155
|
+
})
|
|
156
|
+
});
|
|
157
|
+
const data = body.data ?? {};
|
|
158
|
+
const transaction = data.transaction ?? {};
|
|
159
|
+
return {
|
|
160
|
+
reference: String(transaction.reference ?? reference),
|
|
161
|
+
status: mapRefundStatus(data.status),
|
|
162
|
+
amount: data.amount !== void 0 ? Number(data.amount) : void 0,
|
|
163
|
+
raw: body
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
constructWebhookEvent(rawBody, signature) {
|
|
167
|
+
const expected = createHmac("sha512", ctx.secretKey).update(rawBody).digest("hex");
|
|
168
|
+
if (!signature || !safeEqual(expected, signature)) {
|
|
169
|
+
throw new PayKitError("Invalid Paystack webhook signature", {
|
|
170
|
+
code: "invalid_signature",
|
|
171
|
+
provider: "paystack"
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
let event;
|
|
175
|
+
try {
|
|
176
|
+
event = JSON.parse(rawBody);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
throw new PayKitError("Malformed Paystack webhook body", {
|
|
179
|
+
code: "provider_error",
|
|
180
|
+
provider: "paystack",
|
|
181
|
+
cause: err
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
const data = event.data ?? {};
|
|
185
|
+
return {
|
|
186
|
+
type: mapEventType(event.event),
|
|
187
|
+
reference: String(data.reference ?? ""),
|
|
188
|
+
status: data.status ? mapStatus(data.status) : void 0,
|
|
189
|
+
amount: data.amount !== void 0 ? Number(data.amount) : void 0,
|
|
190
|
+
currency: data.currency ? String(data.currency) : void 0,
|
|
191
|
+
raw: event
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/providers/flutterwave.ts
|
|
198
|
+
var FLUTTERWAVE_BASE = "https://api.flutterwave.com";
|
|
199
|
+
function toMajor(subunits) {
|
|
200
|
+
return subunits / 100;
|
|
201
|
+
}
|
|
202
|
+
function toSubunits(major) {
|
|
203
|
+
return Math.round(Number(major ?? 0) * 100);
|
|
204
|
+
}
|
|
205
|
+
function mapStatus2(raw) {
|
|
206
|
+
switch (raw) {
|
|
207
|
+
case "successful":
|
|
208
|
+
case "success":
|
|
209
|
+
return "success";
|
|
210
|
+
case "failed":
|
|
211
|
+
return "failed";
|
|
212
|
+
default:
|
|
213
|
+
return "pending";
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function mapEventType2(status) {
|
|
217
|
+
if (status === "success") return "charge.success";
|
|
218
|
+
if (status === "failed") return "charge.failed";
|
|
219
|
+
return "unknown";
|
|
220
|
+
}
|
|
221
|
+
function mapRefundStatus2(raw) {
|
|
222
|
+
switch (raw) {
|
|
223
|
+
case "completed":
|
|
224
|
+
case "successful":
|
|
225
|
+
case "success":
|
|
226
|
+
case "processed":
|
|
227
|
+
return "processed";
|
|
228
|
+
case "failed":
|
|
229
|
+
return "failed";
|
|
230
|
+
default:
|
|
231
|
+
return "pending";
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function createFlutterwaveProvider(ctx) {
|
|
235
|
+
const base = ctx.baseUrl ?? FLUTTERWAVE_BASE;
|
|
236
|
+
return {
|
|
237
|
+
name: "flutterwave",
|
|
238
|
+
async initialize(params) {
|
|
239
|
+
const reference = params.reference ?? ctx.generateReference();
|
|
240
|
+
const body = await providerRequest(ctx, "flutterwave", `${base}/v3/payments`, {
|
|
241
|
+
method: "POST",
|
|
242
|
+
body: JSON.stringify({
|
|
243
|
+
tx_ref: reference,
|
|
244
|
+
amount: toMajor(params.amount),
|
|
245
|
+
currency: params.currency ?? "NGN",
|
|
246
|
+
redirect_url: params.callbackUrl,
|
|
247
|
+
customer: { email: params.email },
|
|
248
|
+
meta: params.metadata
|
|
249
|
+
})
|
|
250
|
+
});
|
|
251
|
+
const data = body.data ?? {};
|
|
252
|
+
return {
|
|
253
|
+
reference,
|
|
254
|
+
authorizationUrl: String(data.link ?? ""),
|
|
255
|
+
raw: body
|
|
256
|
+
};
|
|
257
|
+
},
|
|
258
|
+
async verify(reference) {
|
|
259
|
+
const body = await providerRequest(
|
|
260
|
+
ctx,
|
|
261
|
+
"flutterwave",
|
|
262
|
+
`${base}/v3/transactions/verify_by_reference?tx_ref=${encodeURIComponent(reference)}`,
|
|
263
|
+
{ method: "GET" }
|
|
264
|
+
);
|
|
265
|
+
const data = body.data ?? {};
|
|
266
|
+
const customer = data.customer ?? {};
|
|
267
|
+
return {
|
|
268
|
+
reference: String(data.tx_ref ?? reference),
|
|
269
|
+
status: mapStatus2(data.status),
|
|
270
|
+
amount: toSubunits(data.amount),
|
|
271
|
+
currency: String(data.currency ?? ""),
|
|
272
|
+
paidAt: data.created_at ? String(data.created_at) : void 0,
|
|
273
|
+
channel: data.payment_type ? String(data.payment_type) : void 0,
|
|
274
|
+
customer: { email: customer.email ? String(customer.email) : void 0 },
|
|
275
|
+
raw: body
|
|
276
|
+
};
|
|
277
|
+
},
|
|
278
|
+
async refund(reference, options) {
|
|
279
|
+
const verifyBody = await providerRequest(
|
|
280
|
+
ctx,
|
|
281
|
+
"flutterwave",
|
|
282
|
+
`${base}/v3/transactions/verify_by_reference?tx_ref=${encodeURIComponent(reference)}`,
|
|
283
|
+
{ method: "GET" }
|
|
284
|
+
);
|
|
285
|
+
const verifyData = verifyBody.data ?? {};
|
|
286
|
+
const id = verifyData.id;
|
|
287
|
+
if (id === void 0 || id === null) {
|
|
288
|
+
throw new PayKitError(
|
|
289
|
+
`No Flutterwave transaction found for reference "${reference}"`,
|
|
290
|
+
{ code: "provider_error", provider: "flutterwave", raw: verifyBody }
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
const body = await providerRequest(
|
|
294
|
+
ctx,
|
|
295
|
+
"flutterwave",
|
|
296
|
+
`${base}/v3/transactions/${encodeURIComponent(String(id))}/refund`,
|
|
297
|
+
{
|
|
298
|
+
method: "POST",
|
|
299
|
+
body: JSON.stringify(
|
|
300
|
+
options?.amount !== void 0 ? { amount: toMajor(options.amount) } : {}
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
const data = body.data ?? {};
|
|
305
|
+
const refunded = data.amount_refunded ?? data.amount;
|
|
306
|
+
return {
|
|
307
|
+
reference,
|
|
308
|
+
status: mapRefundStatus2(data.status),
|
|
309
|
+
amount: refunded !== void 0 ? toSubunits(refunded) : void 0,
|
|
310
|
+
raw: body
|
|
311
|
+
};
|
|
312
|
+
},
|
|
313
|
+
constructWebhookEvent(rawBody, signature) {
|
|
314
|
+
if (!ctx.webhookSecret) {
|
|
315
|
+
throw new PayKitError(
|
|
316
|
+
"Flutterwave webhook verification requires `webhookSecret` (your Secret hash)",
|
|
317
|
+
{ code: "config_error", provider: "flutterwave" }
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
if (!signature || !safeEqual(ctx.webhookSecret, signature)) {
|
|
321
|
+
throw new PayKitError("Invalid Flutterwave webhook signature", {
|
|
322
|
+
code: "invalid_signature",
|
|
323
|
+
provider: "flutterwave"
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
let event;
|
|
327
|
+
try {
|
|
328
|
+
event = JSON.parse(rawBody);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
throw new PayKitError("Malformed Flutterwave webhook body", {
|
|
331
|
+
code: "provider_error",
|
|
332
|
+
provider: "flutterwave",
|
|
333
|
+
cause: err
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
const data = event.data ?? {};
|
|
337
|
+
const status = mapStatus2(data.status);
|
|
338
|
+
return {
|
|
339
|
+
type: mapEventType2(status),
|
|
340
|
+
reference: String(data.tx_ref ?? ""),
|
|
341
|
+
status,
|
|
342
|
+
amount: data.amount !== void 0 ? toSubunits(data.amount) : void 0,
|
|
343
|
+
currency: data.currency ? String(data.currency) : void 0,
|
|
344
|
+
raw: event
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/client.ts
|
|
351
|
+
function resolveProvider(config, ctx) {
|
|
352
|
+
switch (config.provider) {
|
|
353
|
+
case "paystack":
|
|
354
|
+
return createPaystackProvider(ctx);
|
|
355
|
+
case "flutterwave":
|
|
356
|
+
return createFlutterwaveProvider(ctx);
|
|
357
|
+
default:
|
|
358
|
+
throw new PayKitError(`Unknown provider: ${String(config.provider)}`, {
|
|
359
|
+
code: "config_error"
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
function createPayClient(config) {
|
|
364
|
+
if (!config.secretKey) {
|
|
365
|
+
throw new PayKitError("`secretKey` is required", { code: "config_error" });
|
|
366
|
+
}
|
|
367
|
+
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
368
|
+
if (typeof fetchImpl !== "function") {
|
|
369
|
+
throw new PayKitError(
|
|
370
|
+
"No fetch implementation found. Use Node >= 18 or pass `config.fetch`.",
|
|
371
|
+
{ code: "config_error" }
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
const ctx = {
|
|
375
|
+
secretKey: config.secretKey,
|
|
376
|
+
webhookSecret: config.webhookSecret,
|
|
377
|
+
baseUrl: config.baseUrl,
|
|
378
|
+
fetch: fetchImpl,
|
|
379
|
+
generateReference: config.generateReference ?? (() => `pk_${randomUUID().replace(/-/g, "")}`)
|
|
380
|
+
};
|
|
381
|
+
const provider = resolveProvider(config, ctx);
|
|
382
|
+
return {
|
|
383
|
+
provider: provider.name,
|
|
384
|
+
initialize: (params) => provider.initialize(params),
|
|
385
|
+
verify: (reference) => provider.verify(reference),
|
|
386
|
+
refund: (reference, options) => provider.refund(reference, options),
|
|
387
|
+
webhooks: {
|
|
388
|
+
construct: (rawBody, signature) => provider.constructWebhookEvent(rawBody, signature)
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// src/fallback.ts
|
|
394
|
+
function createFallbackClient(config) {
|
|
395
|
+
if (!config.providers || config.providers.length === 0) {
|
|
396
|
+
throw new PayKitError("createFallbackClient requires at least one provider", {
|
|
397
|
+
code: "config_error"
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
const clients = /* @__PURE__ */ new Map();
|
|
401
|
+
const order = [];
|
|
402
|
+
for (const entry of config.providers) {
|
|
403
|
+
if (clients.has(entry.provider)) continue;
|
|
404
|
+
clients.set(
|
|
405
|
+
entry.provider,
|
|
406
|
+
createPayClient({
|
|
407
|
+
provider: entry.provider,
|
|
408
|
+
secretKey: entry.secretKey,
|
|
409
|
+
webhookSecret: entry.webhookSecret,
|
|
410
|
+
baseUrl: entry.baseUrl,
|
|
411
|
+
fetch: config.fetch,
|
|
412
|
+
generateReference: config.generateReference
|
|
413
|
+
})
|
|
414
|
+
);
|
|
415
|
+
order.push(entry.provider);
|
|
416
|
+
}
|
|
417
|
+
function getClient(provider) {
|
|
418
|
+
const client = clients.get(provider);
|
|
419
|
+
if (!client) {
|
|
420
|
+
throw new PayKitError(
|
|
421
|
+
`Provider "${provider}" is not configured on this fallback client`,
|
|
422
|
+
{ code: "config_error" }
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return client;
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
async initialize(params) {
|
|
429
|
+
let lastError;
|
|
430
|
+
for (const provider of order) {
|
|
431
|
+
try {
|
|
432
|
+
const result = await getClient(provider).initialize(params);
|
|
433
|
+
return { ...result, provider };
|
|
434
|
+
} catch (err) {
|
|
435
|
+
lastError = err;
|
|
436
|
+
if (!isRetryableError(err)) throw err;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
throw lastError ?? new PayKitError("All providers failed to initialize the payment", {
|
|
440
|
+
code: "provider_error"
|
|
441
|
+
});
|
|
442
|
+
},
|
|
443
|
+
verify: (provider, reference) => getClient(provider).verify(reference),
|
|
444
|
+
refund: (provider, reference, options) => getClient(provider).refund(reference, options),
|
|
445
|
+
webhooks: {
|
|
446
|
+
construct: (provider, rawBody, signature) => getClient(provider).webhooks.construct(rawBody, signature)
|
|
447
|
+
},
|
|
448
|
+
client: getClient
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
export {
|
|
452
|
+
PayKitError,
|
|
453
|
+
createFallbackClient,
|
|
454
|
+
createPayClient,
|
|
455
|
+
isRetryableError
|
|
456
|
+
};
|
|
457
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/providers/paystack.ts","../src/internal.ts","../src/providers/flutterwave.ts","../src/fallback.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { PayKitError } from \"./errors\";\nimport { createPaystackProvider } from \"./providers/paystack\";\nimport { createFlutterwaveProvider } from \"./providers/flutterwave\";\nimport type {\n PayClient,\n PayClientConfig,\n PaymentProvider,\n ProviderContext,\n} from \"./types\";\n\nfunction resolveProvider(config: PayClientConfig, ctx: ProviderContext): PaymentProvider {\n switch (config.provider) {\n case \"paystack\":\n return createPaystackProvider(ctx);\n case \"flutterwave\":\n return createFlutterwaveProvider(ctx);\n default:\n throw new PayKitError(`Unknown provider: ${String(config.provider)}`, {\n code: \"config_error\",\n });\n }\n}\n\n/**\n * Create a payment client bound to a single provider.\n *\n * @example\n * const pay = createPayClient({ provider: \"paystack\", secretKey: process.env.PAYSTACK_SECRET_KEY! });\n * const { authorizationUrl, reference } = await pay.initialize({ amount: 500000, email: \"a@b.com\" });\n */\nexport function createPayClient(config: PayClientConfig): PayClient {\n if (!config.secretKey) {\n throw new PayKitError(\"`secretKey` is required\", { code: \"config_error\" });\n }\n\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new PayKitError(\n \"No fetch implementation found. Use Node >= 18 or pass `config.fetch`.\",\n { code: \"config_error\" },\n );\n }\n\n const ctx: ProviderContext = {\n secretKey: config.secretKey,\n webhookSecret: config.webhookSecret,\n baseUrl: config.baseUrl,\n fetch: fetchImpl,\n generateReference:\n config.generateReference ?? (() => `pk_${randomUUID().replace(/-/g, \"\")}`),\n };\n\n const provider = resolveProvider(config, ctx);\n\n return {\n provider: provider.name,\n initialize: (params) => provider.initialize(params),\n verify: (reference) => provider.verify(reference),\n refund: (reference, options) => provider.refund(reference, options),\n webhooks: {\n construct: (rawBody, signature) => provider.constructWebhookEvent(rawBody, signature),\n },\n };\n}\n","export type PayKitErrorCode =\n | \"provider_error\"\n | \"network_error\"\n | \"invalid_signature\"\n | \"config_error\"\n | \"verification_failed\";\n\nexport interface PayKitErrorOptions {\n code: PayKitErrorCode;\n provider?: string;\n statusCode?: number;\n raw?: unknown;\n cause?: unknown;\n}\n\n/**\n * Whether an error is worth retrying on another provider: network failures and\n * outage-like HTTP statuses (5xx, 429). Client errors (4xx) are not retryable -\n * they would fail the same way everywhere.\n */\nexport function isRetryableError(err: unknown): boolean {\n if (!(err instanceof PayKitError)) return false;\n if (err.code === \"network_error\") return true;\n if (typeof err.statusCode === \"number\") {\n return err.statusCode >= 500 || err.statusCode === 429;\n }\n return false;\n}\n\n/** Single error type surfaced by pay-kit, with a machine-readable `code`. */\nexport class PayKitError extends Error {\n readonly code: PayKitErrorCode;\n readonly provider?: string;\n readonly statusCode?: number;\n readonly raw?: unknown;\n\n constructor(message: string, options: PayKitErrorOptions) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = \"PayKitError\";\n this.code = options.code;\n this.provider = options.provider;\n this.statusCode = options.statusCode;\n this.raw = options.raw;\n }\n}\n","import { createHmac } from \"node:crypto\";\nimport { PayKitError } from \"../errors\";\nimport { providerRequest, safeEqual } from \"../internal\";\nimport type {\n InitializeParams,\n InitializeResult,\n PaymentProvider,\n PaymentStatus,\n ProviderContext,\n RefundOptions,\n RefundResult,\n RefundStatus,\n VerifyResult,\n WebhookEvent,\n WebhookEventType,\n} from \"../types\";\n\nconst PAYSTACK_BASE = \"https://api.paystack.co\";\n\n/** Paystack uses subunits (kobo) natively - matches pay-kit's canonical unit. */\nfunction mapStatus(raw: unknown): PaymentStatus {\n switch (raw) {\n case \"success\":\n return \"success\";\n case \"failed\":\n return \"failed\";\n case \"abandoned\":\n return \"abandoned\";\n default:\n return \"pending\";\n }\n}\n\nfunction mapEventType(event: unknown): WebhookEventType {\n return typeof event === \"string\" && event.length > 0 ? event : \"unknown\";\n}\n\nfunction mapRefundStatus(raw: unknown): RefundStatus {\n switch (raw) {\n case \"processed\":\n case \"success\":\n return \"processed\";\n case \"failed\":\n return \"failed\";\n default:\n return \"pending\";\n }\n}\n\nexport function createPaystackProvider(ctx: ProviderContext): PaymentProvider {\n const base = ctx.baseUrl ?? PAYSTACK_BASE;\n\n return {\n name: \"paystack\",\n\n async initialize(params: InitializeParams): Promise<InitializeResult> {\n const reference = params.reference ?? ctx.generateReference();\n const body = await providerRequest(ctx, \"paystack\", `${base}/transaction/initialize`, {\n method: \"POST\",\n body: JSON.stringify({\n amount: params.amount,\n email: params.email,\n currency: params.currency ?? \"NGN\",\n reference,\n callback_url: params.callbackUrl,\n metadata: params.metadata,\n }),\n });\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n return {\n reference: String(data.reference ?? reference),\n authorizationUrl: String(data.authorization_url ?? \"\"),\n accessCode: data.access_code ? String(data.access_code) : undefined,\n raw: body,\n };\n },\n\n async verify(reference: string): Promise<VerifyResult> {\n const body = await providerRequest(\n ctx,\n \"paystack\",\n `${base}/transaction/verify/${encodeURIComponent(reference)}`,\n { method: \"GET\" },\n );\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n const customer = (data.customer ?? {}) as Record<string, unknown>;\n return {\n reference: String(data.reference ?? reference),\n status: mapStatus(data.status),\n amount: Number(data.amount ?? 0),\n currency: String(data.currency ?? \"\"),\n paidAt: data.paid_at ? String(data.paid_at) : undefined,\n channel: data.channel ? String(data.channel) : undefined,\n customer: { email: customer.email ? String(customer.email) : undefined },\n raw: body,\n };\n },\n\n async refund(reference: string, options?: RefundOptions): Promise<RefundResult> {\n const body = await providerRequest(ctx, \"paystack\", `${base}/refund`, {\n method: \"POST\",\n body: JSON.stringify({\n transaction: reference,\n ...(options?.amount !== undefined ? { amount: options.amount } : {}),\n }),\n });\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n const transaction = (data.transaction ?? {}) as Record<string, unknown>;\n return {\n reference: String(transaction.reference ?? reference),\n status: mapRefundStatus(data.status),\n amount: data.amount !== undefined ? Number(data.amount) : undefined,\n raw: body,\n };\n },\n\n constructWebhookEvent(rawBody: string, signature: string): WebhookEvent {\n const expected = createHmac(\"sha512\", ctx.secretKey).update(rawBody).digest(\"hex\");\n if (!signature || !safeEqual(expected, signature)) {\n throw new PayKitError(\"Invalid Paystack webhook signature\", {\n code: \"invalid_signature\",\n provider: \"paystack\",\n });\n }\n\n let event: Record<string, unknown>;\n try {\n event = JSON.parse(rawBody) as Record<string, unknown>;\n } catch (err) {\n throw new PayKitError(\"Malformed Paystack webhook body\", {\n code: \"provider_error\",\n provider: \"paystack\",\n cause: err,\n });\n }\n\n const data = (event.data ?? {}) as Record<string, unknown>;\n return {\n type: mapEventType(event.event),\n reference: String(data.reference ?? \"\"),\n status: data.status ? mapStatus(data.status) : undefined,\n amount: data.amount !== undefined ? Number(data.amount) : undefined,\n currency: data.currency ? String(data.currency) : undefined,\n raw: event,\n };\n },\n };\n}\n","import { timingSafeEqual } from \"node:crypto\";\nimport { PayKitError } from \"./errors\";\nimport type { ProviderContext, ProviderName } from \"./types\";\n\n/**\n * Authenticated JSON request to a provider, with normalized error handling.\n * Both Paystack and Flutterwave use `Authorization: Bearer <secret>` and signal\n * application-level failure in the body (`status: false` / `status: \"error\"`),\n * so we treat those as errors even on HTTP 200.\n */\nexport async function providerRequest(\n ctx: ProviderContext,\n provider: ProviderName,\n url: string,\n init: RequestInit,\n): Promise<Record<string, unknown>> {\n let res: Response;\n try {\n res = await ctx.fetch(url, {\n ...init,\n headers: {\n Authorization: `Bearer ${ctx.secretKey}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...(init.headers ?? {}),\n },\n });\n } catch (err) {\n throw new PayKitError(`Network error calling ${provider}`, {\n code: \"network_error\",\n provider,\n cause: err,\n });\n }\n\n let body: Record<string, unknown> = {};\n try {\n body = (await res.json()) as Record<string, unknown>;\n } catch {\n body = {};\n }\n\n if (!res.ok || body.status === false || body.status === \"error\") {\n const message =\n typeof body.message === \"string\"\n ? body.message\n : `${provider} request failed (${res.status})`;\n throw new PayKitError(message, {\n code: \"provider_error\",\n provider,\n statusCode: res.status,\n raw: body,\n });\n }\n\n return body;\n}\n\n/** Constant-time comparison of two hex/string signatures. */\nexport function safeEqual(a: string, b: string): boolean {\n const ba = Buffer.from(a);\n const bb = Buffer.from(b);\n if (ba.length !== bb.length) return false;\n return timingSafeEqual(ba, bb);\n}\n","import { PayKitError } from \"../errors\";\nimport { providerRequest, safeEqual } from \"../internal\";\nimport type {\n InitializeParams,\n InitializeResult,\n PaymentProvider,\n PaymentStatus,\n ProviderContext,\n RefundOptions,\n RefundResult,\n RefundStatus,\n VerifyResult,\n WebhookEvent,\n WebhookEventType,\n} from \"../types\";\n\nconst FLUTTERWAVE_BASE = \"https://api.flutterwave.com\";\n\n/**\n * Flutterwave works in major currency units (naira, not kobo), so pay-kit\n * converts to/from its canonical subunit representation at the boundary.\n */\nfunction toMajor(subunits: number): number {\n return subunits / 100;\n}\nfunction toSubunits(major: unknown): number {\n return Math.round(Number(major ?? 0) * 100);\n}\n\nfunction mapStatus(raw: unknown): PaymentStatus {\n switch (raw) {\n case \"successful\":\n case \"success\":\n return \"success\";\n case \"failed\":\n return \"failed\";\n default:\n return \"pending\";\n }\n}\n\nfunction mapEventType(status: PaymentStatus): WebhookEventType {\n if (status === \"success\") return \"charge.success\";\n if (status === \"failed\") return \"charge.failed\";\n return \"unknown\";\n}\n\nfunction mapRefundStatus(raw: unknown): RefundStatus {\n switch (raw) {\n case \"completed\":\n case \"successful\":\n case \"success\":\n case \"processed\":\n return \"processed\";\n case \"failed\":\n return \"failed\";\n default:\n return \"pending\";\n }\n}\n\nexport function createFlutterwaveProvider(ctx: ProviderContext): PaymentProvider {\n const base = ctx.baseUrl ?? FLUTTERWAVE_BASE;\n\n return {\n name: \"flutterwave\",\n\n async initialize(params: InitializeParams): Promise<InitializeResult> {\n const reference = params.reference ?? ctx.generateReference();\n const body = await providerRequest(ctx, \"flutterwave\", `${base}/v3/payments`, {\n method: \"POST\",\n body: JSON.stringify({\n tx_ref: reference,\n amount: toMajor(params.amount),\n currency: params.currency ?? \"NGN\",\n redirect_url: params.callbackUrl,\n customer: { email: params.email },\n meta: params.metadata,\n }),\n });\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n return {\n reference,\n authorizationUrl: String(data.link ?? \"\"),\n raw: body,\n };\n },\n\n async verify(reference: string): Promise<VerifyResult> {\n const body = await providerRequest(\n ctx,\n \"flutterwave\",\n `${base}/v3/transactions/verify_by_reference?tx_ref=${encodeURIComponent(reference)}`,\n { method: \"GET\" },\n );\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n const customer = (data.customer ?? {}) as Record<string, unknown>;\n return {\n reference: String(data.tx_ref ?? reference),\n status: mapStatus(data.status),\n amount: toSubunits(data.amount),\n currency: String(data.currency ?? \"\"),\n paidAt: data.created_at ? String(data.created_at) : undefined,\n channel: data.payment_type ? String(data.payment_type) : undefined,\n customer: { email: customer.email ? String(customer.email) : undefined },\n raw: body,\n };\n },\n\n async refund(reference: string, options?: RefundOptions): Promise<RefundResult> {\n // Flutterwave refunds are keyed by the numeric transaction id, not tx_ref,\n // so resolve the id from the reference first.\n const verifyBody = await providerRequest(\n ctx,\n \"flutterwave\",\n `${base}/v3/transactions/verify_by_reference?tx_ref=${encodeURIComponent(reference)}`,\n { method: \"GET\" },\n );\n const verifyData = (verifyBody.data ?? {}) as Record<string, unknown>;\n const id = verifyData.id;\n if (id === undefined || id === null) {\n throw new PayKitError(\n `No Flutterwave transaction found for reference \"${reference}\"`,\n { code: \"provider_error\", provider: \"flutterwave\", raw: verifyBody },\n );\n }\n\n const body = await providerRequest(\n ctx,\n \"flutterwave\",\n `${base}/v3/transactions/${encodeURIComponent(String(id))}/refund`,\n {\n method: \"POST\",\n body: JSON.stringify(\n options?.amount !== undefined ? { amount: toMajor(options.amount) } : {},\n ),\n },\n );\n\n const data = (body.data ?? {}) as Record<string, unknown>;\n const refunded = data.amount_refunded ?? data.amount;\n return {\n reference,\n status: mapRefundStatus(data.status),\n amount: refunded !== undefined ? toSubunits(refunded) : undefined,\n raw: body,\n };\n },\n\n constructWebhookEvent(rawBody: string, signature: string): WebhookEvent {\n // Flutterwave sends the \"Secret hash\" verbatim in the `verif-hash` header.\n if (!ctx.webhookSecret) {\n throw new PayKitError(\n \"Flutterwave webhook verification requires `webhookSecret` (your Secret hash)\",\n { code: \"config_error\", provider: \"flutterwave\" },\n );\n }\n if (!signature || !safeEqual(ctx.webhookSecret, signature)) {\n throw new PayKitError(\"Invalid Flutterwave webhook signature\", {\n code: \"invalid_signature\",\n provider: \"flutterwave\",\n });\n }\n\n let event: Record<string, unknown>;\n try {\n event = JSON.parse(rawBody) as Record<string, unknown>;\n } catch (err) {\n throw new PayKitError(\"Malformed Flutterwave webhook body\", {\n code: \"provider_error\",\n provider: \"flutterwave\",\n cause: err,\n });\n }\n\n const data = (event.data ?? {}) as Record<string, unknown>;\n const status = mapStatus(data.status);\n return {\n type: mapEventType(status),\n reference: String(data.tx_ref ?? \"\"),\n status,\n amount: data.amount !== undefined ? toSubunits(data.amount) : undefined,\n currency: data.currency ? String(data.currency) : undefined,\n raw: event,\n };\n },\n };\n}\n","import { createPayClient } from \"./client\";\nimport { PayKitError, isRetryableError } from \"./errors\";\nimport type {\n FallbackClient,\n FallbackClientConfig,\n FallbackInitializeResult,\n InitializeParams,\n PayClient,\n ProviderName,\n} from \"./types\";\n\n/**\n * Create a resilient client that tries multiple providers in order.\n *\n * `initialize` falls through to the next provider on outage-like failures\n * (network errors, HTTP 5xx/429) and returns the provider that succeeded.\n * Route `verify`/`refund`/`webhooks` for that transaction back to the same\n * provider - a charge started on Paystack can only be verified on Paystack.\n *\n * @example\n * const pay = createFallbackClient({\n * providers: [\n * { provider: \"paystack\", secretKey: process.env.PAYSTACK_SECRET_KEY! },\n * { provider: \"flutterwave\", secretKey: process.env.FLW_SECRET_KEY! },\n * ],\n * });\n * const { reference, provider } = await pay.initialize({ amount: 500000, email: \"a@b.com\" });\n * // persist { reference, provider }, then later:\n * const result = await pay.verify(provider, reference);\n */\nexport function createFallbackClient(config: FallbackClientConfig): FallbackClient {\n if (!config.providers || config.providers.length === 0) {\n throw new PayKitError(\"createFallbackClient requires at least one provider\", {\n code: \"config_error\",\n });\n }\n\n const clients = new Map<ProviderName, PayClient>();\n const order: ProviderName[] = [];\n\n for (const entry of config.providers) {\n if (clients.has(entry.provider)) continue; // ignore duplicates, keep first\n clients.set(\n entry.provider,\n createPayClient({\n provider: entry.provider,\n secretKey: entry.secretKey,\n webhookSecret: entry.webhookSecret,\n baseUrl: entry.baseUrl,\n fetch: config.fetch,\n generateReference: config.generateReference,\n }),\n );\n order.push(entry.provider);\n }\n\n function getClient(provider: ProviderName): PayClient {\n const client = clients.get(provider);\n if (!client) {\n throw new PayKitError(\n `Provider \"${provider}\" is not configured on this fallback client`,\n { code: \"config_error\" },\n );\n }\n return client;\n }\n\n return {\n async initialize(params: InitializeParams): Promise<FallbackInitializeResult> {\n let lastError: unknown;\n for (const provider of order) {\n try {\n const result = await getClient(provider).initialize(params);\n return { ...result, provider };\n } catch (err) {\n lastError = err;\n // A client error (4xx) would fail the same way everywhere - stop.\n if (!isRetryableError(err)) throw err;\n // Otherwise fall through to the next provider.\n }\n }\n throw (\n lastError ??\n new PayKitError(\"All providers failed to initialize the payment\", {\n code: \"provider_error\",\n })\n );\n },\n\n verify: (provider, reference) => getClient(provider).verify(reference),\n\n refund: (provider, reference, options) => getClient(provider).refund(reference, options),\n\n webhooks: {\n construct: (provider, rawBody, signature) =>\n getClient(provider).webhooks.construct(rawBody, signature),\n },\n\n client: getClient,\n };\n}\n"],"mappings":"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","names":["mapStatus","mapEventType","mapRefundStatus"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@siyegs/pay-kit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "One typed SDK for African payment rails - unified Paystack & Flutterwave: initialize, verify, refund, and signature-verified webhooks, with provider fallback.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/siyegs/pay-kit.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/siyegs/pay-kit#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/siyegs/pay-kit/issues"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.cjs",
|
|
16
|
+
"module": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"require": "./dist/index.cjs"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"dev": "tsup --watch",
|
|
33
|
+
"test": "bun test",
|
|
34
|
+
"test:watch": "bun test --watch",
|
|
35
|
+
"typecheck": "tsc --noEmit",
|
|
36
|
+
"prepublishOnly": "bun run typecheck && bun run test && bun run build"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"paystack",
|
|
40
|
+
"flutterwave",
|
|
41
|
+
"payments",
|
|
42
|
+
"payment-gateway",
|
|
43
|
+
"nigeria",
|
|
44
|
+
"africa",
|
|
45
|
+
"fintech",
|
|
46
|
+
"typescript",
|
|
47
|
+
"sdk",
|
|
48
|
+
"webhooks"
|
|
49
|
+
],
|
|
50
|
+
"author": "Iyegere Success Karboloo",
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/bun": "^1.1.0",
|
|
60
|
+
"tsup": "^8.1.0",
|
|
61
|
+
"typescript": "^5.4.5"
|
|
62
|
+
}
|
|
63
|
+
}
|