@paykit-sdk/paystack 1.2.0 → 1.3.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/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # @paykit-sdk/paystack
2
+
3
+ Paystack provider for PayKit.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @paykit-sdk/paystack
9
+ # or
10
+ pnpm add @paykit-sdk/paystack
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { createEndpointHandlers, PayKit } from '@paykit-sdk/core';
17
+ import { paystack } from '@paykit-sdk/paystack';
18
+
19
+ export const paykit = new PayKit(paystack());
20
+ export const endpoints = createEndpointHandlers(paykit);
21
+ ```
22
+
23
+ Or with direct config:
24
+
25
+ ```typescript
26
+ import { PayKit } from '@paykit-sdk/core';
27
+ import { createPaystack } from '@paykit-sdk/paystack';
28
+
29
+ export const paykit = new PayKit(
30
+ createPaystack({
31
+ secretKey: 'sk_test_...',
32
+ isSandbox: true,
33
+ }),
34
+ );
35
+ ```
36
+
37
+ ## Environment Variables
38
+
39
+ ```bash
40
+ PAYSTACK_SECRET_KEY=sk_test_...
41
+ ```
42
+
43
+ `isSandbox` is inferred from `NODE_ENV` — set `NODE_ENV=production` for live mode.
44
+
45
+ ## Next.js API Route
46
+
47
+ ```typescript
48
+ // app/api/paykit/[...endpoint]/route.ts
49
+ import { endpoints } from '@/lib/paykit';
50
+ import { EndpointPath } from '@paykit-sdk/core';
51
+ import { NextRequest, NextResponse } from 'next/server';
52
+
53
+ export async function POST(
54
+ request: NextRequest,
55
+ { params }: { params: Promise<{ endpoint: string[] }> },
56
+ ) {
57
+ const { endpoint: endpointArray } = await params;
58
+ const endpoint = ('/' + endpointArray.join('/')) as EndpointPath;
59
+ const handler = endpoints[endpoint];
60
+
61
+ if (!handler) {
62
+ return NextResponse.json(
63
+ { message: 'Not found' },
64
+ { status: 404 },
65
+ );
66
+ }
67
+
68
+ const { args } = await request.json();
69
+ const result = await handler(...args);
70
+ return NextResponse.json({ result });
71
+ }
72
+ ```
73
+
74
+ ## Webhooks
75
+
76
+ Paystack sends signed POST requests. Pass your webhook secret to verify signatures.
77
+
78
+ ```typescript
79
+ // app/api/paykit/webhooks/route.ts
80
+ import { paykit } from '@/lib/paykit';
81
+ import { NextRequest, NextResponse } from 'next/server';
82
+
83
+ export async function POST(request: NextRequest) {
84
+ const body = await request.text();
85
+
86
+ const webhook = paykit.webhooks
87
+ .setup({ webhookSecret: process.env.PAYSTACK_SECRET_KEY! })
88
+ .on('payment.created', async event => {
89
+ /* charge.success — pending authorization */
90
+ })
91
+ .on('payment.updated', async event => {
92
+ /* charge.dispute.create — pre-auth hold updated */
93
+ })
94
+ .on('payment.failed', async event => {
95
+ /* charge.failed */
96
+ })
97
+ .on('invoice.generated', async event => {
98
+ /* invoice.payment_failed or invoice.update */
99
+ })
100
+ .on('customer.created', async event => {
101
+ /* customeridentification.success */
102
+ })
103
+ .on('customer.updated', async event => {
104
+ /* customeridentification.failed */
105
+ })
106
+ .on('subscription.created', async event => {
107
+ /* subscription.create */
108
+ })
109
+ .on('subscription.canceled', async event => {
110
+ /* subscription.disable */
111
+ });
112
+
113
+ await webhook.handle({
114
+ body,
115
+ headersAsObject: Object.fromEntries(request.headers),
116
+ fullUrl: request.url,
117
+ });
118
+
119
+ return NextResponse.json({ received: true });
120
+ }
121
+ ```
122
+
123
+ ## Checkout
124
+
125
+ Paystack requires an email customer and amount/currency in `provider_metadata`:
126
+
127
+ ```typescript
128
+ const checkout = await paykit.checkouts.create({
129
+ customer: { email: 'user@example.com' },
130
+ item_id: 'plan_pro',
131
+ session_type: 'one_time',
132
+ quantity: 1,
133
+ success_url: 'https://example.com/success',
134
+ provider_metadata: {
135
+ amount: 50000, // in kobo (NGN) or smallest currency unit
136
+ currency: 'NGN',
137
+ },
138
+ });
139
+
140
+ // Redirect user to checkout.payment_url
141
+ ```
142
+
143
+ ## Documentation
144
+
145
+ Full docs at [docs.usepaykit.com/providers/paystack](https://docs.usepaykit.com/providers/paystack).
146
+
147
+ ## Support
148
+
149
+ - [Paystack Documentation](https://paystack.com/docs)
150
+ - [PayKit Issues](https://github.com/usepaykit/paykit-sdk/issues)
151
+
152
+ ## License
153
+
154
+ ISC
package/dist/index.js CHANGED
@@ -4086,11 +4086,17 @@ var Payment$inboundSchema = (data, overridePaymentUrl) => {
4086
4086
  itemId = paykitMeta.item_id ?? null;
4087
4087
  }
4088
4088
  const status = paystackStatusMap[data.status] ?? "pending";
4089
+ let customer = null;
4090
+ if (data.customer?.email) {
4091
+ customer = { email: data.customer.email };
4092
+ } else if (data.customer?.id) {
4093
+ customer = { id: data.customer.id };
4094
+ }
4089
4095
  return {
4090
4096
  id: data.reference,
4091
4097
  amount: data.amount,
4092
4098
  currency: data.currency,
4093
- customer: data.customer?.email ? { email: data.customer.email } : null,
4099
+ customer,
4094
4100
  status,
4095
4101
  metadata,
4096
4102
  item_id: itemId,
@@ -4115,24 +4121,20 @@ var Checkout$inboundSchema = (init, transaction) => {
4115
4121
  type: core.billingModeSchema.optional()
4116
4122
  })
4117
4123
  );
4118
- if (paykitMeta && typeof paykitMeta === "string") {
4119
- const parsed = core.parseJSON(
4120
- paykitMeta,
4121
- core.Schema.object({
4122
- item_id: core.Schema.string().optional(),
4123
- quantity: core.Schema.number().optional(),
4124
- type: core.billingModeSchema.optional()
4125
- })
4126
- );
4127
- if (parsed) {
4128
- itemId = parsed.item_id ?? "";
4129
- quantity = parsed.quantity ?? 1;
4130
- type = parsed.type ?? null;
4131
- }
4124
+ if (paykitMeta) {
4125
+ itemId = paykitMeta.item_id ?? "";
4126
+ quantity = paykitMeta.quantity ?? 1;
4127
+ type = paykitMeta.type ?? null;
4128
+ }
4129
+ let customer = null;
4130
+ if (transaction.customer?.email) {
4131
+ customer = { email: transaction.customer.email };
4132
+ } else if (transaction.customer?.id) {
4133
+ customer = { id: transaction.customer.id };
4132
4134
  }
4133
4135
  return {
4134
4136
  id: init.reference,
4135
- customer: transaction.customer?.email ? { email: transaction.customer.email } : null,
4137
+ customer,
4136
4138
  payment_url: init.authorization_url,
4137
4139
  metadata: Object.keys(metadata).length > 0 ? metadata : null,
4138
4140
  session_type: type ?? "one_time",
@@ -4281,7 +4283,11 @@ var PaystackProvider = class extends core.AbstractPayKitProvider {
4281
4283
  { provider: this.providerName, method: "createCheckout" }
4282
4284
  );
4283
4285
  }
4284
- const { amount, currency = "NGN" } = data.provider_metadata ?? {};
4286
+ const { amount, currency = "NGN" } = core.validateRequiredKeys(
4287
+ ["amount", "currency"],
4288
+ data.provider_metadata ?? {},
4289
+ "Missing required provider metadata: {keys}"
4290
+ );
4285
4291
  const metadata = {
4286
4292
  ...core.stringifyMetadataValues(data.metadata ?? {}),
4287
4293
  [core.PAYKIT_METADATA_KEY]: JSON.stringify({
@@ -4292,8 +4298,8 @@ var PaystackProvider = class extends core.AbstractPayKitProvider {
4292
4298
  };
4293
4299
  const body = {
4294
4300
  email: data.customer.email,
4295
- amount: amount ?? 0,
4296
- currency,
4301
+ amount: parseInt(amount, 10),
4302
+ currency: currency.toUpperCase(),
4297
4303
  reference: crypto.randomUUID(),
4298
4304
  callback_url: data.success_url,
4299
4305
  metadata: core.stringifyMetadataValues(metadata),
@@ -4605,7 +4611,9 @@ var PaystackProvider = class extends core.AbstractPayKitProvider {
4605
4611
  });
4606
4612
  }
4607
4613
  const expectedSignature = crypto$1.createHmac("sha512", webhookSecret).update(body).digest("hex");
4608
- if (expectedSignature !== signature) {
4614
+ const expectedBuf = Buffer.from(expectedSignature, "hex");
4615
+ const receivedBuf = Buffer.from(signature, "hex");
4616
+ if (expectedBuf.length !== receivedBuf.length || !crypto$1.timingSafeEqual(expectedBuf, receivedBuf)) {
4609
4617
  throw new core.WebhookError("Invalid Paystack webhook signature", {
4610
4618
  provider: this.providerName
4611
4619
  });
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { schema, AbstractPayKitProvider, HTTPClient, OperationFailedError, createCheckoutSchema, ValidationError, isEmailCustomer, InvalidTypeError, PAYKIT_METADATA_KEY, stringifyMetadataValues, ProviderNotSupportedError, createCustomerSchema, parseCustomerName, isIdCustomer, ResourceNotFoundError, createPaymentSchema, createRefundSchema, WebhookError, paykitEvent$InboundSchema, validateRequiredKeys, parseJSON, Schema, omitInternalMetadata, billingModeSchema } from '@paykit-sdk/core';
2
- import { createHmac } from 'crypto';
1
+ import { schema, AbstractPayKitProvider, HTTPClient, OperationFailedError, createCheckoutSchema, ValidationError, isEmailCustomer, InvalidTypeError, validateRequiredKeys, PAYKIT_METADATA_KEY, stringifyMetadataValues, ProviderNotSupportedError, createCustomerSchema, parseCustomerName, isIdCustomer, ResourceNotFoundError, createPaymentSchema, createRefundSchema, WebhookError, paykitEvent$InboundSchema, parseJSON, Schema, omitInternalMetadata, billingModeSchema } from '@paykit-sdk/core';
2
+ import { createHmac, timingSafeEqual } from 'crypto';
3
3
 
4
4
  var __defProp = Object.defineProperty;
5
5
  var __export = (target, all) => {
@@ -4084,11 +4084,17 @@ var Payment$inboundSchema = (data, overridePaymentUrl) => {
4084
4084
  itemId = paykitMeta.item_id ?? null;
4085
4085
  }
4086
4086
  const status = paystackStatusMap[data.status] ?? "pending";
4087
+ let customer = null;
4088
+ if (data.customer?.email) {
4089
+ customer = { email: data.customer.email };
4090
+ } else if (data.customer?.id) {
4091
+ customer = { id: data.customer.id };
4092
+ }
4087
4093
  return {
4088
4094
  id: data.reference,
4089
4095
  amount: data.amount,
4090
4096
  currency: data.currency,
4091
- customer: data.customer?.email ? { email: data.customer.email } : null,
4097
+ customer,
4092
4098
  status,
4093
4099
  metadata,
4094
4100
  item_id: itemId,
@@ -4113,24 +4119,20 @@ var Checkout$inboundSchema = (init, transaction) => {
4113
4119
  type: billingModeSchema.optional()
4114
4120
  })
4115
4121
  );
4116
- if (paykitMeta && typeof paykitMeta === "string") {
4117
- const parsed = parseJSON(
4118
- paykitMeta,
4119
- Schema.object({
4120
- item_id: Schema.string().optional(),
4121
- quantity: Schema.number().optional(),
4122
- type: billingModeSchema.optional()
4123
- })
4124
- );
4125
- if (parsed) {
4126
- itemId = parsed.item_id ?? "";
4127
- quantity = parsed.quantity ?? 1;
4128
- type = parsed.type ?? null;
4129
- }
4122
+ if (paykitMeta) {
4123
+ itemId = paykitMeta.item_id ?? "";
4124
+ quantity = paykitMeta.quantity ?? 1;
4125
+ type = paykitMeta.type ?? null;
4126
+ }
4127
+ let customer = null;
4128
+ if (transaction.customer?.email) {
4129
+ customer = { email: transaction.customer.email };
4130
+ } else if (transaction.customer?.id) {
4131
+ customer = { id: transaction.customer.id };
4130
4132
  }
4131
4133
  return {
4132
4134
  id: init.reference,
4133
- customer: transaction.customer?.email ? { email: transaction.customer.email } : null,
4135
+ customer,
4134
4136
  payment_url: init.authorization_url,
4135
4137
  metadata: Object.keys(metadata).length > 0 ? metadata : null,
4136
4138
  session_type: type ?? "one_time",
@@ -4279,7 +4281,11 @@ var PaystackProvider = class extends AbstractPayKitProvider {
4279
4281
  { provider: this.providerName, method: "createCheckout" }
4280
4282
  );
4281
4283
  }
4282
- const { amount, currency = "NGN" } = data.provider_metadata ?? {};
4284
+ const { amount, currency = "NGN" } = validateRequiredKeys(
4285
+ ["amount", "currency"],
4286
+ data.provider_metadata ?? {},
4287
+ "Missing required provider metadata: {keys}"
4288
+ );
4283
4289
  const metadata = {
4284
4290
  ...stringifyMetadataValues(data.metadata ?? {}),
4285
4291
  [PAYKIT_METADATA_KEY]: JSON.stringify({
@@ -4290,8 +4296,8 @@ var PaystackProvider = class extends AbstractPayKitProvider {
4290
4296
  };
4291
4297
  const body = {
4292
4298
  email: data.customer.email,
4293
- amount: amount ?? 0,
4294
- currency,
4299
+ amount: parseInt(amount, 10),
4300
+ currency: currency.toUpperCase(),
4295
4301
  reference: crypto.randomUUID(),
4296
4302
  callback_url: data.success_url,
4297
4303
  metadata: stringifyMetadataValues(metadata),
@@ -4603,7 +4609,9 @@ var PaystackProvider = class extends AbstractPayKitProvider {
4603
4609
  });
4604
4610
  }
4605
4611
  const expectedSignature = createHmac("sha512", webhookSecret).update(body).digest("hex");
4606
- if (expectedSignature !== signature) {
4612
+ const expectedBuf = Buffer.from(expectedSignature, "hex");
4613
+ const receivedBuf = Buffer.from(signature, "hex");
4614
+ if (expectedBuf.length !== receivedBuf.length || !timingSafeEqual(expectedBuf, receivedBuf)) {
4607
4615
  throw new WebhookError("Invalid Paystack webhook signature", {
4608
4616
  provider: this.providerName
4609
4617
  });
@@ -4086,11 +4086,17 @@ var Payment$inboundSchema = (data, overridePaymentUrl) => {
4086
4086
  itemId = paykitMeta.item_id ?? null;
4087
4087
  }
4088
4088
  const status = paystackStatusMap[data.status] ?? "pending";
4089
+ let customer = null;
4090
+ if (data.customer?.email) {
4091
+ customer = { email: data.customer.email };
4092
+ } else if (data.customer?.id) {
4093
+ customer = { id: data.customer.id };
4094
+ }
4089
4095
  return {
4090
4096
  id: data.reference,
4091
4097
  amount: data.amount,
4092
4098
  currency: data.currency,
4093
- customer: data.customer?.email ? { email: data.customer.email } : null,
4099
+ customer,
4094
4100
  status,
4095
4101
  metadata,
4096
4102
  item_id: itemId,
@@ -4115,24 +4121,20 @@ var Checkout$inboundSchema = (init, transaction) => {
4115
4121
  type: core.billingModeSchema.optional()
4116
4122
  })
4117
4123
  );
4118
- if (paykitMeta && typeof paykitMeta === "string") {
4119
- const parsed = core.parseJSON(
4120
- paykitMeta,
4121
- core.Schema.object({
4122
- item_id: core.Schema.string().optional(),
4123
- quantity: core.Schema.number().optional(),
4124
- type: core.billingModeSchema.optional()
4125
- })
4126
- );
4127
- if (parsed) {
4128
- itemId = parsed.item_id ?? "";
4129
- quantity = parsed.quantity ?? 1;
4130
- type = parsed.type ?? null;
4131
- }
4124
+ if (paykitMeta) {
4125
+ itemId = paykitMeta.item_id ?? "";
4126
+ quantity = paykitMeta.quantity ?? 1;
4127
+ type = paykitMeta.type ?? null;
4128
+ }
4129
+ let customer = null;
4130
+ if (transaction.customer?.email) {
4131
+ customer = { email: transaction.customer.email };
4132
+ } else if (transaction.customer?.id) {
4133
+ customer = { id: transaction.customer.id };
4132
4134
  }
4133
4135
  return {
4134
4136
  id: init.reference,
4135
- customer: transaction.customer?.email ? { email: transaction.customer.email } : null,
4137
+ customer,
4136
4138
  payment_url: init.authorization_url,
4137
4139
  metadata: Object.keys(metadata).length > 0 ? metadata : null,
4138
4140
  session_type: type ?? "one_time",
@@ -4281,7 +4283,11 @@ var PaystackProvider = class extends core.AbstractPayKitProvider {
4281
4283
  { provider: this.providerName, method: "createCheckout" }
4282
4284
  );
4283
4285
  }
4284
- const { amount, currency = "NGN" } = data.provider_metadata ?? {};
4286
+ const { amount, currency = "NGN" } = core.validateRequiredKeys(
4287
+ ["amount", "currency"],
4288
+ data.provider_metadata ?? {},
4289
+ "Missing required provider metadata: {keys}"
4290
+ );
4285
4291
  const metadata = {
4286
4292
  ...core.stringifyMetadataValues(data.metadata ?? {}),
4287
4293
  [core.PAYKIT_METADATA_KEY]: JSON.stringify({
@@ -4292,8 +4298,8 @@ var PaystackProvider = class extends core.AbstractPayKitProvider {
4292
4298
  };
4293
4299
  const body = {
4294
4300
  email: data.customer.email,
4295
- amount: amount ?? 0,
4296
- currency,
4301
+ amount: parseInt(amount, 10),
4302
+ currency: currency.toUpperCase(),
4297
4303
  reference: crypto.randomUUID(),
4298
4304
  callback_url: data.success_url,
4299
4305
  metadata: core.stringifyMetadataValues(metadata),
@@ -4605,7 +4611,9 @@ var PaystackProvider = class extends core.AbstractPayKitProvider {
4605
4611
  });
4606
4612
  }
4607
4613
  const expectedSignature = crypto$1.createHmac("sha512", webhookSecret).update(body).digest("hex");
4608
- if (expectedSignature !== signature) {
4614
+ const expectedBuf = Buffer.from(expectedSignature, "hex");
4615
+ const receivedBuf = Buffer.from(signature, "hex");
4616
+ if (expectedBuf.length !== receivedBuf.length || !crypto$1.timingSafeEqual(expectedBuf, receivedBuf)) {
4609
4617
  throw new core.WebhookError("Invalid Paystack webhook signature", {
4610
4618
  provider: this.providerName
4611
4619
  });
@@ -1,5 +1,5 @@
1
- import { schema, AbstractPayKitProvider, HTTPClient, OperationFailedError, createCheckoutSchema, ValidationError, isEmailCustomer, InvalidTypeError, PAYKIT_METADATA_KEY, stringifyMetadataValues, ProviderNotSupportedError, createCustomerSchema, parseCustomerName, isIdCustomer, ResourceNotFoundError, createPaymentSchema, createRefundSchema, WebhookError, paykitEvent$InboundSchema, parseJSON, Schema, omitInternalMetadata, billingModeSchema } from '@paykit-sdk/core';
2
- import { createHmac } from 'crypto';
1
+ import { schema, AbstractPayKitProvider, HTTPClient, OperationFailedError, createCheckoutSchema, ValidationError, isEmailCustomer, InvalidTypeError, validateRequiredKeys, PAYKIT_METADATA_KEY, stringifyMetadataValues, ProviderNotSupportedError, createCustomerSchema, parseCustomerName, isIdCustomer, ResourceNotFoundError, createPaymentSchema, createRefundSchema, WebhookError, paykitEvent$InboundSchema, parseJSON, Schema, omitInternalMetadata, billingModeSchema } from '@paykit-sdk/core';
2
+ import { createHmac, timingSafeEqual } from 'crypto';
3
3
 
4
4
  var __defProp = Object.defineProperty;
5
5
  var __export = (target, all) => {
@@ -4084,11 +4084,17 @@ var Payment$inboundSchema = (data, overridePaymentUrl) => {
4084
4084
  itemId = paykitMeta.item_id ?? null;
4085
4085
  }
4086
4086
  const status = paystackStatusMap[data.status] ?? "pending";
4087
+ let customer = null;
4088
+ if (data.customer?.email) {
4089
+ customer = { email: data.customer.email };
4090
+ } else if (data.customer?.id) {
4091
+ customer = { id: data.customer.id };
4092
+ }
4087
4093
  return {
4088
4094
  id: data.reference,
4089
4095
  amount: data.amount,
4090
4096
  currency: data.currency,
4091
- customer: data.customer?.email ? { email: data.customer.email } : null,
4097
+ customer,
4092
4098
  status,
4093
4099
  metadata,
4094
4100
  item_id: itemId,
@@ -4113,24 +4119,20 @@ var Checkout$inboundSchema = (init, transaction) => {
4113
4119
  type: billingModeSchema.optional()
4114
4120
  })
4115
4121
  );
4116
- if (paykitMeta && typeof paykitMeta === "string") {
4117
- const parsed = parseJSON(
4118
- paykitMeta,
4119
- Schema.object({
4120
- item_id: Schema.string().optional(),
4121
- quantity: Schema.number().optional(),
4122
- type: billingModeSchema.optional()
4123
- })
4124
- );
4125
- if (parsed) {
4126
- itemId = parsed.item_id ?? "";
4127
- quantity = parsed.quantity ?? 1;
4128
- type = parsed.type ?? null;
4129
- }
4122
+ if (paykitMeta) {
4123
+ itemId = paykitMeta.item_id ?? "";
4124
+ quantity = paykitMeta.quantity ?? 1;
4125
+ type = paykitMeta.type ?? null;
4126
+ }
4127
+ let customer = null;
4128
+ if (transaction.customer?.email) {
4129
+ customer = { email: transaction.customer.email };
4130
+ } else if (transaction.customer?.id) {
4131
+ customer = { id: transaction.customer.id };
4130
4132
  }
4131
4133
  return {
4132
4134
  id: init.reference,
4133
- customer: transaction.customer?.email ? { email: transaction.customer.email } : null,
4135
+ customer,
4134
4136
  payment_url: init.authorization_url,
4135
4137
  metadata: Object.keys(metadata).length > 0 ? metadata : null,
4136
4138
  session_type: type ?? "one_time",
@@ -4279,7 +4281,11 @@ var PaystackProvider = class extends AbstractPayKitProvider {
4279
4281
  { provider: this.providerName, method: "createCheckout" }
4280
4282
  );
4281
4283
  }
4282
- const { amount, currency = "NGN" } = data.provider_metadata ?? {};
4284
+ const { amount, currency = "NGN" } = validateRequiredKeys(
4285
+ ["amount", "currency"],
4286
+ data.provider_metadata ?? {},
4287
+ "Missing required provider metadata: {keys}"
4288
+ );
4283
4289
  const metadata = {
4284
4290
  ...stringifyMetadataValues(data.metadata ?? {}),
4285
4291
  [PAYKIT_METADATA_KEY]: JSON.stringify({
@@ -4290,8 +4296,8 @@ var PaystackProvider = class extends AbstractPayKitProvider {
4290
4296
  };
4291
4297
  const body = {
4292
4298
  email: data.customer.email,
4293
- amount: amount ?? 0,
4294
- currency,
4299
+ amount: parseInt(amount, 10),
4300
+ currency: currency.toUpperCase(),
4295
4301
  reference: crypto.randomUUID(),
4296
4302
  callback_url: data.success_url,
4297
4303
  metadata: stringifyMetadataValues(metadata),
@@ -4603,7 +4609,9 @@ var PaystackProvider = class extends AbstractPayKitProvider {
4603
4609
  });
4604
4610
  }
4605
4611
  const expectedSignature = createHmac("sha512", webhookSecret).update(body).digest("hex");
4606
- if (expectedSignature !== signature) {
4612
+ const expectedBuf = Buffer.from(expectedSignature, "hex");
4613
+ const receivedBuf = Buffer.from(signature, "hex");
4614
+ if (expectedBuf.length !== receivedBuf.length || !timingSafeEqual(expectedBuf, receivedBuf)) {
4607
4615
  throw new WebhookError("Invalid Paystack webhook signature", {
4608
4616
  provider: this.providerName
4609
4617
  });
@@ -41,11 +41,17 @@ var Payment$inboundSchema = (data, overridePaymentUrl) => {
41
41
  itemId = paykitMeta.item_id ?? null;
42
42
  }
43
43
  const status = paystackStatusMap[data.status] ?? "pending";
44
+ let customer = null;
45
+ if (data.customer?.email) {
46
+ customer = { email: data.customer.email };
47
+ } else if (data.customer?.id) {
48
+ customer = { id: data.customer.id };
49
+ }
44
50
  return {
45
51
  id: data.reference,
46
52
  amount: data.amount,
47
53
  currency: data.currency,
48
- customer: data.customer?.email ? { email: data.customer.email } : null,
54
+ customer,
49
55
  status,
50
56
  metadata,
51
57
  item_id: itemId,
@@ -70,24 +76,20 @@ var Checkout$inboundSchema = (init, transaction) => {
70
76
  type: core.billingModeSchema.optional()
71
77
  })
72
78
  );
73
- if (paykitMeta && typeof paykitMeta === "string") {
74
- const parsed = core.parseJSON(
75
- paykitMeta,
76
- core.Schema.object({
77
- item_id: core.Schema.string().optional(),
78
- quantity: core.Schema.number().optional(),
79
- type: core.billingModeSchema.optional()
80
- })
81
- );
82
- if (parsed) {
83
- itemId = parsed.item_id ?? "";
84
- quantity = parsed.quantity ?? 1;
85
- type = parsed.type ?? null;
86
- }
79
+ if (paykitMeta) {
80
+ itemId = paykitMeta.item_id ?? "";
81
+ quantity = paykitMeta.quantity ?? 1;
82
+ type = paykitMeta.type ?? null;
83
+ }
84
+ let customer = null;
85
+ if (transaction.customer?.email) {
86
+ customer = { email: transaction.customer.email };
87
+ } else if (transaction.customer?.id) {
88
+ customer = { id: transaction.customer.id };
87
89
  }
88
90
  return {
89
91
  id: init.reference,
90
- customer: transaction.customer?.email ? { email: transaction.customer.email } : null,
92
+ customer,
91
93
  payment_url: init.authorization_url,
92
94
  metadata: Object.keys(metadata).length > 0 ? metadata : null,
93
95
  session_type: type ?? "one_time",
@@ -39,11 +39,17 @@ var Payment$inboundSchema = (data, overridePaymentUrl) => {
39
39
  itemId = paykitMeta.item_id ?? null;
40
40
  }
41
41
  const status = paystackStatusMap[data.status] ?? "pending";
42
+ let customer = null;
43
+ if (data.customer?.email) {
44
+ customer = { email: data.customer.email };
45
+ } else if (data.customer?.id) {
46
+ customer = { id: data.customer.id };
47
+ }
42
48
  return {
43
49
  id: data.reference,
44
50
  amount: data.amount,
45
51
  currency: data.currency,
46
- customer: data.customer?.email ? { email: data.customer.email } : null,
52
+ customer,
47
53
  status,
48
54
  metadata,
49
55
  item_id: itemId,
@@ -68,24 +74,20 @@ var Checkout$inboundSchema = (init, transaction) => {
68
74
  type: billingModeSchema.optional()
69
75
  })
70
76
  );
71
- if (paykitMeta && typeof paykitMeta === "string") {
72
- const parsed = parseJSON(
73
- paykitMeta,
74
- Schema.object({
75
- item_id: Schema.string().optional(),
76
- quantity: Schema.number().optional(),
77
- type: billingModeSchema.optional()
78
- })
79
- );
80
- if (parsed) {
81
- itemId = parsed.item_id ?? "";
82
- quantity = parsed.quantity ?? 1;
83
- type = parsed.type ?? null;
84
- }
77
+ if (paykitMeta) {
78
+ itemId = paykitMeta.item_id ?? "";
79
+ quantity = paykitMeta.quantity ?? 1;
80
+ type = paykitMeta.type ?? null;
81
+ }
82
+ let customer = null;
83
+ if (transaction.customer?.email) {
84
+ customer = { email: transaction.customer.email };
85
+ } else if (transaction.customer?.id) {
86
+ customer = { id: transaction.customer.id };
85
87
  }
86
88
  return {
87
89
  id: init.reference,
88
- customer: transaction.customer?.email ? { email: transaction.customer.email } : null,
90
+ customer,
89
91
  payment_url: init.authorization_url,
90
92
  metadata: Object.keys(metadata).length > 0 ? metadata : null,
91
93
  session_type: type ?? "one_time",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paykit-sdk/paystack",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Paystack provider for PayKit",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -11,9 +11,6 @@
11
11
  "paykit": {
12
12
  "type": "provider"
13
13
  },
14
- "scripts": {
15
- "build": "tsup"
16
- },
17
14
  "keywords": [
18
15
  "paystack",
19
16
  "paykit",
@@ -23,13 +20,13 @@
23
20
  "author": "Emmanuel Odii",
24
21
  "license": "ISC",
25
22
  "peerDependencies": {
26
- "@paykit-sdk/core": ">=1.2.0"
23
+ "@paykit-sdk/core": "^1.2.3"
27
24
  },
28
25
  "devDependencies": {
29
- "@paykit-sdk/core": "workspace:*",
30
26
  "tsup": "^8.5.0",
31
27
  "typescript": "^5.9.2",
32
- "zod": "^3.24.2"
28
+ "zod": "^3.24.2",
29
+ "@paykit-sdk/core": "1.3.0"
33
30
  },
34
31
  "publishConfig": {
35
32
  "access": "public"
@@ -40,5 +37,10 @@
40
37
  },
41
38
  "bugs": {
42
39
  "url": "https://github.com/usepaykit/paykit-sdk/issues"
40
+ },
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "test": "vitest run --config ../../vitest.config.ts packages/paystack/src",
44
+ "typecheck": "tsc --noEmit"
43
45
  }
44
- }
46
+ }