From ea22f4e9d7a1e088819f3e0a41c4bf34c514025b Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 03:50:01 +0300 Subject: [PATCH 01/13] add the remittances class --- src/remittances.ts | 185 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 src/remittances.ts diff --git a/src/remittances.ts b/src/remittances.ts new file mode 100644 index 0000000..4f611e0 --- /dev/null +++ b/src/remittances.ts @@ -0,0 +1,185 @@ +import { AxiosInstance } from "axios"; +import {v4 as uuid} from "uuid"; + +import { getTransactionError } from "./errors"; +import { validateRemittance } from "./validate"; + +import { Balance, + FailureReason, + PartyIdType, + TransactionStatus + } from "./common"; + +export interface RemittanceRequest { + /** + * Unique Transfer Reference (UUID v4), will be automatically generated if not explicitly supplied + */ + + referenceId?: string; + /** + * Amount that will be debited from the payer account. + */ + amount: string; + + /** + * ISO4217 Currency + */ + currency: string; + + /** + * External id is used as a reference to the transaction. + * External id is used for reconciliation. + * The external id will be included in transaction history report. + * External id is not required to be unique. + */ + externalId?: string; + + /** + * Party identifies an account holder in the wallet platform. + * Party consists of two parameters, type and partyId. + * Each type have its own validation of the partyId + * MSISDN - Mobile Number validated according to ITU-T E.164. Validated with IsMSISDN + * EMAIL - Validated to be a valid e-mail format. Validated with IsEmail + * PARTY_CODE - UUID of the party. Validated with IsUuid + */ + payee: { + partyIdType: PartyIdType; + partyId: string; + }; + + /** + * Message that will be written in the payer transaction history message field. + */ + payerMessage?: string; + /** + * Message that will be written in the payee transaction history note field. + */ + payeeNote?: string; + /** + * URL to the server where the callback should be sent. + */ + callbackUrl?: string; +} + +export interface Remit { + /** + * Amount that will be debited from the payer account. + */ + amount: string; + + /** + * Financial transactionIdd from mobile money manager. + * Used to connect to the specific financial transaction made in the account + */ + financialTransactionId: string; + /** + * ISO4217 Currency + */ + currency: string; + + /** + * External id is used as a reference to the transaction. + * External id is used for reconciliation. + * The external id will be included in transaction history report. + * External id is not required to be unique. + */ + externalId: string; + + /** + * Party identifies a account holder in the wallet platform. + * Party consists of two parameters, type and partyId. + * Each type have its own validation of the partyId + * MSISDN - Mobile Number validated according to ITU-T E.164. Validated with IsMSISDN + * EMAIL - Validated to be a valid e-mail format. Validated with IsEmail + * PARTY_CODE - UUID of the party. Validated with IsUuid + */ + payee: { + partyIdType: "MSISDN"; + partyId: string; + }; + + status: TransactionStatus; + reason?: FailureReason; + +} + +export default class Remittances { + private client: AxiosInstance; + + constructor(client: AxiosInstance) { + this.client = client; + } + + /** + * Remit operation to send funds to local recipients from the diaspora + * from the owner's account to the payee's account + * + * @param remittanceRequest + */ + public remit({ + callbackUrl, + referenceId= uuid(), + ...remittanceRequest + }: RemittanceRequest): Promise<string> { + + return validateRemittance({referenceId, ...remittanceRequest}).then(() => { + return this.client + .post<void>("/remittance/v1_0/transfer", remittanceRequest, { + headers: { + "X-Reference-Id": referenceId, + ...(callbackUrl ? { "X-Callback-Url": callbackUrl } : {}) + } + }) + .then(() => referenceId); + }); + } + + /** + * This method is used to retrieve the transaction. You can invoke this method + * to at intervals until your transaction fails or succeeds. + * + * If the transaction has failed, it will throw an appropriate error. The error will be a subclass + * of `MtnMoMoError`. Check [`src/error.ts`](https://github.com/sparkplug/momoapi-node/blob/master/src/errors.ts) + * for the various errors that can be thrown + * + * @param referenceId the value returned from `remit` + */ + public getTransaction( referenceId: string): Promise<Remit> { + return this.client.get<Remit>(`/remittance/v1_0/transfer/${referenceId}`) + .then(response => response.data) + .then(transaction => { + if (transaction.status === TransactionStatus.FAILED) { + return Promise.reject(getTransactionError(transaction)); + } + + return Promise.resolve(transaction); + }); + } + + /** + * Get the balance of the account. + */ + public getBalance(): Promise<Balance> { + return this.client + .get<Balance>("/remittance/v1_0/account/balance") + .then(response => response.data); + } + + /** + * This method is used to check if an account holder is registered and active in the system. + * + * @param id Specifies the type of the party ID. Allowed values [msisdn, email, party_code]. + * accountHolderId should explicitly be in small letters. + * + * @param type The party number. Validated according to the party ID type (case Sensitive). + * msisdn - Mobile Number validated according to ITU-T E.164. Validated with IsMSISDN + * email - Validated to be a valid e-mail format. Validated with IsEmail + * party_code - UUID of the party. Validated with IsUuid + */ + public isPayerActive(id: string, type: PartyIdType = PartyIdType.MSISDN): Promise<boolean> { + return this.client + .get<{result: boolean}>(`/remittance/v1_0/accountholder/${String(type).toLowerCase()}/${id}/active`) + .then(response => response.data) + .then(data => data.result ? data.result : false); + } +} From 7aa75fa82f73cf2c72e2acfef97757bcbe3ff769 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 03:50:49 +0300 Subject: [PATCH 02/13] add the remittances class tests --- test/remittances.test.ts | 159 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 test/remittances.test.ts diff --git a/test/remittances.test.ts b/test/remittances.test.ts new file mode 100644 index 0000000..972f594 --- /dev/null +++ b/test/remittances.test.ts @@ -0,0 +1,159 @@ +import { AxiosInstance } from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { expect } from "chai"; + +import Remittances from "../src/remittances"; + +import { createMock } from "./mock"; + +import { PartyIdType } from "../src/common"; +import {RemittanceRequest} from "./../src/remittances"; + +describe("Remittances", function() { + let remittances: Remittances; + let mockAdapter: MockAdapter; + let mockClient: AxiosInstance; + + beforeEach(() => { + [mockClient, mockAdapter] = createMock(); + remittances = new Remittances(mockClient); + }); + + describe("remit", function() { + context("when the amount is missing", function() { + it("throws an error", function() { + const request = {} as RemittanceRequest; + return expect(remittances.remit(request)).to.be.rejectedWith( + "amount is required" + ); + }); + }); + + context("when the amount is not numeric", function() { + it("throws an error", function() { + const request = { amount: "alphabetic" } as RemittanceRequest; + return expect(remittances.remit(request)).to.be.rejectedWith( + "amount must be a number" + ); + }); + }); + + context("when the currency is missing", function() { + it("throws an error", function() { + const request = { + amount: "1000" + } as RemittanceRequest; + return expect(remittances.remit(request)).to.be.rejectedWith( + "currency is required" + ); + }); + }); + + context("when the payee is missing", function() { + it("throws an error", function() { + const request = { + amount: "1000", + currency: "UGX" + } as RemittanceRequest; + return expect(remittances.remit(request)).to.be.rejectedWith( + "payee is required" + ); + }); + }); + + context("when the party id is missing", function() { + it("throws an error", function() { + const request = { + amount: "1000", + currency: "UGX", + payee: { + } + } as RemittanceRequest; + return expect(remittances.remit(request)).to.be.rejectedWith( + "payee.partyId is required" + ); + }); + }); + + context("when the party id type is missing", function() { + it("throws an error", function() { + const request = { + amount: "1000", + currency: "UGX", + payee: { + partyId: "xxx", + } + } as RemittanceRequest; + return expect(remittances.remit(request)).to.be.rejectedWith( + "payee.partyIdType is required" + ); + }); + }); + + it("makes the correct request", function() { + const request: RemittanceRequest = { + amount: "50", + currency: "EUR", + externalId: "123456", + payee: { + partyIdType: PartyIdType.MSISDN, + partyId: "256774290781" + }, + payerMessage: "testing", + payeeNote: "hello" + }; + return expect( + remittances.remit({ ...request, callbackUrl: "callback url" }) + ).to.be.fulfilled.then(() => { + expect(mockAdapter.history.post).to.have.lengthOf(1); + expect(mockAdapter.history.post[0].url).to.eq( + "/remittance/v1_0/transfer" + ); + expect(mockAdapter.history.post[0].data).to.eq(JSON.stringify(request)); + expect(mockAdapter.history.post[0].headers["X-Reference-Id"]).to.be.a( + "string" + ); + expect(mockAdapter.history.post[0].headers["X-Callback-Url"]).to.eq( + "callback url" + ); + }); + }); + }); + + describe("getTransaction", function() { + it("makes the correct request", function() { + return expect( + remittances.getTransaction("reference") + ).to.be.fulfilled.then(() => { + expect(mockAdapter.history.get).to.have.lengthOf(1); + expect(mockAdapter.history.get[0].url).to.eq( + "/remittance/v1_0/transfer/reference" + ); + }); + }); + }); + + describe("getBalance", function() { + it("makes the correct request", function() { + return expect(remittances.getBalance()).to.be.fulfilled.then(() => { + expect(mockAdapter.history.get).to.have.lengthOf(1); + expect(mockAdapter.history.get[0].url).to.eq( + "/remittance/v1_0/account/balance" + ); + }); + }); + }); + + describe("isPayerActive", function() { + it("makes the correct request", function() { + return expect( + remittances.isPayerActive("0772000000", PartyIdType.MSISDN) + ).to.be.fulfilled.then(() => { + expect(mockAdapter.history.get).to.have.lengthOf(1); + expect(mockAdapter.history.get[0].url).to.eq( + "/remittance/v1_0/accountholder/msisdn/0772000000/active" + ); + }); + }); + }); +}); From 022c104dbfa1e6a6028f9a254fac3cdbe8ac809c Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 03:53:07 +0300 Subject: [PATCH 03/13] add auth for the remittance operation --- src/auth.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/auth.ts b/src/auth.ts index 9d07ea2..e5f5252 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -70,6 +70,20 @@ export const authorizeDisbursements: Authorizer = function( .then(response => response.data); }; +export const authorizeRemittances: Authorizer = function( + config: Config, + client: AxiosInstance = createClient(config) +): Promise<AccessToken> { + const basicAuthToken: string = createBasicAuthToken(config); + return client + .post<AccessToken>("/remittance/token/", null, { + headers: { + Authorization: `Basic ${basicAuthToken}` + } + }) + .then(response => response.data); +}; + export function createBasicAuthToken(config: UserConfig): string { return Buffer.from(`${config.userId}:${config.userSecret}`).toString( "base64" From f2d2a8b9eda63b2d4d86b09be60f3aa787e55735 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 03:54:04 +0300 Subject: [PATCH 04/13] add tests for auth - remittance operation --- test/auth.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/auth.test.ts b/test/auth.test.ts index 58c1d7e..804854c 100644 --- a/test/auth.test.ts +++ b/test/auth.test.ts @@ -5,6 +5,7 @@ import { expect } from "./chai"; import { authorizeCollections, authorizeDisbursements, + authorizeRemittances, createBasicAuthToken, createTokenRefresher } from "../src/auth"; @@ -85,6 +86,21 @@ describe("Auth", function() { }); }); + describe("authorizeRemittances", function() { + it("makes the correct request", function() { + const [mockClient, mockAdapter] = createMock(); + return expect( + authorizeRemittances(config, mockClient) + ).to.be.fulfilled.then(() => { + expect(mockAdapter.history.post).to.have.lengthOf(1); + expect(mockAdapter.history.post[0].url).to.eq("/remittance/token/"); + expect(mockAdapter.history.post[0].headers.Authorization).to.eq( + "Basic " + Buffer.from("id:secret").toString("base64") + ); + }); + }); + }); + describe("createBasicAuthToken", function() { it("encodes id and secret in base64", function() { expect( From 7c9ac8df02f38b0d9619ca9f75049ae3a0e07636 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 03:55:45 +0300 Subject: [PATCH 05/13] add validation for the remittance operation --- src/validate.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/validate.ts b/src/validate.ts index b875983..3857444 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -9,6 +9,7 @@ import { UserConfig } from "./common"; import { TransferRequest } from "./disbursements"; +import {RemittanceRequest} from "./remittances"; export function validateRequestToPay( paymentRequest: PaymentRequest @@ -50,6 +51,26 @@ export function validateTransfer( }); } +export function validateRemittance( remittanceRequest: RemittanceRequest +): Promise<void> { + const { amount, currency, payee, referenceId }: RemittanceRequest = remittanceRequest || {}; + return Promise.resolve().then(() => { + strictEqual(isTruthy(referenceId), true, "referenceId is required"); + strictEqual(isUuid4(referenceId as string), true, "referenceId must be a valid uuid v4"); + strictEqual(isTruthy(amount), true, "amount is required"); + strictEqual(isNumeric(amount), true, "amount must be a number"); + strictEqual(isTruthy(currency), true, "currency is required"); + strictEqual(isTruthy(payee), true, "payee is required"); + strictEqual(isTruthy(payee.partyId), true, "payee.partyId is required"); + strictEqual( + isTruthy(payee.partyIdType), + true, + "payee.partyIdType is required" + ); + strictEqual(isString(currency), true, "amount must be a string"); + }); +} + export function validateGlobalConfig(config: GlobalConfig): void { const { callbackHost, baseUrl, environment } = config; strictEqual(isTruthy(callbackHost), true, "callbackHost is required"); From 498ef91d83e254dd864736006fcc2d55a57e5100 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 03:57:29 +0300 Subject: [PATCH 06/13] add the remit inteface to error class --- src/errors.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/errors.ts b/src/errors.ts index b97024f..e2b3300 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -2,6 +2,7 @@ import { AxiosError } from "axios"; import { Payment } from "./collections"; import { FailureReason } from "./common"; import { Transfer } from "./disbursements"; +import {Remit} from "./remittances"; interface ErrorBody { code: FailureReason; @@ -9,7 +10,7 @@ interface ErrorBody { } export class MtnMoMoError extends Error { - public transaction?: Payment | Transfer; + public transaction?: Payment | Transfer | Remit; constructor(message?: string) { super(message); @@ -171,7 +172,7 @@ export function getError(code?: FailureReason, message?: string) { return new UnspecifiedError(message); } -export function getTransactionError(transaction: Payment | Transfer) { +export function getTransactionError(transaction: Payment | Transfer | Remit) { const error: MtnMoMoError = getError(transaction.reason as FailureReason); error.transaction = transaction; From 83febfbefb635d6c34f32a5dcba46214e7fd04d8 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 04:00:49 +0300 Subject: [PATCH 07/13] add remittance operation to global config --- src/index.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/index.ts b/src/index.ts index 6c4b298..04639a4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,7 @@ export { Payment, PaymentRequest } from "./collections"; export { Transfer, TransferRequest } from "./disbursements"; +export { Remit, RemittanceRequest } from "./remittances"; + export * from "./errors"; export { PartyIdType as PayerType, @@ -16,11 +18,13 @@ import { AxiosInstance } from "axios"; import Collections from "./collections"; import Disbursements from "./disbursements"; +import Remittances from "./remittances"; import Users from "./users"; import { authorizeCollections, authorizeDisbursements, + authorizeRemittances, createTokenRefresher } from "./auth"; import { createAuthClient, createClient } from "./client"; @@ -41,6 +45,7 @@ import { export interface MomoClient { Collections(productConfig: ProductConfig): Collections; Disbursements(productConfig: ProductConfig): Disbursements; + Remittances(productConfig: ProductConfig): Remittances; Users(subscription: SubscriptionConfig): Users; } @@ -89,6 +94,21 @@ export function create(globalConfig: GlobalConfig): MomoClient { return new Disbursements(client); }, + Remittances(productConfig: ProductConfig): Remittances { + const config: Config = { + ...defaultGlobalConfig, + ...globalConfig, + ...productConfig, + }; + + const client: AxiosInstance = createAuthClient( + createTokenRefresher(authorizeRemittances, config), + createClient(config) + ); + + return new Remittances(client); + }, + Users(subscriptionConfig: SubscriptionConfig): Users { validateSubscriptionConfig(subscriptionConfig); From e1b0a50efb20a6971165157ea8f628161978fdd9 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 04:01:44 +0300 Subject: [PATCH 08/13] add tests for remittance op. - global config --- test/index.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/index.test.ts b/test/index.test.ts index fa59087..2715aea 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -30,6 +30,12 @@ describe("MomoClient", function() { .to.have.property("Disbursements") .that.is.a("function"); }); + + it("returns a creator for Remittances client", function() { + expect(momo.create({ callbackHost: "example.com" })) + .to.have.property("Remittances") + .that.is.a("function"); + }); }); }); }); From 1fc148da49e3480a99b37db5910976f6ed73d5f2 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 04:02:52 +0300 Subject: [PATCH 09/13] mock the remittance endpoint --- test/mock.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/mock.ts b/test/mock.ts index 21532cd..6d6e772 100644 --- a/test/mock.ts +++ b/test/mock.ts @@ -4,6 +4,7 @@ import MockAdapter from "axios-mock-adapter"; import { Payment } from "../src/collections"; import { AccessToken, Balance, Credentials } from "../src/common"; import { Transfer } from "../src/disbursements"; +import {Remit} from "../src/remittances"; export function createMock(): [AxiosInstance, MockAdapter] { const client = axios.create({ @@ -84,5 +85,35 @@ export function createMock(): [AxiosInstance, MockAdapter] { currency: "UGX" } as Balance); + mock.onPost("/remittance/token/").reply(200, { + access_token: "token", + token_type: "access_token", + expires_in: 3600 + } as AccessToken); + + mock + .onGet( + /\/remittance\/v1_0\/accountholder\/(msisdn|email|party_code)\/\w+/ + ) + .reply(200, "true"); + + mock.onPost("/remittance/v1_0/transfer").reply(201); + + mock.onGet(/\/remittance\/v1_0\/transfer\/[\w\-]+/).reply(200, { + financialTransactionId: "tx id", + externalId: "string", + amount: "2000", + currency: "UGX", + payee: { + partyIdType: "MSISDN", + partyId: "256772000000" + }, + status: "SUCCESSFUL" + } as Remit); + + mock.onGet("/remittance/v1_0/account/balance").reply(200, { + availableBalance: "2000", + currency: "UGX" + } as Balance); return [client, mock]; } From fe5f872e049b27d08b561855a45c51d58f9d1d53 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Fri, 15 Apr 2022 04:03:27 +0300 Subject: [PATCH 10/13] add example for the remittance operation --- examples/remittances.js | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/remittances.js diff --git a/examples/remittances.js b/examples/remittances.js new file mode 100644 index 0000000..3cacf79 --- /dev/null +++ b/examples/remittances.js @@ -0,0 +1,51 @@ +const momo = require("../lib"); + +const { Remittances } = momo.create({ callbackHost: process.env.CALLBACK_HOST }); + +// initialise collections +const remittances = Remittances({ + userSecret: process.env.REMITTANCES_USER_SECRET, + userId: process.env.REMITTANCES_USER_ID, + primaryKey: process.env.REMITTANCES_PRIMARY_KEY +}); + +const partyId = "256776564739"; +const partyIdType = momo.PayerType.MSISDN; +// Transfer +remittances + .isPayerActive(partyId, partyIdType) + .then((isActive) => { + console.log("Is Active ? ", isActive); + if (!isActive) { + return Promise.reject("Party not active"); + } + return remittances.remit({ + amount: "100", + currency: "EUR", + externalId: "947354", + payee: { + partyIdType, + partyId + }, + payerMessage: "testing", + payeeNote: "hello", + callbackUrl: process.env.CALLBACK_URL ? process.env.CALLBACK_URL : "https://75f59b50.ngrok.io" + }); + }) + + .then(transactionId => { + console.log({ transactionId }); + + // Get transaction status + return remittances.getTransaction(transactionId); + }) + .then(transaction => { + console.log({ transaction }); + + // Get account balance + return remittances.getBalance(); + }) + .then(accountBalance => console.log({ accountBalance })) + .catch(error => { + console.log(error); + }); From d4f1d197eb8b14799b145b8ee2c3c668067c55bf Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Tue, 19 Apr 2022 09:26:00 +0300 Subject: [PATCH 11/13] reject promise with error, remove ngrok url --- examples/remittances.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/remittances.js b/examples/remittances.js index 3cacf79..d71eaab 100644 --- a/examples/remittances.js +++ b/examples/remittances.js @@ -17,7 +17,7 @@ remittances .then((isActive) => { console.log("Is Active ? ", isActive); if (!isActive) { - return Promise.reject("Party not active"); + return Promise.reject( new Error("Party not active")); } return remittances.remit({ amount: "100", @@ -29,7 +29,7 @@ remittances }, payerMessage: "testing", payeeNote: "hello", - callbackUrl: process.env.CALLBACK_URL ? process.env.CALLBACK_URL : "https://75f59b50.ngrok.io" + callbackUrl: process.env.CALLBACK_URL }); }) From 444010261cf2dd2fbb0a74ef1badd55dfa96bf35 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Wed, 20 Apr 2022 12:44:18 +0300 Subject: [PATCH 12/13] fix: remove type coercion --- src/validate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validate.ts b/src/validate.ts index 3857444..fc183e5 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -56,7 +56,7 @@ export function validateRemittance( remittanceRequest: RemittanceRequest const { amount, currency, payee, referenceId }: RemittanceRequest = remittanceRequest || {}; return Promise.resolve().then(() => { strictEqual(isTruthy(referenceId), true, "referenceId is required"); - strictEqual(isUuid4(referenceId as string), true, "referenceId must be a valid uuid v4"); + strictEqual(isUuid4(referenceId), true, "referenceId must be a valid uuid v4"); strictEqual(isTruthy(amount), true, "amount is required"); strictEqual(isNumeric(amount), true, "amount must be a number"); strictEqual(isTruthy(currency), true, "currency is required"); From 74fc4f5aa68186679d6db8de99e711ad54aa6eb3 Mon Sep 17 00:00:00 2001 From: Luigi Morel <morelmiles@gmail.com> Date: Wed, 20 Apr 2022 12:45:26 +0300 Subject: [PATCH 13/13] fix:changes referenceId type to strictly string --- src/remittances.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remittances.ts b/src/remittances.ts index 4f611e0..c4349ba 100644 --- a/src/remittances.ts +++ b/src/remittances.ts @@ -15,7 +15,7 @@ export interface RemittanceRequest { * Unique Transfer Reference (UUID v4), will be automatically generated if not explicitly supplied */ - referenceId?: string; + referenceId: string; /** * Amount that will be debited from the payer account. */