@herberthtk/yo-payments-api 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/certs/Yo_Uganda_Public_Certificate.crt +28 -0
- package/certs/Yo_Uganda_Public_Sandbox_Certificate.crt +35 -0
- package/dist/index.cjs +857 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +391 -0
- package/dist/index.d.ts +391 -0
- package/dist/index.js +828 -0
- package/dist/index.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/YoAPI.ts","../src/constants.ts","../src/embeddedCerts.ts","../src/http.ts","../src/keys.ts","../src/xml.ts"],"sourcesContent":["/**\n * Error thrown for transport-level and protocol-level failures:\n * connection errors, timeouts, non-2xx HTTP statuses, oversized bodies,\n * malformed XML and responses missing the <Response> node.\n * Gateway-level business failures (e.g. Status FAILED) are still returned\n * as normal response objects, exactly like the PHP library.\n */\nexport class YoAPIError extends Error {\n /** HTTP status code when the failure came with an HTTP response. */\n readonly status?: number;\n /** Truncated response body (up to 500 chars) when one was received. */\n readonly body?: string;\n\n constructor(message: string, options?: { status?: number; body?: string; cause?: unknown }) {\n super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n this.name = \"YoAPIError\";\n this.status = options?.status;\n this.body = options?.body;\n }\n}\n","import { createHash, createPrivateKey, sign as rsaSign, verify as rsaVerify } from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n CERTS_DIR,\n DEFAULT_MAX_RESPONSE_BYTES,\n PRODUCTION_URL,\n PUBLIC_KEY_FILE_FOR_PRODUCTION,\n PUBLIC_KEY_FILE_FOR_SANDBOX,\n SANDBOX_URL,\n defaultVerificationCertificate,\n} from \"./constants.ts\";\nimport { postXml } from \"./http.ts\";\nimport { loadPublicKeyCached } from \"./keys.ts\";\nimport type {\n AcctBalanceResponse,\n DepositFundsResponse,\n DepositTransactionType,\n InternalTransferResponse,\n MinistatementResponse,\n MsisdnKycInfoResponse,\n NonBlocking,\n PaymentFailureNotificationBody,\n PaymentFailureNotificationResult,\n PaymentNotificationBody,\n PaymentNotificationResult,\n PurchaseAirtimeStockResponse,\n SendAirtimeResponse,\n TransactionCheckStatusResponse,\n TransactionDetail,\n YoMode,\n} from \"./types.ts\";\nimport {\n XML_HEADER,\n asArray,\n asRecord,\n el,\n opt,\n parseGatewayResponse,\n setIfNonEmpty,\n setIfNotNull,\n str,\n} from \"./xml.ts\";\nimport type { XmlNode } from \"./xml.ts\";\n\n/**\n * Yo! Payments API client (TypeScript port of the official PHP library YoAPI.php).\n *\n * Values are inserted into the request XML verbatim (exactly like the PHP library),\n * so any special XML characters in narratives, references or notification URLs must\n * be escaped by the caller.\n */\nexport class YoAPI {\n /** The Yo! Payments API Username. Required. */\n private username: string;\n\n /** The Yo! Payments API Password. Required. */\n private password: string;\n\n /** Whether the gateway connection is held open until the request completes. Default \"FALSE\". */\n private nonBlocking: NonBlocking = \"FALSE\";\n\n /** An externally agreed reference (e.g. an invoice number). */\n private externalReference: string | null = null;\n\n /** A reference code related to another Yo! Payments system transaction. */\n private internalReference: string | null = null;\n\n /** Text appended to the confirmation SMS sent by the mobile money provider. */\n private providerReferenceText: string | null = null;\n\n /** URL notified as soon as funds are successfully deposited into your account. */\n private instantNotificationUrl: string | null = null;\n\n /** URL notified as soon as a deposit request fails. */\n private failureNotificationUrl: string | null = null;\n\n /** May be required to authenticate certain deposit requests. */\n private authenticationSignatureBase64: string | null = null;\n\n /** \"PULL\" or \"PUSH\". Default \"PULL\". */\n private depositTransactionType: DepositTransactionType = \"PULL\";\n\n /** The URL API requests are submitted to. */\n private yoUrl: string = PRODUCTION_URL;\n\n /** Certificate used to verify IPN signatures (sandbox or production). */\n private publicKeyFile: string;\n\n /** Whether publicKeyFile is still the bundled default (enables the embedded-cert fallback). */\n private publicKeyFileIsDefault = true;\n\n private transactionLimitAccountIdentifier: string | null = null;\n\n /** Unique nonce per request, required when public key authentication is enabled. */\n private publicKeyAuthenticationNonce: string | null = null;\n\n /** Base64 RSA signature over SHA1(username+amount+account+narrative+external_ref+nonce). */\n private publicKeyAuthenticationSignatureBase64: string | null = null;\n\n /** Location of the private key used to sign the public key authentication signature. */\n private privateKeyFileLocation: string | null = null;\n\n /**\n * Private key PEM content used to sign the public key authentication signature.\n * Prefer this over a file location on serverless/bundled hosts where the\n * filesystem is ephemeral (e.g. Vercel). Takes precedence when both are set.\n */\n private privateKeyContent: string | null = null;\n\n private readonly mode: YoMode;\n\n /** Request timeout in milliseconds (PHP library uses curl timeout 120s). <= 0 means no timeout, like curl. */\n private timeoutMs: number = 120_000;\n\n /**\n * Whether to verify the gateway TLS certificate. Default true.\n * The PHP library disables peer verification; this port verifies by default and\n * only skips verification when explicitly opted out via setTlsVerificationEnabled(false).\n */\n private verifyTls: boolean = true;\n\n /** Maximum accepted gateway response body in bytes (default 1 MiB). */\n private maxResponseBytes: number = DEFAULT_MAX_RESPONSE_BYTES;\n\n constructor(username: string, password: string, mode: YoMode = \"production\") {\n this.username = username;\n this.password = password;\n this.mode = mode;\n\n if (mode === \"sandbox\") {\n this.yoUrl = SANDBOX_URL;\n this.publicKeyFile = join(CERTS_DIR, PUBLIC_KEY_FILE_FOR_SANDBOX);\n } else {\n this.yoUrl = PRODUCTION_URL;\n this.publicKeyFile = join(CERTS_DIR, PUBLIC_KEY_FILE_FOR_PRODUCTION);\n }\n }\n\n /** Returns the mode (\"production\" or \"sandbox\") this instance was created with. */\n getMode(): YoMode {\n return this.mode;\n }\n\n /** Set the API Username. */\n setUsername(username: string): void {\n this.username = username;\n }\n\n /** Returns the API Username. */\n getUsername(): string {\n return this.username;\n }\n\n /** Set the API Password. */\n setPassword(password: string): void {\n this.password = password;\n }\n\n /** Returns the API Password. */\n getPassword(): string {\n return this.password;\n }\n\n /** Set the URL to submit API requests to. */\n setUrl(url: string): void {\n this.yoUrl = url;\n }\n\n /** Returns the URL API requests are submitted to. */\n getUrl(): string {\n return this.yoUrl;\n }\n\n /** Set the path of the certificate used to verify IPN signatures. */\n setPublicKeyFileUrl(publicKeyFileUrl: string): void {\n this.publicKeyFile = publicKeyFileUrl;\n this.publicKeyFileIsDefault = false;\n }\n\n /** Returns the path of the certificate used to verify IPN signatures. */\n getPublicKeyFileUrl(): string {\n return this.publicKeyFile;\n }\n\n /** Set the NonBlocking variable: \"TRUE\" for non-blocking API requests. */\n setNonblocking(nonblocking: NonBlocking): void {\n this.nonBlocking = nonblocking;\n }\n\n /** Returns the NonBlocking variable. */\n getNonblocking(): NonBlocking {\n return this.nonBlocking;\n }\n\n /** Set the External Reference used when submitting payment requests. */\n setExternalReference(externalReference: string | null): void {\n this.externalReference = externalReference;\n }\n\n /** Returns the externalReference variable. */\n getExternalReference(): string | null {\n return this.externalReference;\n }\n\n /** Set the Internal Reference used when submitting payment requests. */\n setInternalReference(internalReference: string | null): void {\n this.internalReference = internalReference;\n }\n\n /** Returns the internalReference variable. */\n getInternalReference(): string | null {\n return this.internalReference;\n }\n\n /** Set the Provider Reference Text used when submitting payment requests. */\n setProviderReferenceText(providerReferenceText: string | null): void {\n this.providerReferenceText = providerReferenceText;\n }\n\n /** Returns the providerReferenceText variable. */\n getProviderReferenceText(): string | null {\n return this.providerReferenceText;\n }\n\n /** Set the Instant Notification URL (useful for non-blocking requests). */\n setInstantNotificationUrl(instantNotificationUrl: string | null): void {\n this.instantNotificationUrl = instantNotificationUrl;\n }\n\n /** Returns the instantNotificationUrl variable. */\n getInstantNotificationUrl(): string | null {\n return this.instantNotificationUrl;\n }\n\n /** Set the Failure Notification URL (useful for non-blocking requests). */\n setFailureNotificationUrl(failureNotificationUrl: string | null): void {\n this.failureNotificationUrl = failureNotificationUrl;\n }\n\n /** Returns the failureNotificationUrl variable. */\n getFailureNotificationUrl(): string | null {\n return this.failureNotificationUrl;\n }\n\n /** Set the Authentication Signature Base64. */\n setAuthenticationSignatureBase64(authenticationSignatureBase64: string | null): void {\n this.authenticationSignatureBase64 = authenticationSignatureBase64;\n }\n\n /** Returns the Authentication Signature Base64 variable. */\n getAuthenticationSignatureBase64(): string | null {\n return this.authenticationSignatureBase64;\n }\n\n /** Set the Deposit Transaction Type (\"PULL\" or \"PUSH\") used by acTransactionCheckStatus. */\n setDepositTransactionType(depositTransactionType: DepositTransactionType): void {\n this.depositTransactionType = depositTransactionType;\n }\n\n /** Returns the Deposit Transaction Type variable. */\n getDepositTransactionType(): DepositTransactionType {\n return this.depositTransactionType;\n }\n\n /** Set the Transaction Limit Account Identifier (refer to your account administrator). */\n setTransactionLimitAccountIdentifier(transactionLimitAccountIdentifier: string | null): void {\n this.transactionLimitAccountIdentifier = transactionLimitAccountIdentifier;\n }\n\n /** Returns the Transaction Limit Account Identifier variable. */\n getTransactionLimitAccountIdentifier(): string | null {\n return this.transactionLimitAccountIdentifier;\n }\n\n /** Set the Public Key Authentication Nonce (refer to your account administrator). */\n setPublicKeyAuthenticationNonce(publicKeyAuthenticationNonce: string | null): void {\n this.publicKeyAuthenticationNonce = publicKeyAuthenticationNonce;\n }\n\n /** Returns the Public Key Authentication Nonce variable. */\n getPublicKeyAuthenticationNonce(): string | null {\n return this.publicKeyAuthenticationNonce;\n }\n\n /** Set the Public Key Authentication Base64-Encoded Signature (refer to your account administrator). */\n setPublicKeyAuthenticationSignatureBase64(publicKeyAuthenticationSignatureBase64: string | null): void {\n this.publicKeyAuthenticationSignatureBase64 = publicKeyAuthenticationSignatureBase64;\n }\n\n /** Returns the Public Key Authentication Base64-Encoded Signature variable. */\n getPublicKeyAuthenticationSignatureBase64(): string | null {\n return this.publicKeyAuthenticationSignatureBase64;\n }\n\n /** Set the location of the private key used to sign the public key authentication signature. */\n setPrivateKeyFileLocation(privateKeyFileLocation: string | null): void {\n this.privateKeyFileLocation = privateKeyFileLocation;\n }\n\n /** Returns the Private Key File variable. */\n getPrivateKeyFileLocation(): string | null {\n return this.privateKeyFileLocation;\n }\n\n /**\n * Set the private key PEM content directly (alternative to setPrivateKeyFileLocation).\n * Useful where key files are unavailable, e.g. serverless deployments reading\n * the key from an environment variable. Takes precedence when both are set.\n */\n setPrivateKeyContent(privateKeyContent: string | null): void {\n this.privateKeyContent = privateKeyContent;\n }\n\n /** Returns the Private Key PEM content variable. */\n getPrivateKeyContent(): string | null {\n return this.privateKeyContent;\n }\n\n /** Set the request timeout in milliseconds. Values <= 0 disable the timeout (like PHP curl timeout 0). */\n setTimeout(timeoutMs: number): void {\n this.timeoutMs = timeoutMs;\n }\n\n /** Returns the request timeout in milliseconds. */\n getTimeout(): number {\n return this.timeoutMs;\n }\n\n /**\n * Enable or disable verification of the gateway TLS certificate (default enabled).\n * Disable only for testing against endpoints with self-signed certificates —\n * the PHP library always skips verification.\n * Note: the underlying mechanism is a Bun fetch extension; on Node.js, disabling\n * verification additionally requires NODE_TLS_REJECT_UNAUTHORIZED=0 in the environment.\n */\n setTlsVerificationEnabled(enabled: boolean): void {\n this.verifyTls = enabled;\n }\n\n /** Returns whether gateway TLS certificate verification is enabled. */\n getTlsVerificationEnabled(): boolean {\n return this.verifyTls;\n }\n\n /** Set the maximum accepted gateway response body in bytes (default 1048576). */\n setMaxResponseBytes(maxResponseBytes: number): void {\n this.maxResponseBytes = maxResponseBytes;\n }\n\n /** Returns the maximum accepted gateway response body in bytes. */\n getMaxResponseBytes(): number {\n return this.maxResponseBytes;\n }\n\n /**\n * Request Mobile Money User to deposit funds into your account.\n * Shortly after submitting, the mobile money user receives an on-screen prompt to\n * authorize the transfer. Not supported by all mobile money operator networks.\n * @param msisdn the mobile money phone number in the format 256772123456\n * @param amount the amount to deposit into your account (fractions supported)\n * @param narrative the reason for the mobile money user to deposit funds\n */\n async acDepositFunds(msisdn: string, amount: number | string, narrative: string): Promise<DepositFundsResponse> {\n const xml = this.requestXml(\n this.authXml() +\n el(\"Method\", \"acdepositfunds\") +\n el(\"NonBlocking\", this.nonBlocking) +\n el(\"Account\", msisdn) +\n el(\"Amount\", amount) +\n el(\"Narrative\", narrative) +\n opt(\"ExternalReference\", this.externalReference) +\n opt(\"InternalReference\", this.internalReference) +\n opt(\"ProviderReferenceText\", this.providerReferenceText) +\n opt(\"InstantNotificationUrl\", this.instantNotificationUrl) +\n opt(\"FailureNotificationUrl\", this.failureNotificationUrl) +\n opt(\"AuthenticationSignatureBase64\", this.authenticationSignatureBase64),\n );\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: DepositFundsResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n StatusMessage: str(response.StatusMessage),\n TransactionStatus: str(response.TransactionStatus),\n };\n setIfNonEmpty(result, \"ErrorMessageCode\", str(response.ErrorMessageCode));\n setIfNonEmpty(result, \"ErrorMessage\", str(response.ErrorMessage));\n setIfNonEmpty(result, \"TransactionReference\", str(response.TransactionReference));\n setIfNonEmpty(result, \"MNOTransactionReferenceId\", str(response.MNOTransactionReferenceId));\n setIfNonEmpty(result, \"IssuedReceiptNumber\", str(response.IssuedReceiptNumber));\n\n return result;\n }\n\n /**\n * Check the status of a transaction that was earlier submitted for processing.\n * Particularly useful when NonBlocking is \"TRUE\".\n * @param transactionReference the gateway reference uniquely identifying the transaction\n * @param privateTransactionReference the External Reference used to carry out the transaction\n */\n async acTransactionCheckStatus(\n transactionReference: string | null,\n privateTransactionReference: string | null = null,\n ): Promise<TransactionCheckStatusResponse> {\n const xml = this.requestXml(\n this.authXml() +\n el(\"Method\", \"actransactioncheckstatus\") +\n opt(\"TransactionReference\", transactionReference) +\n opt(\"PrivateTransactionReference\", privateTransactionReference) +\n el(\"DepositTransactionType\", this.depositTransactionType),\n );\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: TransactionCheckStatusResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n StatusMessage: str(response.StatusMessage),\n TransactionStatus: str(response.TransactionStatus),\n };\n setIfNonEmpty(result, \"ErrorMessageCode\", str(response.ErrorMessageCode));\n setIfNonEmpty(result, \"ErrorMessage\", str(response.ErrorMessage));\n setIfNonEmpty(result, \"TransactionReference\", str(response.TransactionReference));\n setIfNonEmpty(result, \"MNOTransactionReferenceId\", str(response.MNOTransactionReferenceId));\n setIfNonEmpty(result, \"Amount\", str(response.Amount));\n setIfNonEmpty(result, \"AmountFormatted\", str(response.AmountFormatted));\n setIfNonEmpty(result, \"CurrencyCode\", str(response.CurrencyCode));\n setIfNonEmpty(result, \"TransactionInitiationDate\", str(response.TransactionInitiationDate));\n setIfNonEmpty(result, \"TransactionCompletionDate\", str(response.TransactionCompletionDate));\n setIfNonEmpty(result, \"IssuedReceiptNumber\", str(response.IssuedReceiptNumber));\n\n return result;\n }\n\n /**\n * Transfer funds from your Payment Account to another Yo! Payments Account.\n * @param currencyCode e.g. \"UGX-MTNMM\", \"UGX-MTNAT\", \"UGX-WTLAT\", \"UGX-OULAT\", \"UGX-AIRAT\"\n * @param amount the amount to be transferred\n * @param beneficiaryAccount account number of the beneficiary Yo! Payments user\n * @param beneficiaryEmail email address of the recipient of funds\n * @param narrative textual narrative about the transaction\n */\n async acInternalTransfer(\n currencyCode: string,\n amount: number | string,\n beneficiaryAccount: number | string,\n beneficiaryEmail: string,\n narrative: string,\n ): Promise<InternalTransferResponse> {\n const xml = this.requestXml(\n this.authXml() +\n el(\"Method\", \"acinternaltransfer\") +\n el(\"CurrencyCode\", currencyCode) +\n el(\"Amount\", amount) +\n el(\"BeneficiaryAccount\", beneficiaryAccount) +\n el(\"BeneficiaryEmail\", beneficiaryEmail) +\n el(\"Narrative\", narrative) +\n opt(\"InternalReference\", this.internalReference) +\n opt(\"ExternalReference\", this.externalReference),\n );\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: InternalTransferResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n StatusMessage: str(response.StatusMessage),\n TransactionStatus: str(response.TransactionStatus),\n };\n setIfNonEmpty(result, \"ErrorMessageCode\", str(response.ErrorMessageCode));\n setIfNonEmpty(result, \"ErrorMessage\", str(response.ErrorMessage));\n setIfNonEmpty(result, \"TransactionReference\", str(response.TransactionReference));\n setIfNonEmpty(result, \"MNOTransactionReferenceId\", str(response.MNOTransactionReferenceId));\n setIfNonEmpty(result, \"IssuedReceiptNumber\", str(response.IssuedReceiptNumber));\n\n return result;\n }\n\n /**\n * Get the current balance of your Yo! Payments Account.\n * The returned object contains an array of balances (including airtime).\n */\n async acAcctBalance(): Promise<AcctBalanceResponse> {\n const xml = this.requestXml(this.authXml() + el(\"Method\", \"acacctbalance\"));\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: AcctBalanceResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n balance: [],\n };\n\n const currencies = asArray(asRecord(asRecord(response.Balance).Currency));\n for (const currency of currencies) {\n const node = asRecord(currency);\n result.balance.push({ code: str(node.Code), balance: str(node.Balance) });\n }\n\n setIfNonEmpty(result, \"StatusMessage\", str(response.StatusMessage));\n setIfNonEmpty(result, \"ErrorMessageCode\", str(response.ErrorMessageCode));\n setIfNonEmpty(result, \"ErrorMessage\", str(response.ErrorMessage));\n\n return result;\n }\n\n /**\n * Return transactions carried out on your account for a certain period of time.\n * @param startDate format YYYY-MM-DD HH:MM:SS\n * @param endDate format YYYY-MM-DD HH:MM:SS\n * @param transactionStatus e.g. \"FAILED\", \"PENDING\", \"INDETERMINATE\", \"SUCCEEDED\", \"FAILED,SUCCEEDED\"\n * @param currencyCode e.g. \"UGX-MTNMM\", \"UGX-WARIDMM\", \"UGX-MTNAT\", \"UGX-WTLAT\", \"UGX-OULAT\", \"UGX-AIRAT\"\n * @param resultSetLimit a value of 0 returns all; default gateway limit = 15\n * @param transactionEntryDesignation \"TRANSACTION\", \"CHARGES\" or \"ANY\"\n * @param externalReference filter using this external reference\n */\n async acGetMinistatement(\n startDate: string | null = null,\n endDate: string | null = null,\n transactionStatus: string | null = null,\n currencyCode: string | null = null,\n resultSetLimit: number | null = null,\n transactionEntryDesignation: string = \"ANY\",\n externalReference: string | null = null,\n ): Promise<MinistatementResponse> {\n const xml = this.requestXml(\n this.authXml() +\n el(\"Method\", \"acgetministatement\") +\n opt(\"StartDate\", startDate) +\n opt(\"EndDate\", endDate) +\n opt(\"TransactionStatus\", transactionStatus) +\n opt(\"CurrencyCode\", currencyCode) +\n opt(\"ResultSetLimit\", resultSetLimit) +\n el(\"TransactionEntryDesignation\", transactionEntryDesignation) +\n opt(\"ExternalReference\", externalReference),\n );\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: MinistatementResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n TotalTransactions: str(response.TotalTransactions),\n ReturnedTransactions: str(response.ReturnedTransactions),\n Transactions: [],\n };\n\n const transactions = asArray(asRecord(response.Transactions).Transaction);\n for (const transaction of transactions) {\n const node = asRecord(transaction);\n const detail: TransactionDetail = {\n TransactionSystemId: str(node.TransactionSystemId),\n TransactionReference: str(node.TransactionReference),\n TransactionStatus: str(node.TransactionStatus),\n InitiationDate: str(node.InitiationDate),\n CompletionDate: str(node.CompletionDate),\n NarrativeBase64: str(asArray(node.NarrativeBase64)[0]),\n Currency: str(node.Currency),\n Amount: str(node.Amount),\n Balance: str(node.Balance),\n GeneralType: str(node.GeneralType),\n DetailedType: str(node.DetailedType),\n BeneficiaryBase64: str(node.BeneficiaryBase64),\n SenderBase64: str(node.SenderBase64),\n TransactionEntryDesignation: str(node.TransactionEntryDesignation),\n };\n setIfNonEmpty(detail, \"BeneficiaryMsisdn\", str(node.BeneficiaryMsisdn));\n setIfNonEmpty(detail, \"SenderMsisdn\", str(node.SenderMsisdn));\n setIfNonEmpty(detail, \"Base64TransactionExternalReference\", str(node.Base64TransactionExternalReference));\n\n result.Transactions.push(detail);\n }\n\n setIfNonEmpty(result, \"ErrorMessageCode\", str(response.ErrorMessageCode));\n setIfNonEmpty(result, \"ErrorMessage\", str(response.ErrorMessage));\n\n return result;\n }\n\n /**\n * Send airtime to a mobile phone user.\n * @param msisdn the mobile phone number in the format 256772123456\n * @param amount the amount of airtime to be sent to the mobile user\n * @param narrative textual narrative about the transfer\n */\n async acSendAirtimeMobile(msisdn: string, amount: number | string, narrative: string): Promise<SendAirtimeResponse> {\n const xml = this.requestXml(\n this.authXml() +\n el(\"Method\", \"acsendairtimemobile\") +\n el(\"NonBlocking\", this.nonBlocking) +\n el(\"Account\", msisdn) +\n el(\"Amount\", amount) +\n el(\"Narrative\", narrative) +\n opt(\"ExternalReference\", this.externalReference) +\n opt(\"InternalReference\", this.internalReference) +\n opt(\"ProviderReferenceText\", this.providerReferenceText),\n );\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: SendAirtimeResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n StatusMessage: str(response.StatusMessage),\n TransactionStatus: str(response.TransactionStatus),\n };\n setIfNotNull(result, \"ErrorMessageCode\", response.ErrorMessageCode);\n setIfNotNull(result, \"ErrorMessage\", response.ErrorMessage);\n setIfNotNull(result, \"TransactionReference\", response.TransactionReference);\n setIfNotNull(result, \"MNOTransactionReferenceId\", response.MNOTransactionReferenceId);\n setIfNotNull(result, \"IssuedReceiptNumber\", response.IssuedReceiptNumber);\n\n return result;\n }\n\n /**\n * Send airtime from your Yo! Payments account to another Yo! Payments user account.\n * @param currencyCode e.g. \"UGX-MTNAT\", \"UGX-WTLAT\", \"UGX-OULAT\", \"UGX-AIRAT\"\n * @param amount the amount of airtime to be sent to the beneficiary Yo! Payments user\n * @param beneficiaryAccount the beneficiary Yo! Payments account number\n * @param beneficiaryEmail the beneficiary email address\n * @param narrative textual narrative about the transfer\n */\n async acSendAirtimeInternal(\n currencyCode: string,\n amount: number | string,\n beneficiaryAccount: number | string,\n beneficiaryEmail: string,\n narrative: string,\n ): Promise<SendAirtimeResponse> {\n const xml = this.requestXml(\n this.authXml() +\n el(\"Method\", \"acsendairtimeinternal\") +\n el(\"CurrencyCode\", currencyCode) +\n el(\"Amount\", amount) +\n el(\"BeneficiaryAccount\", beneficiaryAccount) +\n el(\"BeneficiaryEmail\", beneficiaryEmail) +\n el(\"Narrative\", narrative) +\n opt(\"InternalReference\", this.internalReference) +\n opt(\"ExternalReference\", this.externalReference),\n );\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: SendAirtimeResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n StatusMessage: str(response.StatusMessage),\n TransactionStatus: str(response.TransactionStatus),\n };\n setIfNotNull(result, \"ErrorMessageCode\", response.ErrorMessageCode);\n setIfNotNull(result, \"ErrorMessage\", response.ErrorMessage);\n setIfNotNull(result, \"TransactionReference\", response.TransactionReference);\n setIfNotNull(result, \"MNOTransactionReferenceId\", response.MNOTransactionReferenceId);\n setIfNotNull(result, \"IssuedReceiptNumber\", response.IssuedReceiptNumber);\n\n return result;\n }\n\n /**\n * Withdraw funds from your Yo! Payments Account to a mobile money user.\n * Handle with care: if compromised, it can lead to withdrawal of funds from your account.\n * Requires permission granted by the issuance of an API Access Letter.\n * @param msisdn the mobile money phone number in the format 256772123456\n * @param amount the amount to withdraw from your account (fractions supported)\n * @param narrative the reason for withdrawal of funds from your account\n */\n async acWithdrawFunds(msisdn: string, amount: number | string, narrative: string): Promise<DepositFundsResponse> {\n const xml = this.requestXml(\n this.authXml() +\n el(\"Method\", \"acwithdrawfunds\") +\n el(\"NonBlocking\", this.nonBlocking) +\n el(\"Account\", msisdn) +\n el(\"Amount\", amount) +\n el(\"Narrative\", narrative) +\n opt(\"ExternalReference\", this.externalReference) +\n opt(\"InternalReference\", this.internalReference) +\n opt(\"ProviderReferenceText\", this.providerReferenceText) +\n opt(\"TransactionLimitAccountIdentifier\", this.transactionLimitAccountIdentifier) +\n opt(\"PublicKeyAuthenticationNonce\", this.publicKeyAuthenticationNonce) +\n opt(\"PublicKeyAuthenticationSignatureBase64\", this.publicKeyAuthenticationSignatureBase64),\n );\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: DepositFundsResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n StatusMessage: str(response.StatusMessage),\n TransactionStatus: str(response.TransactionStatus),\n };\n setIfNonEmpty(result, \"ErrorMessageCode\", str(response.ErrorMessageCode));\n setIfNonEmpty(result, \"ErrorMessage\", str(response.ErrorMessage));\n setIfNonEmpty(result, \"TransactionReference\", str(response.TransactionReference));\n setIfNonEmpty(result, \"MNOTransactionReferenceId\", str(response.MNOTransactionReferenceId));\n setIfNonEmpty(result, \"IssuedReceiptNumber\", str(response.IssuedReceiptNumber));\n\n return result;\n }\n\n /**\n * Purchase airtime using your Mobile Money Credit.\n * @param airtimeCurrencyCode e.g. \"UGX-MTNAT\", \"UGX-AIRAT\", \"UGX-OULAT\", \"UGX-UTLAT\", \"UGX-SMTAT\"\n * @param amount the amount to spend (fractions supported)\n */\n async acUserPurchaseAirtimestock(\n airtimeCurrencyCode: string,\n amount: number | string,\n ): Promise<PurchaseAirtimeStockResponse> {\n const xml = this.requestXml(\n this.authXml() +\n el(\"Method\", \"acuserpurchaseairtimestock\") +\n el(\"AirtimeCurrencyCode\", airtimeCurrencyCode) +\n el(\"Amount\", amount) +\n // The PHP library sends externalReference inside a TransactionReference tag; kept for parity.\n opt(\"TransactionReference\", this.externalReference),\n );\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: PurchaseAirtimeStockResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n };\n setIfNonEmpty(result, \"StatusMessage\", str(response.StatusMessage));\n setIfNonEmpty(result, \"TransactionReference\", str(response.TransactionReference));\n setIfNonEmpty(result, \"TotalCurrencyDebited\", str(response.TotalCurrencyDebited));\n setIfNonEmpty(result, \"CommissionAmount\", str(response.CommissionAmount));\n setIfNonEmpty(result, \"ErrorMessageCode\", str(response.ErrorMessageCode));\n setIfNonEmpty(result, \"ErrorMessage\", str(response.ErrorMessage));\n\n return result;\n }\n\n /**\n * Obtain the name of a phone number before paying out funds.\n * Only available for MTN Uganda and Airtel Uganda networks; requires permission\n * from support@yo.co.ug.\n * @param msisdn the phone number in the format 2567XXXXXXXXXX\n */\n async acGetMsisdnKycInfo(msisdn: string): Promise<MsisdnKycInfoResponse> {\n const xml = this.requestXml(this.authXml() + el(\"Method\", \"acgetmsisdnkycinfo\") + el(\"Msisdn\", msisdn));\n\n const response = asRecord((await this.parseResponse(xml)).Response);\n\n const result: MsisdnKycInfoResponse = {\n Status: str(response.Status),\n StatusCode: str(response.StatusCode),\n };\n setIfNonEmpty(result, \"StatusMessage\", str(response.StatusMessage));\n\n const names = asRecord(asRecord(asRecord(response.AccountInformation).PersonalInformation).Names);\n setIfNonEmpty(result, \"FirstName\", str(names.FirstName));\n setIfNonEmpty(result, \"MiddleName\", str(names.MiddleName));\n setIfNonEmpty(result, \"Surname\", str(names.Surname));\n\n return result;\n }\n\n /**\n * Decode and verify a successful payment notification (IPN) POSTed to your\n * Instant Notification URL. Pass the parsed form body of the request.\n */\n receivePaymentNotification(body: PaymentNotificationBody): PaymentNotificationResult {\n return {\n is_verified: this.verifyPaymentNotification(body),\n date_time: body.date_time ?? \"\",\n amount: body.amount ?? \"\",\n narrative: body.narrative ?? \"\",\n network_ref: body.network_ref ?? \"\",\n external_ref: body.external_ref ?? \"\",\n msisdn: body.msisdn ?? \"\",\n };\n }\n\n /**\n * Decode and verify a failed payment notification POSTed to your\n * Failure Notification URL. Pass the parsed form body of the request.\n */\n receivePaymentFailureNotification(body: PaymentFailureNotificationBody): PaymentFailureNotificationResult {\n return {\n is_verified: this.verifyPaymentFailureNotification(body),\n failed_transaction_reference: body.failed_transaction_reference ?? \"\",\n transaction_init_date: body.transaction_init_date ?? \"\",\n };\n }\n\n /**\n * Calculate the Public Key Authentication Signature required by some payout requests.\n * Sets publicKeyAuthenticationSignatureBase64 on success.\n * @param msisdn the account the funds will be pushed to\n * @param amount the transaction amount\n * @param narrative the transaction narrative\n */\n generatePublicKeyAuthenticationSignature(msisdn: string, amount: number | string, narrative: string): void {\n // Loose check like PHP's `== NULL`: null, undefined and \"\" are all missing.\n if (!this.publicKeyAuthenticationNonce) {\n throw new Error(\"Public key authentication nonce is not set. Please set it to continue\");\n }\n\n if (!this.privateKeyFileLocation && this.privateKeyContent === null) {\n throw new Error(\"Private key file location cannot be NULL\");\n }\n\n let privateKeyPem: string | null = this.privateKeyContent;\n if (privateKeyPem === null) {\n try {\n privateKeyPem = readFileSync(this.privateKeyFileLocation as string, \"utf-8\");\n } catch {\n throw new Error(\n `Private key file could not be opened. Confirm your file location ${this.privateKeyFileLocation}`,\n );\n }\n }\n\n let privateKey;\n try {\n privateKey = createPrivateKey(privateKeyPem);\n } catch {\n throw new Error(\"Private key is invalid\");\n }\n\n const data =\n this.username +\n String(amount) +\n msisdn +\n narrative +\n (this.externalReference ?? \"\") +\n this.publicKeyAuthenticationNonce;\n\n // SHA1 is mandated by the Yo! Payments protocol (mirrors PHP's\n // openssl_sign(..., 'sha1WithRSAEncryption')); do not \"upgrade\" it.\n const sha1Hex = createHash(\"sha1\").update(data).digest(\"hex\");\n\n const signature = rsaSign(\"sha1\", Buffer.from(sha1Hex, \"utf-8\"), privateKey);\n\n this.publicKeyAuthenticationSignatureBase64 = signature.toString(\"base64\");\n }\n\n /** POST raw XML to the gateway and return the XML response body. */\n protected async getXmlResponse(xml: string): Promise<string> {\n return postXml(this.yoUrl, xml, {\n timeoutMs: this.timeoutMs,\n verifyTls: this.verifyTls,\n maxResponseBytes: this.maxResponseBytes,\n });\n }\n\n /** Verify the RSA-SHA256 signature on a payment notification against the Yo public certificate. */\n protected verifyPaymentNotification(body: PaymentNotificationBody): boolean {\n const data =\n (body.date_time ?? \"\") +\n (body.amount ?? \"\") +\n (body.narrative ?? \"\") +\n (body.network_ref ?? \"\") +\n (body.external_ref ?? \"\") +\n (body.msisdn ?? \"\");\n\n return this.verifySignature(data, body.signature);\n }\n\n /** Verify the RSA-SHA256 signature on a payment failure notification against the Yo public certificate. */\n protected verifyPaymentFailureNotification(body: PaymentFailureNotificationBody): boolean {\n const data = (body.failed_transaction_reference ?? \"\") + (body.transaction_init_date ?? \"\");\n\n return this.verifySignature(data, body.verification);\n }\n\n private verifySignature(data: string, signatureBase64: string | undefined): boolean {\n if (!signatureBase64) return false;\n\n const publicKey = loadPublicKeyCached(\n this.publicKeyFile,\n this.publicKeyFileIsDefault ? defaultVerificationCertificate(this.mode) : undefined,\n );\n if (publicKey === null) return false;\n\n try {\n return rsaVerify(\"sha256\", Buffer.from(data, \"utf-8\"), publicKey, Buffer.from(signatureBase64, \"base64\"));\n } catch {\n return false;\n }\n }\n\n private authXml(): string {\n return el(\"APIUsername\", this.username) + el(\"APIPassword\", this.password);\n }\n\n private requestXml(body: string): string {\n return `${XML_HEADER}<AutoCreate><Request>${body}</Request></AutoCreate>`;\n }\n\n /** POST the request XML to the gateway and return the parsed envelope that holds the <Response> node. */\n private async parseResponse(requestXml: string): Promise<XmlNode> {\n return parseGatewayResponse(await this.getXmlResponse(requestXml));\n }\n}\n\nexport default YoAPI;\n","import { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { YoMode } from \"./types.ts\";\nimport { YO_UGANDA_PRODUCTION_CERTIFICATE, YO_UGANDA_SANDBOX_CERTIFICATE } from \"./embeddedCerts.ts\";\n\nexport const SANDBOX_URL = \"https://sandbox.yo.co.ug/services/yopaymentsdev/task.php\";\nexport const PRODUCTION_URL = \"https://paymentsapi1.yo.co.ug/ybs/task.php\";\n\nexport const PUBLIC_KEY_FILE_FOR_SANDBOX = \"Yo_Uganda_Public_Sandbox_Certificate.crt\";\nexport const PUBLIC_KEY_FILE_FOR_PRODUCTION = \"Yo_Uganda_Public_Certificate.crt\";\n\n/** Default cap for gateway response bodies (real responses are a few KB). */\nexport const DEFAULT_MAX_RESPONSE_BYTES = 1024 * 1024;\n\nconst MODULE_DIR = resolveModuleDir();\nexport const CERTS_DIR = join(MODULE_DIR, \"..\", \"certs\");\n\n/**\n * Resolve the directory of this module. Bundled outputs (e.g. CJS builds,\n * Next.js server bundles) may not support import.meta.url — fall back to the\n * process working directory instead of crashing at import time.\n */\nfunction resolveModuleDir(): string {\n try {\n const metaUrl = import.meta?.url;\n if (typeof metaUrl === \"string\" && metaUrl.length > 0) {\n return dirname(fileURLToPath(metaUrl));\n }\n } catch {\n // ignore — use the fallback below\n }\n return process.cwd();\n}\n\n/**\n * Embedded verification certificate for the given mode. Used as a fallback\n * when the cert files in certs/ cannot be resolved at runtime.\n */\nexport function defaultVerificationCertificate(mode: YoMode): string {\n return mode === \"sandbox\" ? YO_UGANDA_SANDBOX_CERTIFICATE : YO_UGANDA_PRODUCTION_CERTIFICATE;\n}\n","// AUTO-GENERATED from certs/*.crt — do not edit by hand.\n// Regenerate with: bun run embed-certs\n\nexport const YO_UGANDA_SANDBOX_CERTIFICATE = \"-----BEGIN CERTIFICATE-----\\n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\\n-----END CERTIFICATE-----\\n\";\n\nexport const YO_UGANDA_PRODUCTION_CERTIFICATE = \"-----BEGIN CERTIFICATE-----\\nMIIEvTCCA6WgAwIBAgIJAN3e7VqDg5zQMA0GCSqGSIb3DQEBBQUAMIGaMQswCQYD\\nVQQGEwJVRzEQMA4GA1UECBMHS2FtcGFsYTEQMA4GA1UEBxMHS2FtcGFsYTEbMBkG\\nA1UECgwSWW8hIFVnYW5kYSBMaW1pdGVkMRUwEwYDVQQLDAxZbyEgUGF5bWVudHMx\\nFTATBgNVBAMTDHd3dy55by5jby51ZzEcMBoGCSqGSIb3DQEJARYNaW5mb0B5by5j\\nby51ZzAeFw0xMzA4MDkwNTQyMTRaFw0yMzA4MDcwNTQyMTRaMIGaMQswCQYDVQQG\\nEwJVRzEQMA4GA1UECBMHS2FtcGFsYTEQMA4GA1UEBxMHS2FtcGFsYTEbMBkGA1UE\\nCgwSWW8hIFVnYW5kYSBMaW1pdGVkMRUwEwYDVQQLDAxZbyEgUGF5bWVudHMxFTAT\\nBgNVBAMTDHd3dy55by5jby51ZzEcMBoGCSqGSIb3DQEJARYNaW5mb0B5by5jby51\\nZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPPo+N67Z56ebScXJ9tX\\ntFpSNBNNyDlqU/X8bqouZjWuvxpWOI4xZkPKXi0t205ooVbQL/+962NASjJRrouQ\\nIUhJq7xhwb+KKcWyFpA25742mNgaxeZJa9iofiHeKotBvHz6pswuqa2gXAyTTmYf\\nj6BOIFhDeUffOjfJYbzACy7WLtbK6VIRSTHypQY+zMQluw1euyY8524GYzf8E+c5\\n9qjIa5YY5PPianvvR25VDNRCm0Z6GPolhIGvYPUWHFZx+HtU8xoZumi5Kddvipew\\nuujxNVBRyQ8bVRoYxKKuDMFHiXA6V01oPzSOtfPK7JI+rd2JFU7dQgbFxTXI9+Qx\\n2yUCAwEAAaOCAQIwgf8wHQYDVR0OBBYEFPj0nwwE8lJByx243yV6cfXbTKbhMIHP\\nBgNVHSMEgccwgcSAFPj0nwwE8lJByx243yV6cfXbTKbhoYGgpIGdMIGaMQswCQYD\\nVQQGEwJVRzEQMA4GA1UECBMHS2FtcGFsYTEQMA4GA1UEBxMHS2FtcGFsYTEbMBkG\\nA1UECgwSWW8hIFVnYW5kYSBMaW1pdGVkMRUwEwYDVQQLDAxZbyEgUGF5bWVudHMx\\nFTATBgNVBAMTDHd3dy55by5jby51ZzEcMBoGCSqGSIb3DQEJARYNaW5mb0B5by5j\\nby51Z4IJAN3e7VqDg5zQMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB\\nAGCaUMHBxGVtVsA8xMDWknjH6hV9yuca3s0qRrOoMfM7nyOjeYtUNgZlsLxuX2n3\\nFhoeK9DUBvIKVSlVfO5SXgsXyWKG54YFEkZ8D50Krsyl5NCfaAJezkQ0MNdtpG98\\nwlD/cYa6C6DC/s1eilUbI5QqaxLo+EFy5VuHQ8tAuxJbNTVPMW9GvTjxofeMUnug\\nSxUMDqHmEkzbQV7yCBVqf3yi4XOM4/6B7Tr6gaandpuR+v2XaKl4SOf8G5svn96g\\nKn+Bk8p6rlBWAl+5hWxHWi4dkjiLsk8q+aeKh6ibwYtRjEt/sbWTgJAZjI1mTT8d\\nwsLYlL7k1O3wCjUeMQzi274=\\n-----END CERTIFICATE-----\\n\";\n","import { YoAPIError } from \"./errors.ts\";\n\nexport interface PostXmlOptions {\n /** Request timeout in milliseconds; <= 0 disables the timeout (like PHP curl). */\n timeoutMs: number;\n /** Whether to verify the gateway TLS certificate. */\n verifyTls: boolean;\n /** Maximum accepted response body in bytes. */\n maxResponseBytes: number;\n}\n\n/** POST raw XML to the gateway and return the XML response body. */\nexport async function postXml(url: string, xml: string, options: PostXmlOptions): Promise<string> {\n const init: RequestInit & { tls?: { rejectUnauthorized: boolean } } = {\n method: \"POST\",\n body: xml,\n headers: {\n \"Content-Type\": \"text/xml\",\n \"Content-transfer-encoding\": \"text\",\n \"Content-Length\": String(Buffer.byteLength(xml)),\n },\n };\n\n // A timeout <= 0 means \"no timeout\", mirroring PHP's curl timeout semantics.\n if (options.timeoutMs > 0) {\n init.signal = AbortSignal.timeout(options.timeoutMs);\n }\n\n // TLS is verified by default (unlike the PHP library). The `tls` key is a\n // Bun fetch extension; on Node.js, disabling verification additionally\n // requires NODE_TLS_REJECT_UNAUTHORIZED=0 in the environment.\n if (!options.verifyTls) {\n init.tls = { rejectUnauthorized: false };\n }\n\n let res: Response;\n try {\n res = await fetch(url, init);\n } catch (error) {\n throw new YoAPIError(\n `Request to the Yo! Payments gateway failed: ${(error as Error)?.message ?? error}`,\n { cause: error },\n );\n }\n\n const text = await readBoundedText(res, options.maxResponseBytes);\n\n if (!res.ok) {\n throw new YoAPIError(`Yo! Payments gateway responded with HTTP ${res.status}`, {\n status: res.status,\n body: text.slice(0, 500),\n });\n }\n\n return text;\n}\n\n/** Read the response body, enforcing a byte limit to bound memory use. */\nasync function readBoundedText(res: Response, limit: number): Promise<string> {\n const declared = res.headers.get(\"content-length\");\n if (declared !== null && Number(declared) > limit) {\n throw new YoAPIError(\n `Yo! Payments gateway response (${declared} bytes) exceeds the limit of ${limit} bytes`,\n );\n }\n\n if (res.body === null) {\n return \"\";\n }\n\n const reader = res.body.getReader();\n const chunks: Uint8Array[] = [];\n let size = 0;\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n size += value.byteLength;\n if (size > limit) {\n await reader.cancel().catch(() => undefined);\n throw new YoAPIError(\n `Yo! Payments gateway response exceeds the limit of ${limit} bytes`,\n );\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n return Buffer.concat(chunks).toString(\"utf-8\");\n}\n","import { createPublicKey } from \"node:crypto\";\nimport type { KeyObject } from \"node:crypto\";\nimport { readFileSync, statSync } from \"node:fs\";\n\ninterface CachedPublicKey {\n size: number;\n mtimeMs: number;\n key: KeyObject;\n}\n\n/** Verification-key cache so IPN endpoints don't re-read the certificate per request. */\nconst publicKeyCache = new Map<string, CachedPublicKey>();\n\n/** Load (and cache) a PEM public key/certificate; null when unreadable or invalid. */\nexport function loadPublicKeyCached(filePath: string, fallbackPem?: string): KeyObject | null {\n const fromFile = loadFromFile(filePath);\n if (fromFile !== null) return fromFile;\n\n if (fallbackPem === undefined) return null;\n\n // Fallback results are deliberately not cached: a later-appearing file\n // must always win over the embedded certificate.\n try {\n return createPublicKey(fallbackPem);\n } catch {\n return null;\n }\n}\n\nfunction loadFromFile(filePath: string): KeyObject | null {\n let size: number;\n let mtimeMs: number;\n try {\n const stat = statSync(filePath);\n size = stat.size;\n mtimeMs = stat.mtimeMs;\n } catch {\n return null;\n }\n\n const cached = publicKeyCache.get(filePath);\n if (cached !== undefined && cached.size === size && cached.mtimeMs === mtimeMs) {\n return cached.key;\n }\n\n try {\n const key = createPublicKey(readFileSync(filePath, \"utf-8\"));\n publicKeyCache.set(filePath, { size, mtimeMs, key });\n return key;\n } catch {\n return null;\n }\n}\n","import { XMLParser, XMLValidator } from \"fast-xml-parser\";\nimport { YoAPIError } from \"./errors.ts\";\n\nexport type XmlNode = Record<string, any>;\n\nexport const XML_HEADER = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>';\n\n/** Shared response parser (XMLParser instances are stateless across parse calls). */\nconst responseParser = new XMLParser({\n ignoreAttributes: true,\n parseTagValue: false,\n ignoreDeclaration: true,\n ignorePiTags: true,\n});\n\nexport function el(tag: string, value: string | number): string {\n return `<${tag}>${value}</${tag}>`;\n}\n\n/** Mirrors PHP's `if ($value != NULL)` check: null, undefined and \"\" are all skipped. */\nexport function opt(tag: string, value: string | number | null | undefined): string {\n if (value === null || value === undefined || value === \"\") return \"\";\n return el(tag, value);\n}\n\n/** Mirrors PHP's `(string)` cast on a SimpleXMLElement child. */\nexport function str(value: unknown): string {\n if (value === undefined || value === null) return \"\";\n if (typeof value === \"object\") return \"\";\n return String(value);\n}\n\n/** Mirrors PHP's `!empty($response->X)`: include only when the string value is non-empty (PHP also treats \"0\" as empty). */\nexport function setIfNonEmpty<T extends object, K extends keyof T>(target: T, key: K, value: string): void {\n if (value !== \"\" && value !== \"0\") (target as Record<string, unknown>)[key as string] = value;\n}\n\n/** Mirrors PHP's `if ($response->X != null)`: include when the element exists with a non-empty value (\"0\" included). */\nexport function setIfNotNull<T extends object, K extends keyof T>(target: T, key: K, value: unknown): void {\n if (value === undefined || value === null) return;\n if (typeof value === \"object\") return;\n if (String(value) !== \"\") (target as Record<string, unknown>)[key as string] = String(value);\n}\n\nexport function asArray<T>(value: T | T[] | undefined | null): T[] {\n if (value === undefined || value === null) return [];\n return Array.isArray(value) ? value : [value];\n}\n\nexport function asRecord(value: unknown): XmlNode {\n if (value === undefined || value === null || typeof value !== \"object\") return {};\n return value as XmlNode;\n}\n\n/**\n * Validate gateway response XML and return the parsed envelope node that holds\n * the <Response> child. Throws YoAPIError on malformed XML or a missing node.\n */\nexport function parseGatewayResponse(responseXml: string): XmlNode {\n const validation = XMLValidator.validate(responseXml);\n if (validation !== true) {\n throw new YoAPIError(`Invalid XML response from the Yo! Payments gateway: ${validation.err.msg}`, {\n body: responseXml.slice(0, 500),\n });\n }\n\n const doc = asRecord(responseParser.parse(responseXml));\n\n if (doc.Response !== undefined) return doc;\n\n for (const value of Object.values(doc)) {\n const node = asRecord(value);\n if (node.Response !== undefined) return node;\n }\n\n throw new YoAPIError(\"Yo! Payments gateway response did not contain a <Response> node\", {\n body: responseXml.slice(0, 500),\n });\n}\n"],"mappings":"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","names":["readFileSync","join","join","readFileSync"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@herberthtk/yo-payments-api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript client for the Yo! Payments mobile-money gateway (deposits, withdrawals, airtime, statements, IPN verification). Works in Node.js 18+, Bun, and Next.js (server-side).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/herberthk/yo-payments-api.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/herberthk/yo-payments-api#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/herberthk/yo-payments-api/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"yo-payments",
|
|
16
|
+
"mobile-money",
|
|
17
|
+
"mtn",
|
|
18
|
+
"airtel",
|
|
19
|
+
"uganda",
|
|
20
|
+
"payments",
|
|
21
|
+
"ipn",
|
|
22
|
+
"typescript"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.cjs",
|
|
26
|
+
"module": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"require": {
|
|
35
|
+
"types": "./dist/index.d.cts",
|
|
36
|
+
"default": "./dist/index.cjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"./certs/*": "./certs/*",
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist/",
|
|
44
|
+
"certs/",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE",
|
|
47
|
+
"CHANGELOG.md"
|
|
48
|
+
],
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"sideEffects": false,
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public",
|
|
55
|
+
"provenance": true
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"fast-xml-parser": "^5.11.1"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
62
|
+
"@release-it/conventional-changelog": "^12.0.0",
|
|
63
|
+
"@types/bun": "latest",
|
|
64
|
+
"@types/node": "^22",
|
|
65
|
+
"conventional-changelog-conventionalcommits": "^10.4.0",
|
|
66
|
+
"release-it": "^21.0.2",
|
|
67
|
+
"tsup": "^8.5.1",
|
|
68
|
+
"typescript": "^5.9.2"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "tsup",
|
|
72
|
+
"prebuild": "bun run embed-certs",
|
|
73
|
+
"prepublishOnly": "bun run build",
|
|
74
|
+
"test": "bun test",
|
|
75
|
+
"typecheck": "tsc --noEmit",
|
|
76
|
+
"check:pack": "attw --pack",
|
|
77
|
+
"release": "release-it",
|
|
78
|
+
"embed-certs": "bun scripts/embed-certs.ts"
|
|
79
|
+
}
|
|
80
|
+
}
|