@herberthtk/yo-payments-api 0.2.0 → 0.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 CHANGED
@@ -22,9 +22,13 @@ import { YoAPI } from "@herberthtk/yo-payments-api";
22
22
  const yoAPI = new YoAPI("API_USERNAME", "API_PASSWORD");
23
23
 
24
24
  // Request a mobile money user to deposit funds into your account
25
- const response = await yoAPI.acDepositFunds("256770000000", 10000, "Reason for transfer of funds");
25
+ const response = await yoAPI.acDepositFunds(
26
+ "256770000000",
27
+ 10000,
28
+ "Reason for transfer of funds",
29
+ );
26
30
  if (response.Status === "OK") {
27
- console.log("Transaction Reference =", response.TransactionReference);
31
+ console.log("Transaction Reference =", response.TransactionReference);
28
32
  }
29
33
 
30
34
  // Check the balance of your account
@@ -40,26 +44,26 @@ All network methods are `async` and return typed response objects. Method names
40
44
  const yoAPI = new YoAPI(username: string, password: string, mode: "production" | "sandbox" = "production");
41
45
  ```
42
46
 
43
- | Setter | Type | Default | Purpose |
44
- |---|---|---|---|
45
- | `setExternalReference` | `string \| null` | `null` | Your reference for the payment (e.g. invoice number); sent with most requests |
46
- | `setInternalReference` | `string \| null` | `null` | Reference to another Yo! Payments system transaction |
47
- | `setNonblocking` | `"TRUE" \| "FALSE"` | `"FALSE"` | `"TRUE"` returns immediately; poll status or use IPN URLs |
48
- | `setInstantNotificationUrl` | `string \| null` | `null` | URL POSTed on successful deposit (non-blocking flow) |
49
- | `setFailureNotificationUrl` | `string \| null` | `null` | URL POSTed on failed deposit (non-blocking flow) |
50
- | `setProviderReferenceText` | `string \| null` | `null` | Text appended to the subscriber's confirmation SMS |
51
- | `setAuthenticationSignatureBase64` | `string \| null` | `null` | Required for certain deposit requests (ask Yo! support) |
52
- | `setDepositTransactionType` | `"PULL" \| "PUSH"` | `"PULL"` | Which deposit flow `acTransactionCheckStatus` follows up on |
53
- | `setTransactionLimitAccountIdentifier` | `string \| null` | `null` | Ask your account administrator before using |
54
- | `setPublicKeyAuthenticationNonce` | `string \| null` | `null` | Unique-per-request nonce for public-key-auth payouts |
55
- | `setPublicKeyAuthenticationSignatureBase64` | `string \| null` | `null` | Usually set via `generatePublicKeyAuthenticationSignature` |
56
- | `setPrivateKeyFileLocation` | `string \| null` | `null` | Path to the signing private key (PEM file) |
57
- | `setPrivateKeyContent` | `string \| null` | `null` | Key PEM text; for serverless hosts without key files (wins over file location) |
58
- | `setPublicKeyFileUrl` | `string` | bundled cert | Certificate used to verify IPN signatures |
59
- | `setUrl` | `string` | gateway URL | Override the API endpoint (testing/proxies) |
60
- | `setTimeout` | `number` (ms) | `120000` | Request timeout; `<= 0` disables it |
61
- | `setTlsVerificationEnabled` | `boolean` | `true` | Only disable for testing against self-signed endpoints |
62
- | `setMaxResponseBytes` | `number` | `1048576` | Cap on gateway response bodies |
47
+ | Setter | Type | Default | Purpose |
48
+ | ------------------------------------------- | ------------------- | ------------ | ------------------------------------------------------------------------------ |
49
+ | `setExternalReference` | `string \| null` | `null` | Your reference for the payment (e.g. invoice number); sent with most requests |
50
+ | `setInternalReference` | `string \| null` | `null` | Reference to another Yo! Payments system transaction |
51
+ | `setNonblocking` | `"TRUE" \| "FALSE"` | `"FALSE"` | `"TRUE"` returns immediately; poll status or use IPN URLs |
52
+ | `setInstantNotificationUrl` | `string \| null` | `null` | URL POSTed on successful deposit (non-blocking flow) |
53
+ | `setFailureNotificationUrl` | `string \| null` | `null` | URL POSTed on failed deposit (non-blocking flow) |
54
+ | `setProviderReferenceText` | `string \| null` | `null` | Text appended to the subscriber's confirmation SMS |
55
+ | `setAuthenticationSignatureBase64` | `string \| null` | `null` | Required for certain deposit requests (ask Yo! support) |
56
+ | `setDepositTransactionType` | `"PULL" \| "PUSH"` | `"PULL"` | Which deposit flow `acTransactionCheckStatus` follows up on |
57
+ | `setTransactionLimitAccountIdentifier` | `string \| null` | `null` | Ask your account administrator before using |
58
+ | `setPublicKeyAuthenticationNonce` | `string \| null` | `null` | Unique-per-request nonce for public-key-auth payouts |
59
+ | `setPublicKeyAuthenticationSignatureBase64` | `string \| null` | `null` | Usually set via `generatePublicKeyAuthenticationSignature` |
60
+ | `setPrivateKeyFileLocation` | `string \| null` | `null` | Path to the signing private key (PEM file) |
61
+ | `setPrivateKeyContent` | `string \| null` | `null` | Key PEM text; for serverless hosts without key files (wins over file location) |
62
+ | `setPublicKeyFileUrl` | `string` | bundled cert | Certificate used to verify IPN signatures |
63
+ | `setUrl` | `string` | gateway URL | Override the API endpoint (testing/proxies) |
64
+ | `setTimeout` | `number` (ms) | `120000` | Request timeout; `<= 0` disables it |
65
+ | `setTlsVerificationEnabled` | `boolean` | `true` | Only disable for testing against self-signed endpoints |
66
+ | `setMaxResponseBytes` | `number` | `1048576` | Cap on gateway response bodies |
63
67
 
64
68
  Every setter has a matching getter (`getExternalReference()`, `getMode()`, …). One instance holds per-request state, so create a fresh client per request — never share one across concurrent operations.
65
69
 
@@ -76,14 +80,18 @@ Amounts accept `number | string` — pass a string when exact formatting matters
76
80
  ### acDepositFunds — request a mobile money deposit (USSD PIN prompt)
77
81
 
78
82
  ```ts
79
- const res: DepositFundsResponse = await yoAPI.acDepositFunds(msisdn, amount, narrative);
83
+ const res: DepositFundsResponse = await yoAPI.acDepositFunds(
84
+ msisdn,
85
+ amount,
86
+ narrative,
87
+ );
80
88
  ```
81
89
 
82
- | Parameter | Type | Description |
83
- |---|---|---|
84
- | `msisdn` | `string` | Subscriber phone, e.g. `"256770000000"` |
85
- | `amount` | `number \| string` | Amount to collect |
86
- | `narrative` | `string` | Reason shown to the subscriber |
90
+ | Parameter | Type | Description |
91
+ | ----------- | ------------------ | --------------------------------------- |
92
+ | `msisdn` | `string` | Subscriber phone, e.g. `"256770000000"` |
93
+ | `amount` | `number \| string` | Amount to collect |
94
+ | `narrative` | `string` | Reason shown to the subscriber |
87
95
 
88
96
  Response (`DepositFundsResponse`): `Status`, `StatusCode`, `StatusMessage`, `TransactionStatus` always present. On success also `TransactionReference` (save this — it identifies the payment everywhere else), `MNOTransactionReferenceId`, `IssuedReceiptNumber`. On business failure, `ErrorMessageCode` / `ErrorMessage` instead. Optional request tweaks: `setNonblocking("TRUE")` + IPN URLs, `setAuthenticationSignatureBase64`.
89
97
 
@@ -143,7 +151,13 @@ const res: MinistatementResponse = await yoAPI.acGetMinistatement(
143
151
  // to a phone number
144
152
  await yoAPI.acSendAirtimeMobile(msisdn, amount, narrative);
145
153
  // to another Yo! account ("UGX-MTNAT" | "UGX-WTLAT" | "UGX-OULAT" | "UGX-AIRAT")
146
- await yoAPI.acSendAirtimeInternal(currencyCode, amount, beneficiaryAccount, beneficiaryEmail, narrative);
154
+ await yoAPI.acSendAirtimeInternal(
155
+ currencyCode,
156
+ amount,
157
+ beneficiaryAccount,
158
+ beneficiaryEmail,
159
+ narrative,
160
+ );
147
161
  ```
148
162
 
149
163
  Same response shape as deposits.
@@ -151,7 +165,11 @@ Same response shape as deposits.
151
165
  ### acWithdrawFunds — pay out to mobile money (handle with care)
152
166
 
153
167
  ```ts
154
- const res: DepositFundsResponse = await yoAPI.acWithdrawFunds(msisdn, amount, narrative);
168
+ const res: DepositFundsResponse = await yoAPI.acWithdrawFunds(
169
+ msisdn,
170
+ amount,
171
+ narrative,
172
+ );
155
173
  ```
156
174
 
157
175
  Same response shape as deposits. Requires an API Access Letter; some payouts additionally require public-key authentication — see below. Optional: `setTransactionLimitAccountIdentifier`, `setPublicKeyAuthenticationNonce` + `setPublicKeyAuthenticationSignatureBase64`.
@@ -170,7 +188,8 @@ const res: PurchaseAirtimeStockResponse = await yoAPI.acUserPurchaseAirtimestock
170
188
  ### acGetMsisdnKycInfo — name lookup before paying out
171
189
 
172
190
  ```ts
173
- const res: MsisdnKycInfoResponse = await yoAPI.acGetMsisdnKycInfo("256770000000");
191
+ const res: MsisdnKycInfoResponse =
192
+ await yoAPI.acGetMsisdnKycInfo("256770000000");
174
193
  // res.FirstName / res.MiddleName / res.Surname when the gateway returns them
175
194
  ```
176
195
 
@@ -180,12 +199,21 @@ MTN Uganda and Airtel Uganda only; needs permission from support@yo.co.ug. `Stat
180
199
 
181
200
  ```ts
182
201
  const payment: PaymentNotificationResult = yoAPI.receivePaymentNotification({
183
- date_time, amount, narrative, network_ref, external_ref, msisdn, signature,
202
+ date_time,
203
+ amount,
204
+ narrative,
205
+ network_ref,
206
+ external_ref,
207
+ msisdn,
208
+ signature,
184
209
  });
185
210
  // payment.is_verified === true → trust payment.msisdn / .amount / .external_ref / ...
186
- const failure: PaymentFailureNotificationResult = yoAPI.receivePaymentFailureNotification({
187
- failed_transaction_reference, transaction_init_date, verification,
188
- });
211
+ const failure: PaymentFailureNotificationResult =
212
+ yoAPI.receivePaymentFailureNotification({
213
+ failed_transaction_reference,
214
+ transaction_init_date,
215
+ verification,
216
+ });
189
217
  ```
190
218
 
191
219
  Pass the parsed POST form body (PHP reads `$_POST`; here you supply it). Verification is RSA-SHA256 against the bundled Yo! certificate and is fail-closed: any problem (bad signature, missing cert) yields `is_verified: false`, never a throw. Always gate crediting on `is_verified` **and** dedupe on `external_ref` — notifications carry no replay protection.
@@ -207,6 +235,7 @@ Signs `username + amount + msisdn + narrative + externalReference + nonce` (SHA1
207
235
  Concrete objects each call resolves to. Absent optional fields are omitted (never `null`).
208
236
 
209
237
  **Deposits, transfers, airtime, withdrawals** (`DepositFundsResponse` family) — success:
238
+
210
239
  ```ts
211
240
  {
212
241
  Status: "OK",
@@ -220,6 +249,7 @@ Concrete objects each call resolves to. Absent optional fields are omitted (neve
220
249
  ```
221
250
 
222
251
  Same calls — business failure (returned, not thrown):
252
+
223
253
  ```ts
224
254
  {
225
255
  Status: "FAILED",
@@ -232,6 +262,7 @@ Same calls — business failure (returned, not thrown):
232
262
  ```
233
263
 
234
264
  **Transaction status** (`TransactionCheckStatusResponse`) — success carries the money fields:
265
+
235
266
  ```ts
236
267
  {
237
268
  Status: "OK",
@@ -249,6 +280,7 @@ Same calls — business failure (returned, not thrown):
249
280
  ```
250
281
 
251
282
  Still pending — keep polling:
283
+
252
284
  ```ts
253
285
  {
254
286
  Status: "OK",
@@ -259,6 +291,7 @@ Still pending — keep polling:
259
291
  ```
260
292
 
261
293
  **Balance** (`AcctBalanceResponse`):
294
+
262
295
  ```ts
263
296
  {
264
297
  Status: "OK",
@@ -271,6 +304,7 @@ Still pending — keep polling:
271
304
  ```
272
305
 
273
306
  **Ministatement** (`MinistatementResponse`) — `Transactions` is always an array:
307
+
274
308
  ```ts
275
309
  {
276
310
  Status: "OK",
@@ -302,6 +336,7 @@ Still pending — keep polling:
302
336
  ```
303
337
 
304
338
  **Airtimestock purchase** (`PurchaseAirtimeStockResponse`):
339
+
305
340
  ```ts
306
341
  {
307
342
  Status: "OK",
@@ -314,6 +349,7 @@ Still pending — keep polling:
314
349
  ```
315
350
 
316
351
  **KYC lookup** (`MsisdnKycInfoResponse`):
352
+
317
353
  ```ts
318
354
  {
319
355
  Status: "OK",
@@ -326,6 +362,7 @@ Still pending — keep polling:
326
362
  ```
327
363
 
328
364
  **Verified payment notification** (`PaymentNotificationResult`):
365
+
329
366
  ```ts
330
367
  {
331
368
  is_verified: true,
@@ -339,6 +376,7 @@ Still pending — keep polling:
339
376
  ```
340
377
 
341
378
  Unverifiable notification (bad signature or cert problem) — credit nothing:
379
+
342
380
  ```ts
343
381
  {
344
382
  is_verified: false,
@@ -352,6 +390,7 @@ Unverifiable notification (bad signature or cert problem) — credit nothing:
352
390
  ```
353
391
 
354
392
  **Transport failure** — thrown as `YoAPIError`, e.g. gateway HTTP 502:
393
+
355
394
  ```ts
356
395
  // caught error instance:
357
396
  YoAPIError: Yo! Payments gateway responded with HTTP 502
@@ -363,18 +402,20 @@ YoAPIError: Yo! Payments gateway responded with HTTP 502
363
402
  ## Usage cases
364
403
 
365
404
  **1. Blocking deposit** — simplest collection flow; the call returns after the subscriber approves:
405
+
366
406
  ```ts
367
407
  const api = new YoAPI(u, p, "sandbox");
368
408
  api.setExternalReference(`INV-${Date.now()}`);
369
409
  const res = await api.acDepositFunds("256770000000", 10000, "Order payment");
370
410
  if (res.Status === "OK" && res.TransactionStatus === "SUCCEEDED") {
371
- await markPaid(res.TransactionReference!);
411
+ await markPaid(res.TransactionReference!);
372
412
  } else {
373
- console.error(res.ErrorMessageCode, res.ErrorMessage);
413
+ console.error(res.ErrorMessageCode, res.ErrorMessage);
374
414
  }
375
415
  ```
376
416
 
377
417
  **2. Non-blocking deposit with IPN + polling fallback** — instant response, then confirm:
418
+
378
419
  ```ts
379
420
  api.setNonblocking("TRUE");
380
421
  api.setInstantNotificationUrl("https://example.com/api/yo/ipn");
@@ -383,19 +424,27 @@ const res = await api.acDepositFunds("256770000000", 10000, "Order payment");
383
424
  // ...meanwhile your IPN endpoint verifies and credits on payment.external_ref...
384
425
  // ...and/or poll until settled:
385
426
  for (;;) {
386
- const st = await api.acTransactionCheckStatus(null, externalRef);
387
- if (st.TransactionStatus !== "PENDING") break;
388
- await new Promise((r) => setTimeout(r, 5000));
427
+ const st = await api.acTransactionCheckStatus(null, externalRef);
428
+ if (st.TransactionStatus !== "PENDING") break;
429
+ await new Promise((r) => setTimeout(r, 5000));
389
430
  }
390
431
  ```
391
432
 
392
433
  **3. Daily reconciliation from the ministatement:**
434
+
393
435
  ```ts
394
- const st = await api.acGetMinistatement("2026-09-10 00:00:00", "2026-09-10 23:59:59", "SUCCEEDED", "UGX-MTNMM", 0);
436
+ const st = await api.acGetMinistatement(
437
+ "2026-09-10 00:00:00",
438
+ "2026-09-10 23:59:59",
439
+ "SUCCEEDED",
440
+ "UGX-MTNMM",
441
+ 0,
442
+ );
395
443
  for (const tx of st.Transactions) await reconcile(tx);
396
444
  ```
397
445
 
398
446
  **4. Serverless payout with key material (no key files on Vercel/Lambda):**
447
+
399
448
  ```ts
400
449
  api.setExternalReference("SAL-SEP-001");
401
450
  api.setPublicKeyAuthenticationNonce(crypto.randomUUID());
@@ -411,22 +460,24 @@ PHP reads `$_POST` / `php://input` globals, which is impossible in TypeScript, s
411
460
  ```ts
412
461
  // Bun HTTP server example
413
462
  Bun.serve({
414
- port: 3000,
415
- async fetch(req) {
416
- const form = await req.formData();
417
- const body = Object.fromEntries(form.entries()) as any;
418
-
419
- const yoAPI = new YoAPI("API_USERNAME", "API_PASSWORD", "sandbox");
420
- const payment = yoAPI.receivePaymentNotification(body);
421
- if (payment.is_verified) {
422
- console.log(`Payment from ${payment.msisdn} of ${payment.amount} (ref ${payment.external_ref})`);
423
- // update your transaction status where external_ref = payment.external_ref
424
- }
425
-
426
- // Failure notifications:
427
- // const failure = yoAPI.receivePaymentFailureNotification(body);
428
- return new Response("OK");
429
- },
463
+ port: 3000,
464
+ async fetch(req) {
465
+ const form = await req.formData();
466
+ const body = Object.fromEntries(form.entries()) as any;
467
+
468
+ const yoAPI = new YoAPI("API_USERNAME", "API_PASSWORD", "sandbox");
469
+ const payment = yoAPI.receivePaymentNotification(body);
470
+ if (payment.is_verified) {
471
+ console.log(
472
+ `Payment from ${payment.msisdn} of ${payment.amount} (ref ${payment.external_ref})`,
473
+ );
474
+ // update your transaction status where external_ref = payment.external_ref
475
+ }
476
+
477
+ // Failure notifications:
478
+ // const failure = yoAPI.receivePaymentFailureNotification(body);
479
+ return new Response("OK");
480
+ },
430
481
  });
431
482
  ```
432
483
 
@@ -444,7 +495,11 @@ import "server-only";
444
495
  import { YoAPI } from "@herberthtk/yo-payments-api";
445
496
 
446
497
  export function getYoClient() {
447
- return new YoAPI(process.env.YO_API_USERNAME!, process.env.YO_API_PASSWORD!, "sandbox");
498
+ return new YoAPI(
499
+ process.env.YO_API_USERNAME!,
500
+ process.env.YO_API_PASSWORD!,
501
+ "sandbox",
502
+ );
448
503
  }
449
504
  ```
450
505
 
@@ -453,27 +508,18 @@ export function getYoClient() {
453
508
  import "server-only";
454
509
  import { getYoClient } from "@/lib/yo";
455
510
 
456
- export const runtime = "nodejs";
457
511
  export const dynamic = "force-dynamic";
512
+ export const runtime = "nodejs";
458
513
 
459
514
  export async function POST(req: Request) {
460
- const form = await req.formData();
461
- const body: Record<string, string> = {};
462
- for (const [k, v] of form.entries()) if (typeof v === "string") body[k] = v;
463
-
464
- const payment = getYoClient().receivePaymentNotification({
465
- date_time: body.date_time ?? "",
466
- amount: body.amount ?? "",
467
- narrative: body.narrative ?? "",
468
- network_ref: body.network_ref ?? "",
469
- external_ref: body.external_ref ?? "",
470
- msisdn: body.msisdn ?? "",
471
- signature: body.signature ?? "",
472
- });
473
- if (!payment.is_verified) return new Response("NOT VERIFIED", { status: 400 });
474
-
475
- // TODO: persist + mark processed idempotently on payment.external_ref
476
- return new Response("OK");
515
+ const form = await req.formData();
516
+ const body = Object.fromEntries(form.entries()) as any;
517
+ const payment = getYoClient().receivePaymentNotification(body);
518
+ if (!payment.is_verified)
519
+ return new Response("NOT VERIFIED", { status: 400 });
520
+
521
+ // TODO: persist + mark processed idempotently on payment.external_ref
522
+ return new Response("OK");
477
523
  }
478
524
  ```
479
525
 
@@ -482,12 +528,17 @@ export async function POST(req: Request) {
482
528
  "use server";
483
529
  import { getYoClient } from "@/lib/yo";
484
530
 
485
- export async function requestDeposit(msisdn: string, amount: number, narrative: string) {
486
- const api = getYoClient();
487
- api.setExternalReference(`${Date.now()}`);
488
- const res = await api.acDepositFunds(msisdn, amount, narrative);
489
- if (res.Status === "OK") return { ok: true, reference: res.TransactionReference };
490
- return { ok: false, message: res.StatusMessage };
531
+ export async function requestDeposit(
532
+ msisdn: string,
533
+ amount: number,
534
+ narrative: string,
535
+ ) {
536
+ const api = getYoClient();
537
+ api.setExternalReference(`${Date.now()}`);
538
+ const res = await api.acDepositFunds(msisdn, amount, narrative);
539
+ if (res.Status === "OK")
540
+ return { ok: true, reference: res.TransactionReference };
541
+ return { ok: false, message: res.StatusMessage };
491
542
  }
492
543
  ```
493
544
 
@@ -496,8 +547,14 @@ export async function requestDeposit(msisdn: string, amount: number, narrative:
496
547
  import { getYoClient } from "@/lib/yo";
497
548
 
498
549
  export default async function StatementPage() {
499
- const res = await getYoClient().acGetMinistatement(null, null, "SUCCEEDED", "UGX-MTNMM", 0);
500
- return <pre>{JSON.stringify(res.Transactions, null, 2)}</pre>;
550
+ const res = await getYoClient().acGetMinistatement(
551
+ null,
552
+ null,
553
+ "SUCCEEDED",
554
+ "UGX-MTNMM",
555
+ 0,
556
+ );
557
+ return <pre>{JSON.stringify(res.Transactions, null, 2)}</pre>;
501
558
  }
502
559
  ```
503
560
 
@@ -511,11 +568,11 @@ Transport-level and protocol-level failures throw `YoAPIError` (an `Error` subcl
511
568
  import { YoAPI, YoAPIError } from "@herberthtk/yo-payments-api";
512
569
 
513
570
  try {
514
- await yoAPI.acAcctBalance();
571
+ await yoAPI.acAcctBalance();
515
572
  } catch (e) {
516
- if (e instanceof YoAPIError) {
517
- console.error(e.message, "status:", e.status, "cause:", e.cause);
518
- }
573
+ if (e instanceof YoAPIError) {
574
+ console.error(e.message, "status:", e.status, "cause:", e.cause);
575
+ }
519
576
  }
520
577
  ```
521
578
 
@@ -566,7 +623,7 @@ The suite (`tests/YoAPI.test.ts`, `tests/examples.test.ts`, `tests/keys.test.ts`
566
623
 
567
624
  ### Releasing (maintainers)
568
625
 
569
- Versions follow [Conventional Commits](https://www.conventionalcommits.org/) (`feat:` → minor, `fix:` → patch, `feat!:`/`BREAKING CHANGE:` → major). To cut a release, run **Actions → Release → Run workflow** — release-it bumps the version, updates `CHANGELOG.md`, tags, creates the GitHub release and publishes to npm via trusted publishing (no npm token needed). First-time setup only: `npm login` + one manual `npm publish --access public`, then register the repo as a trusted publisher in the npm package settings.
626
+ Versions follow [Conventional Commits](https://www.conventionalcommits.org/) (`feat:` → minor, `fix:` → patch, `feat!:`/`BREAKING CHANGE:` → major). Before the first release, configure npm trusted publishing for `@herberthtk/yo-payments-api` in npm package settings: select GitHub Actions, set the repository to `herberthk/yo-payments-api`, and set the workflow filename to `release.yml` (not its full path). See npm's [trusted publishers guide](https://docs.npmjs.com/trusted-publishers/) for the full setup. Then run **Actions → Release → Run workflow** — release-it bumps the version, updates `CHANGELOG.md`, tags, creates the GitHub release and publishes to npm via trusted publishing (no npm token needed).
570
627
 
571
628
  ## Project structure
572
629
 
package/dist/index.cjs CHANGED
@@ -815,12 +815,12 @@ var YoAPI = class {
815
815
  maxResponseBytes: this.maxResponseBytes
816
816
  });
817
817
  }
818
- /** Verify the RSA-SHA256 signature on a payment notification against the Yo public certificate. */
818
+ /** Verify the RSA-SHA1 signature on a payment notification against the Yo public certificate. */
819
819
  verifyPaymentNotification(body) {
820
820
  const data = (body.date_time ?? "") + (body.amount ?? "") + (body.narrative ?? "") + (body.network_ref ?? "") + (body.external_ref ?? "") + (body.msisdn ?? "");
821
821
  return this.verifySignature(data, body.signature);
822
822
  }
823
- /** Verify the RSA-SHA256 signature on a payment failure notification against the Yo public certificate. */
823
+ /** Verify the RSA-SHA1 signature on a payment failure notification against the Yo public certificate. */
824
824
  verifyPaymentFailureNotification(body) {
825
825
  const data = (body.failed_transaction_reference ?? "") + (body.transaction_init_date ?? "");
826
826
  return this.verifySignature(data, body.verification);
@@ -833,7 +833,9 @@ var YoAPI = class {
833
833
  );
834
834
  if (publicKey === null) return false;
835
835
  try {
836
- return (0, import_node_crypto2.verify)("sha256", Buffer.from(data, "utf-8"), publicKey, Buffer.from(signatureBase64, "base64"));
836
+ const dataBuf = Buffer.from(data, "utf-8");
837
+ const sigBuf = Buffer.from(signatureBase64, "base64");
838
+ return (0, import_node_crypto2.verify)("sha1", dataBuf, publicKey, sigBuf) || (0, import_node_crypto2.verify)("sha256", dataBuf, publicKey, sigBuf);
837
839
  } catch {
838
840
  return false;
839
841
  }