@paykit-sdk/monnify 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -211,6 +211,48 @@ var MonnifyProvider = class extends core.AbstractPayKitProvider {
211
211
  }
212
212
  return altResponse.value.responseBody;
213
213
  }
214
+ /**
215
+ * Monnify only has one way to move money: a hosted redirect
216
+ * transaction. createCheckout and createPayment both boil down to
217
+ * this same call - only the input validation and the response
218
+ * mapper differ between the two.
219
+ */
220
+ async initializeTransaction(params) {
221
+ const paymentReference = crypto.randomUUID();
222
+ const body = {
223
+ amount: params.amount,
224
+ paymentReference,
225
+ paymentDescription: params.description,
226
+ currencyCode: params.currency,
227
+ redirectUrl: params.redirectUrl,
228
+ paymentMethods: ["CARD", "ACCOUNT_TRANSFER"],
229
+ metadata: params.metadata,
230
+ customerEmail: params.email
231
+ };
232
+ const response = await this._client.post(
233
+ "/v1/merchant/transactions/init-transaction",
234
+ {
235
+ body: JSON.stringify(body),
236
+ headers: await this.tokenManager.getAuthHeaders()
237
+ }
238
+ );
239
+ const responseBody = this.ensureResponse(
240
+ response,
241
+ "initializeTransaction"
242
+ );
243
+ const transactionReference = responseBody.transactionReference;
244
+ const checkoutUrl = responseBody.checkoutUrl;
245
+ const transactionResponse = await this._client.get(
246
+ `/v2/merchant/transactions/query?paymentReference=${paymentReference}`,
247
+ { headers: await this.tokenManager.getAuthHeaders() }
248
+ );
249
+ const transactionData = this.ensureResponse(
250
+ transactionResponse,
251
+ "initializeTransaction",
252
+ "Failed to retrieve transaction details"
253
+ );
254
+ return { ...transactionData, checkoutUrl, transactionReference };
255
+ }
214
256
  createCheckout = async (params) => {
215
257
  const data = this.validateSchema(
216
258
  core.createCheckoutSchema,
@@ -233,50 +275,21 @@ var MonnifyProvider = class extends core.AbstractPayKitProvider {
233
275
  data.provider_metadata ?? { currency: "NGN" },
234
276
  "The following fields must be present in the provider_metadata of createCheckout: {keys}"
235
277
  );
236
- const paymentReference = crypto.randomUUID();
237
- const body = {
278
+ const transactionData = await this.initializeTransaction({
279
+ email: data.customer.email,
238
280
  amount,
239
- paymentReference,
240
- paymentDescription: `Checkout for ${data.item_id} x ${data.quantity} item${data.quantity > 1 ? "s" : ""}`,
241
- currencyCode: currency,
281
+ currency,
242
282
  redirectUrl: data.success_url,
243
- paymentMethods: ["CARD", "ACCOUNT_TRANSFER"],
283
+ description: `Checkout for ${data.item_id} x ${data.quantity} item${data.quantity > 1 ? "s" : ""}`,
244
284
  metadata: {
245
285
  ...params.metadata,
246
286
  [core.PAYKIT_METADATA_KEY]: JSON.stringify({
247
287
  item: data.item_id,
248
288
  qty: data.quantity
249
289
  })
250
- },
251
- customerEmail: data.customer.email
252
- };
253
- const response = await this._client.post(
254
- "/v1/merchant/transactions/init-transaction",
255
- {
256
- body: JSON.stringify(body),
257
- headers: await this.tokenManager.getAuthHeaders()
258
290
  }
259
- );
260
- const responseBody = this.ensureResponse(
261
- response,
262
- "createCheckout"
263
- );
264
- const transactionReference = responseBody.transactionReference;
265
- const checkoutUrl = responseBody.checkoutUrl;
266
- const checkoutResponse = await this._client.get(
267
- `/v2/merchant/transactions/query?paymentReference=${paymentReference}`,
268
- { headers: await this.tokenManager.getAuthHeaders() }
269
- );
270
- const checkoutData = this.ensureResponse(
271
- checkoutResponse,
272
- "createCheckout",
273
- "Failed to retrieve checkout details"
274
- );
275
- return Checkout$inboundSchema({
276
- ...checkoutData,
277
- checkoutUrl,
278
- transactionReference
279
291
  });
292
+ return Checkout$inboundSchema(transactionData);
280
293
  };
281
294
  retrieveCheckout = async (id) => {
282
295
  const transactionData = await this.queryTransaction(
@@ -395,14 +408,39 @@ var MonnifyProvider = class extends core.AbstractPayKitProvider {
395
408
  );
396
409
  };
397
410
  createPayment = async (params) => {
398
- throw new core.ProviderNotSupportedError(
399
- "createPayment",
400
- "Moniepoint",
401
- {
402
- reason: "Moniepoint doesn't support creating payments",
403
- alternative: "Use the createPayment method instead"
404
- }
411
+ const data = this.validateSchema(
412
+ core.createPaymentSchema,
413
+ params,
414
+ "createPayment"
415
+ );
416
+ if (!core.isEmailCustomer(data.customer)) {
417
+ throw new core.InvalidTypeError(
418
+ "customer",
419
+ "object (customer) with email",
420
+ "string (customer ID)",
421
+ {
422
+ provider: this.providerName,
423
+ method: "createPayment"
424
+ }
425
+ );
426
+ }
427
+ const { success_url } = core.validateRequiredKeys(
428
+ ["success_url"],
429
+ data.provider_metadata ?? {},
430
+ "The following fields must be present in the provider_metadata of createPayment: {keys}"
405
431
  );
432
+ const transactionData = await this.initializeTransaction({
433
+ email: data.customer.email,
434
+ amount: data.amount.toString(),
435
+ currency: data.currency,
436
+ redirectUrl: success_url,
437
+ description: `Payment for ${data.item_id}`,
438
+ metadata: {
439
+ ...data.metadata,
440
+ [core.PAYKIT_METADATA_KEY]: JSON.stringify({ item: data.item_id })
441
+ }
442
+ });
443
+ return Payment$inboundSchema(transactionData);
406
444
  };
407
445
  retrievePayment = async (id) => {
408
446
  try {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { schema, Schema, validateRequiredKeys, AbstractPayKitProvider, HTTPClient, OAuth2TokenManager, ValidationError, OperationFailedError, createCheckoutSchema, isEmailCustomer, InvalidTypeError, PAYKIT_METADATA_KEY, ProviderNotSupportedError, createRefundSchema, WebhookError, parseJSON, omitInternalMetadata } from '@paykit-sdk/core';
1
+ import { schema, Schema, validateRequiredKeys, AbstractPayKitProvider, HTTPClient, OAuth2TokenManager, ValidationError, OperationFailedError, createCheckoutSchema, isEmailCustomer, InvalidTypeError, PAYKIT_METADATA_KEY, ProviderNotSupportedError, createPaymentSchema, createRefundSchema, WebhookError, parseJSON, omitInternalMetadata } from '@paykit-sdk/core';
2
2
  import { timingSafeEqual } from 'crypto';
3
3
  import { sha512 } from 'js-sha512';
4
4
 
@@ -209,6 +209,48 @@ var MonnifyProvider = class extends AbstractPayKitProvider {
209
209
  }
210
210
  return altResponse.value.responseBody;
211
211
  }
212
+ /**
213
+ * Monnify only has one way to move money: a hosted redirect
214
+ * transaction. createCheckout and createPayment both boil down to
215
+ * this same call - only the input validation and the response
216
+ * mapper differ between the two.
217
+ */
218
+ async initializeTransaction(params) {
219
+ const paymentReference = crypto.randomUUID();
220
+ const body = {
221
+ amount: params.amount,
222
+ paymentReference,
223
+ paymentDescription: params.description,
224
+ currencyCode: params.currency,
225
+ redirectUrl: params.redirectUrl,
226
+ paymentMethods: ["CARD", "ACCOUNT_TRANSFER"],
227
+ metadata: params.metadata,
228
+ customerEmail: params.email
229
+ };
230
+ const response = await this._client.post(
231
+ "/v1/merchant/transactions/init-transaction",
232
+ {
233
+ body: JSON.stringify(body),
234
+ headers: await this.tokenManager.getAuthHeaders()
235
+ }
236
+ );
237
+ const responseBody = this.ensureResponse(
238
+ response,
239
+ "initializeTransaction"
240
+ );
241
+ const transactionReference = responseBody.transactionReference;
242
+ const checkoutUrl = responseBody.checkoutUrl;
243
+ const transactionResponse = await this._client.get(
244
+ `/v2/merchant/transactions/query?paymentReference=${paymentReference}`,
245
+ { headers: await this.tokenManager.getAuthHeaders() }
246
+ );
247
+ const transactionData = this.ensureResponse(
248
+ transactionResponse,
249
+ "initializeTransaction",
250
+ "Failed to retrieve transaction details"
251
+ );
252
+ return { ...transactionData, checkoutUrl, transactionReference };
253
+ }
212
254
  createCheckout = async (params) => {
213
255
  const data = this.validateSchema(
214
256
  createCheckoutSchema,
@@ -231,50 +273,21 @@ var MonnifyProvider = class extends AbstractPayKitProvider {
231
273
  data.provider_metadata ?? { currency: "NGN" },
232
274
  "The following fields must be present in the provider_metadata of createCheckout: {keys}"
233
275
  );
234
- const paymentReference = crypto.randomUUID();
235
- const body = {
276
+ const transactionData = await this.initializeTransaction({
277
+ email: data.customer.email,
236
278
  amount,
237
- paymentReference,
238
- paymentDescription: `Checkout for ${data.item_id} x ${data.quantity} item${data.quantity > 1 ? "s" : ""}`,
239
- currencyCode: currency,
279
+ currency,
240
280
  redirectUrl: data.success_url,
241
- paymentMethods: ["CARD", "ACCOUNT_TRANSFER"],
281
+ description: `Checkout for ${data.item_id} x ${data.quantity} item${data.quantity > 1 ? "s" : ""}`,
242
282
  metadata: {
243
283
  ...params.metadata,
244
284
  [PAYKIT_METADATA_KEY]: JSON.stringify({
245
285
  item: data.item_id,
246
286
  qty: data.quantity
247
287
  })
248
- },
249
- customerEmail: data.customer.email
250
- };
251
- const response = await this._client.post(
252
- "/v1/merchant/transactions/init-transaction",
253
- {
254
- body: JSON.stringify(body),
255
- headers: await this.tokenManager.getAuthHeaders()
256
288
  }
257
- );
258
- const responseBody = this.ensureResponse(
259
- response,
260
- "createCheckout"
261
- );
262
- const transactionReference = responseBody.transactionReference;
263
- const checkoutUrl = responseBody.checkoutUrl;
264
- const checkoutResponse = await this._client.get(
265
- `/v2/merchant/transactions/query?paymentReference=${paymentReference}`,
266
- { headers: await this.tokenManager.getAuthHeaders() }
267
- );
268
- const checkoutData = this.ensureResponse(
269
- checkoutResponse,
270
- "createCheckout",
271
- "Failed to retrieve checkout details"
272
- );
273
- return Checkout$inboundSchema({
274
- ...checkoutData,
275
- checkoutUrl,
276
- transactionReference
277
289
  });
290
+ return Checkout$inboundSchema(transactionData);
278
291
  };
279
292
  retrieveCheckout = async (id) => {
280
293
  const transactionData = await this.queryTransaction(
@@ -393,14 +406,39 @@ var MonnifyProvider = class extends AbstractPayKitProvider {
393
406
  );
394
407
  };
395
408
  createPayment = async (params) => {
396
- throw new ProviderNotSupportedError(
397
- "createPayment",
398
- "Moniepoint",
399
- {
400
- reason: "Moniepoint doesn't support creating payments",
401
- alternative: "Use the createPayment method instead"
402
- }
409
+ const data = this.validateSchema(
410
+ createPaymentSchema,
411
+ params,
412
+ "createPayment"
413
+ );
414
+ if (!isEmailCustomer(data.customer)) {
415
+ throw new InvalidTypeError(
416
+ "customer",
417
+ "object (customer) with email",
418
+ "string (customer ID)",
419
+ {
420
+ provider: this.providerName,
421
+ method: "createPayment"
422
+ }
423
+ );
424
+ }
425
+ const { success_url } = validateRequiredKeys(
426
+ ["success_url"],
427
+ data.provider_metadata ?? {},
428
+ "The following fields must be present in the provider_metadata of createPayment: {keys}"
403
429
  );
430
+ const transactionData = await this.initializeTransaction({
431
+ email: data.customer.email,
432
+ amount: data.amount.toString(),
433
+ currency: data.currency,
434
+ redirectUrl: success_url,
435
+ description: `Payment for ${data.item_id}`,
436
+ metadata: {
437
+ ...data.metadata,
438
+ [PAYKIT_METADATA_KEY]: JSON.stringify({ item: data.item_id })
439
+ }
440
+ });
441
+ return Payment$inboundSchema(transactionData);
404
442
  };
405
443
  retrievePayment = async (id) => {
406
444
  try {
@@ -1,6 +1,18 @@
1
1
  import { PaykitProviderOptions, AbstractPayKitProvider, PayKitProvider, ProviderMetadataRegistry, CreateCheckoutSchema, Checkout, UpdateCheckoutSchema, CreateCustomerParams, Customer, UpdateCustomerParams, CreateSubscriptionSchema, Subscription, UpdateSubscriptionSchema, CreatePaymentSchema, Payment, UpdatePaymentSchema, CapturePaymentSchema, CreateRefundSchema, Refund, WebhookHandlerConfig, WebhookEventPayload } from '@paykit-sdk/core';
2
2
 
3
3
  interface MonnifyMetadata extends ProviderMetadataRegistry {
4
+ checkout?: {
5
+ amount?: string;
6
+ currency?: string;
7
+ };
8
+ payment?: {
9
+ /**
10
+ * Monnify only supports hosted/redirect transactions - a
11
+ * direct createPayment call still needs somewhere to send the
12
+ * customer, same as createCheckout's success_url.
13
+ */
14
+ success_url?: string;
15
+ };
4
16
  }
5
17
  interface MonnifyRawEvents extends Record<string, any> {
6
18
  }
@@ -39,6 +51,13 @@ declare class MonnifyProvider extends AbstractPayKitProvider implements PayKitPr
39
51
  * Queries transaction by transactionReference or paymentReference (with fallback)
40
52
  */
41
53
  private queryTransaction;
54
+ /**
55
+ * Monnify only has one way to move money: a hosted redirect
56
+ * transaction. createCheckout and createPayment both boil down to
57
+ * this same call - only the input validation and the response
58
+ * mapper differ between the two.
59
+ */
60
+ private initializeTransaction;
42
61
  createCheckout: (params: CreateCheckoutSchema) => Promise<Checkout>;
43
62
  retrieveCheckout: (id: string) => Promise<Checkout>;
44
63
  updateCheckout: (id: string, params: UpdateCheckoutSchema) => Promise<Checkout>;
@@ -52,7 +71,7 @@ declare class MonnifyProvider extends AbstractPayKitProvider implements PayKitPr
52
71
  cancelSubscription: (id: string) => Promise<Subscription>;
53
72
  deleteSubscription: (id: string) => Promise<null>;
54
73
  retrieveSubscription: (id: string) => Promise<Subscription | null>;
55
- createPayment: (params: CreatePaymentSchema) => Promise<Payment>;
74
+ createPayment: (params: CreatePaymentSchema<MonnifyMetadata["payment"]>) => Promise<Payment>;
56
75
  retrievePayment: (id: string) => Promise<Payment | null>;
57
76
  updatePayment: (id: string, params: UpdatePaymentSchema) => Promise<Payment>;
58
77
  deletePayment: (id: string) => Promise<null>;
@@ -1,6 +1,18 @@
1
1
  import { PaykitProviderOptions, AbstractPayKitProvider, PayKitProvider, ProviderMetadataRegistry, CreateCheckoutSchema, Checkout, UpdateCheckoutSchema, CreateCustomerParams, Customer, UpdateCustomerParams, CreateSubscriptionSchema, Subscription, UpdateSubscriptionSchema, CreatePaymentSchema, Payment, UpdatePaymentSchema, CapturePaymentSchema, CreateRefundSchema, Refund, WebhookHandlerConfig, WebhookEventPayload } from '@paykit-sdk/core';
2
2
 
3
3
  interface MonnifyMetadata extends ProviderMetadataRegistry {
4
+ checkout?: {
5
+ amount?: string;
6
+ currency?: string;
7
+ };
8
+ payment?: {
9
+ /**
10
+ * Monnify only supports hosted/redirect transactions - a
11
+ * direct createPayment call still needs somewhere to send the
12
+ * customer, same as createCheckout's success_url.
13
+ */
14
+ success_url?: string;
15
+ };
4
16
  }
5
17
  interface MonnifyRawEvents extends Record<string, any> {
6
18
  }
@@ -39,6 +51,13 @@ declare class MonnifyProvider extends AbstractPayKitProvider implements PayKitPr
39
51
  * Queries transaction by transactionReference or paymentReference (with fallback)
40
52
  */
41
53
  private queryTransaction;
54
+ /**
55
+ * Monnify only has one way to move money: a hosted redirect
56
+ * transaction. createCheckout and createPayment both boil down to
57
+ * this same call - only the input validation and the response
58
+ * mapper differ between the two.
59
+ */
60
+ private initializeTransaction;
42
61
  createCheckout: (params: CreateCheckoutSchema) => Promise<Checkout>;
43
62
  retrieveCheckout: (id: string) => Promise<Checkout>;
44
63
  updateCheckout: (id: string, params: UpdateCheckoutSchema) => Promise<Checkout>;
@@ -52,7 +71,7 @@ declare class MonnifyProvider extends AbstractPayKitProvider implements PayKitPr
52
71
  cancelSubscription: (id: string) => Promise<Subscription>;
53
72
  deleteSubscription: (id: string) => Promise<null>;
54
73
  retrieveSubscription: (id: string) => Promise<Subscription | null>;
55
- createPayment: (params: CreatePaymentSchema) => Promise<Payment>;
74
+ createPayment: (params: CreatePaymentSchema<MonnifyMetadata["payment"]>) => Promise<Payment>;
56
75
  retrievePayment: (id: string) => Promise<Payment | null>;
57
76
  updatePayment: (id: string, params: UpdatePaymentSchema) => Promise<Payment>;
58
77
  deletePayment: (id: string) => Promise<null>;
@@ -211,6 +211,48 @@ var MonnifyProvider = class extends core.AbstractPayKitProvider {
211
211
  }
212
212
  return altResponse.value.responseBody;
213
213
  }
214
+ /**
215
+ * Monnify only has one way to move money: a hosted redirect
216
+ * transaction. createCheckout and createPayment both boil down to
217
+ * this same call - only the input validation and the response
218
+ * mapper differ between the two.
219
+ */
220
+ async initializeTransaction(params) {
221
+ const paymentReference = crypto.randomUUID();
222
+ const body = {
223
+ amount: params.amount,
224
+ paymentReference,
225
+ paymentDescription: params.description,
226
+ currencyCode: params.currency,
227
+ redirectUrl: params.redirectUrl,
228
+ paymentMethods: ["CARD", "ACCOUNT_TRANSFER"],
229
+ metadata: params.metadata,
230
+ customerEmail: params.email
231
+ };
232
+ const response = await this._client.post(
233
+ "/v1/merchant/transactions/init-transaction",
234
+ {
235
+ body: JSON.stringify(body),
236
+ headers: await this.tokenManager.getAuthHeaders()
237
+ }
238
+ );
239
+ const responseBody = this.ensureResponse(
240
+ response,
241
+ "initializeTransaction"
242
+ );
243
+ const transactionReference = responseBody.transactionReference;
244
+ const checkoutUrl = responseBody.checkoutUrl;
245
+ const transactionResponse = await this._client.get(
246
+ `/v2/merchant/transactions/query?paymentReference=${paymentReference}`,
247
+ { headers: await this.tokenManager.getAuthHeaders() }
248
+ );
249
+ const transactionData = this.ensureResponse(
250
+ transactionResponse,
251
+ "initializeTransaction",
252
+ "Failed to retrieve transaction details"
253
+ );
254
+ return { ...transactionData, checkoutUrl, transactionReference };
255
+ }
214
256
  createCheckout = async (params) => {
215
257
  const data = this.validateSchema(
216
258
  core.createCheckoutSchema,
@@ -233,50 +275,21 @@ var MonnifyProvider = class extends core.AbstractPayKitProvider {
233
275
  data.provider_metadata ?? { currency: "NGN" },
234
276
  "The following fields must be present in the provider_metadata of createCheckout: {keys}"
235
277
  );
236
- const paymentReference = crypto.randomUUID();
237
- const body = {
278
+ const transactionData = await this.initializeTransaction({
279
+ email: data.customer.email,
238
280
  amount,
239
- paymentReference,
240
- paymentDescription: `Checkout for ${data.item_id} x ${data.quantity} item${data.quantity > 1 ? "s" : ""}`,
241
- currencyCode: currency,
281
+ currency,
242
282
  redirectUrl: data.success_url,
243
- paymentMethods: ["CARD", "ACCOUNT_TRANSFER"],
283
+ description: `Checkout for ${data.item_id} x ${data.quantity} item${data.quantity > 1 ? "s" : ""}`,
244
284
  metadata: {
245
285
  ...params.metadata,
246
286
  [core.PAYKIT_METADATA_KEY]: JSON.stringify({
247
287
  item: data.item_id,
248
288
  qty: data.quantity
249
289
  })
250
- },
251
- customerEmail: data.customer.email
252
- };
253
- const response = await this._client.post(
254
- "/v1/merchant/transactions/init-transaction",
255
- {
256
- body: JSON.stringify(body),
257
- headers: await this.tokenManager.getAuthHeaders()
258
290
  }
259
- );
260
- const responseBody = this.ensureResponse(
261
- response,
262
- "createCheckout"
263
- );
264
- const transactionReference = responseBody.transactionReference;
265
- const checkoutUrl = responseBody.checkoutUrl;
266
- const checkoutResponse = await this._client.get(
267
- `/v2/merchant/transactions/query?paymentReference=${paymentReference}`,
268
- { headers: await this.tokenManager.getAuthHeaders() }
269
- );
270
- const checkoutData = this.ensureResponse(
271
- checkoutResponse,
272
- "createCheckout",
273
- "Failed to retrieve checkout details"
274
- );
275
- return Checkout$inboundSchema({
276
- ...checkoutData,
277
- checkoutUrl,
278
- transactionReference
279
291
  });
292
+ return Checkout$inboundSchema(transactionData);
280
293
  };
281
294
  retrieveCheckout = async (id) => {
282
295
  const transactionData = await this.queryTransaction(
@@ -395,14 +408,39 @@ var MonnifyProvider = class extends core.AbstractPayKitProvider {
395
408
  );
396
409
  };
397
410
  createPayment = async (params) => {
398
- throw new core.ProviderNotSupportedError(
399
- "createPayment",
400
- "Moniepoint",
401
- {
402
- reason: "Moniepoint doesn't support creating payments",
403
- alternative: "Use the createPayment method instead"
404
- }
411
+ const data = this.validateSchema(
412
+ core.createPaymentSchema,
413
+ params,
414
+ "createPayment"
415
+ );
416
+ if (!core.isEmailCustomer(data.customer)) {
417
+ throw new core.InvalidTypeError(
418
+ "customer",
419
+ "object (customer) with email",
420
+ "string (customer ID)",
421
+ {
422
+ provider: this.providerName,
423
+ method: "createPayment"
424
+ }
425
+ );
426
+ }
427
+ const { success_url } = core.validateRequiredKeys(
428
+ ["success_url"],
429
+ data.provider_metadata ?? {},
430
+ "The following fields must be present in the provider_metadata of createPayment: {keys}"
405
431
  );
432
+ const transactionData = await this.initializeTransaction({
433
+ email: data.customer.email,
434
+ amount: data.amount.toString(),
435
+ currency: data.currency,
436
+ redirectUrl: success_url,
437
+ description: `Payment for ${data.item_id}`,
438
+ metadata: {
439
+ ...data.metadata,
440
+ [core.PAYKIT_METADATA_KEY]: JSON.stringify({ item: data.item_id })
441
+ }
442
+ });
443
+ return Payment$inboundSchema(transactionData);
406
444
  };
407
445
  retrievePayment = async (id) => {
408
446
  try {
@@ -1,4 +1,4 @@
1
- import { schema, Schema, AbstractPayKitProvider, HTTPClient, OAuth2TokenManager, ValidationError, OperationFailedError, createCheckoutSchema, isEmailCustomer, InvalidTypeError, validateRequiredKeys, PAYKIT_METADATA_KEY, ProviderNotSupportedError, createRefundSchema, WebhookError, parseJSON, omitInternalMetadata } from '@paykit-sdk/core';
1
+ import { schema, Schema, AbstractPayKitProvider, HTTPClient, OAuth2TokenManager, ValidationError, OperationFailedError, createCheckoutSchema, isEmailCustomer, InvalidTypeError, validateRequiredKeys, PAYKIT_METADATA_KEY, ProviderNotSupportedError, createPaymentSchema, createRefundSchema, WebhookError, parseJSON, omitInternalMetadata } from '@paykit-sdk/core';
2
2
  import { timingSafeEqual } from 'crypto';
3
3
  import { sha512 } from 'js-sha512';
4
4
 
@@ -209,6 +209,48 @@ var MonnifyProvider = class extends AbstractPayKitProvider {
209
209
  }
210
210
  return altResponse.value.responseBody;
211
211
  }
212
+ /**
213
+ * Monnify only has one way to move money: a hosted redirect
214
+ * transaction. createCheckout and createPayment both boil down to
215
+ * this same call - only the input validation and the response
216
+ * mapper differ between the two.
217
+ */
218
+ async initializeTransaction(params) {
219
+ const paymentReference = crypto.randomUUID();
220
+ const body = {
221
+ amount: params.amount,
222
+ paymentReference,
223
+ paymentDescription: params.description,
224
+ currencyCode: params.currency,
225
+ redirectUrl: params.redirectUrl,
226
+ paymentMethods: ["CARD", "ACCOUNT_TRANSFER"],
227
+ metadata: params.metadata,
228
+ customerEmail: params.email
229
+ };
230
+ const response = await this._client.post(
231
+ "/v1/merchant/transactions/init-transaction",
232
+ {
233
+ body: JSON.stringify(body),
234
+ headers: await this.tokenManager.getAuthHeaders()
235
+ }
236
+ );
237
+ const responseBody = this.ensureResponse(
238
+ response,
239
+ "initializeTransaction"
240
+ );
241
+ const transactionReference = responseBody.transactionReference;
242
+ const checkoutUrl = responseBody.checkoutUrl;
243
+ const transactionResponse = await this._client.get(
244
+ `/v2/merchant/transactions/query?paymentReference=${paymentReference}`,
245
+ { headers: await this.tokenManager.getAuthHeaders() }
246
+ );
247
+ const transactionData = this.ensureResponse(
248
+ transactionResponse,
249
+ "initializeTransaction",
250
+ "Failed to retrieve transaction details"
251
+ );
252
+ return { ...transactionData, checkoutUrl, transactionReference };
253
+ }
212
254
  createCheckout = async (params) => {
213
255
  const data = this.validateSchema(
214
256
  createCheckoutSchema,
@@ -231,50 +273,21 @@ var MonnifyProvider = class extends AbstractPayKitProvider {
231
273
  data.provider_metadata ?? { currency: "NGN" },
232
274
  "The following fields must be present in the provider_metadata of createCheckout: {keys}"
233
275
  );
234
- const paymentReference = crypto.randomUUID();
235
- const body = {
276
+ const transactionData = await this.initializeTransaction({
277
+ email: data.customer.email,
236
278
  amount,
237
- paymentReference,
238
- paymentDescription: `Checkout for ${data.item_id} x ${data.quantity} item${data.quantity > 1 ? "s" : ""}`,
239
- currencyCode: currency,
279
+ currency,
240
280
  redirectUrl: data.success_url,
241
- paymentMethods: ["CARD", "ACCOUNT_TRANSFER"],
281
+ description: `Checkout for ${data.item_id} x ${data.quantity} item${data.quantity > 1 ? "s" : ""}`,
242
282
  metadata: {
243
283
  ...params.metadata,
244
284
  [PAYKIT_METADATA_KEY]: JSON.stringify({
245
285
  item: data.item_id,
246
286
  qty: data.quantity
247
287
  })
248
- },
249
- customerEmail: data.customer.email
250
- };
251
- const response = await this._client.post(
252
- "/v1/merchant/transactions/init-transaction",
253
- {
254
- body: JSON.stringify(body),
255
- headers: await this.tokenManager.getAuthHeaders()
256
288
  }
257
- );
258
- const responseBody = this.ensureResponse(
259
- response,
260
- "createCheckout"
261
- );
262
- const transactionReference = responseBody.transactionReference;
263
- const checkoutUrl = responseBody.checkoutUrl;
264
- const checkoutResponse = await this._client.get(
265
- `/v2/merchant/transactions/query?paymentReference=${paymentReference}`,
266
- { headers: await this.tokenManager.getAuthHeaders() }
267
- );
268
- const checkoutData = this.ensureResponse(
269
- checkoutResponse,
270
- "createCheckout",
271
- "Failed to retrieve checkout details"
272
- );
273
- return Checkout$inboundSchema({
274
- ...checkoutData,
275
- checkoutUrl,
276
- transactionReference
277
289
  });
290
+ return Checkout$inboundSchema(transactionData);
278
291
  };
279
292
  retrieveCheckout = async (id) => {
280
293
  const transactionData = await this.queryTransaction(
@@ -393,14 +406,39 @@ var MonnifyProvider = class extends AbstractPayKitProvider {
393
406
  );
394
407
  };
395
408
  createPayment = async (params) => {
396
- throw new ProviderNotSupportedError(
397
- "createPayment",
398
- "Moniepoint",
399
- {
400
- reason: "Moniepoint doesn't support creating payments",
401
- alternative: "Use the createPayment method instead"
402
- }
409
+ const data = this.validateSchema(
410
+ createPaymentSchema,
411
+ params,
412
+ "createPayment"
413
+ );
414
+ if (!isEmailCustomer(data.customer)) {
415
+ throw new InvalidTypeError(
416
+ "customer",
417
+ "object (customer) with email",
418
+ "string (customer ID)",
419
+ {
420
+ provider: this.providerName,
421
+ method: "createPayment"
422
+ }
423
+ );
424
+ }
425
+ const { success_url } = validateRequiredKeys(
426
+ ["success_url"],
427
+ data.provider_metadata ?? {},
428
+ "The following fields must be present in the provider_metadata of createPayment: {keys}"
403
429
  );
430
+ const transactionData = await this.initializeTransaction({
431
+ email: data.customer.email,
432
+ amount: data.amount.toString(),
433
+ currency: data.currency,
434
+ redirectUrl: success_url,
435
+ description: `Payment for ${data.item_id}`,
436
+ metadata: {
437
+ ...data.metadata,
438
+ [PAYKIT_METADATA_KEY]: JSON.stringify({ item: data.item_id })
439
+ }
440
+ });
441
+ return Payment$inboundSchema(transactionData);
404
442
  };
405
443
  retrievePayment = async (id) => {
406
444
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paykit-sdk/monnify",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Moniepoint provider for PayKit",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -27,7 +27,7 @@
27
27
  "devDependencies": {
28
28
  "tsup": "^8.5.0",
29
29
  "typescript": "^5.9.2",
30
- "@paykit-sdk/core": "1.3.0"
30
+ "@paykit-sdk/core": "1.3.1"
31
31
  },
32
32
  "dependencies": {
33
33
  "js-sha512": "^0.9.0"