libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 3e473d71a91f0795f585e81f83add32af3aec114
parent 16e0190f5337ce4c0c5c61df204e811e84960d75
Author: Antoine A <>
Date:   Tue, 30 Jan 2024 20:59:26 +0100

Improve EBICS pain.002 status extraction

Diffstat:
Dcommon/import.py | 66------------------------------------------------------------------
Aebics/codegen.py | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Debics/import.py | 66------------------------------------------------------------------
Debics/src/main/kotlin/EbicsCodeSets.kt | 309-------------------------------------------------------------------------------
Aebics/src/main/kotlin/Iso20022CodeSets.kt | 330+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aebics/src/main/kotlin/Iso20022Constants.kt | 36++++++++++++++++++++++++++++++++++++
Mnexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt | 24++++++++++++++++++++++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt | 77+++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
8 files changed, 510 insertions(+), 469 deletions(-)

diff --git a/common/import.py b/common/import.py @@ -1,66 +0,0 @@ -# Update EBICS constants file using latest external code sets files - -import requests -from zipfile import ZipFile -from io import BytesIO -import polars as pl - -# Get XLSX zip file from server -r = requests.get( - "https://www.iso20022.org/sites/default/files/media/file/ExternalCodeSets_XLSX.zip" -) -assert r.status_code == 200 - -# Unzip the XLSX file -zip = ZipFile(BytesIO(r.content)) -files = zip.namelist() -assert len(files) == 1 -file = zip.open(files[0]) - -# Parse excel -df = pl.read_excel(file, sheet_name="AllCodeSets") - -def extractCodeSet(setName: str, className: str) -> str: - out = f"enum class {className}(val isoCode: String, val description: String) {{" - - for row in df.filter(pl.col("Code Set") == setName).sort("Code Value").rows(named=True): - (value, isoCode, description) = ( - row["Code Value"], - row["Code Name"], - row["Code Definition"].split("\n", 1)[0].strip(), - ) - out += f'\n\t{value}("{isoCode}", "{description}"),' - - out += "\n}" - return out - -# Write kotlin file -kt = f"""/* - * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. - - * LibEuFin is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation; either version 3, or - * (at your option) any later version. - - * LibEuFin is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General - * Public License for more details. - - * You should have received a copy of the GNU Affero General Public - * License along with LibEuFin; see the file COPYING. If not, see - * <http://www.gnu.org/licenses/> - */ - -// THIS FILE IS GENERATED, DO NOT EDIT - -package tech.libeufin.common - -{extractCodeSet("ExternalStatusReason1Code", "ExternalStatusReasonCode")} - -{extractCodeSet("ExternalPaymentGroupStatus1Code", "ExternalPaymentGroupStatusCode")} -""" -with open("src/main/kotlin/EbicsCodeSets.kt", "w") as file1: - file1.write(kt) diff --git a/ebics/codegen.py b/ebics/codegen.py @@ -0,0 +1,71 @@ +# Update EBICS constants file using latest external code sets files + +import requests +from zipfile import ZipFile +from io import BytesIO +import polars as pl + +def iso20022codegen(): + # Get XLSX zip file from server + r = requests.get( + "https://www.iso20022.org/sites/default/files/media/file/ExternalCodeSets_XLSX.zip" + ) + assert r.status_code == 200 + + # Unzip the XLSX file + zip = ZipFile(BytesIO(r.content)) + files = zip.namelist() + assert len(files) == 1 + file = zip.open(files[0]) + + # Parse excel + df = pl.read_excel(file, sheet_name="AllCodeSets") + + def extractCodeSet(setName: str, className: str) -> str: + out = f"enum class {className}(val isoCode: String, val description: String) {{" + + for row in df.filter(pl.col("Code Set") == setName).sort("Code Value").rows(named=True): + (value, isoCode, description) = ( + row["Code Value"], + row["Code Name"], + row["Code Definition"].split("\n", 1)[0].strip(), + ) + out += f'\n\t{value}("{isoCode}", "{description}"),' + + out += "\n}" + return out + + # Write kotlin file + kt = f"""/* + * This file is part of LibEuFin. + * Copyright (C) 2024 Taler Systems S.A. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +// THIS FILE IS GENERATED, DO NOT EDIT + +package tech.libeufin.ebics + +{extractCodeSet("ExternalStatusReason1Code", "ExternalStatusReasonCode")} + +{extractCodeSet("ExternalPaymentGroupStatus1Code", "ExternalPaymentGroupStatusCode")} + +{extractCodeSet("ExternalPaymentTransactionStatus1Code", "ExternalPaymentTransactionStatusCode")} +""" + with open("src/main/kotlin/Iso20022CodeSets.kt", "w") as file1: + file1.write(kt) + +iso20022codegen() diff --git a/ebics/import.py b/ebics/import.py @@ -1,66 +0,0 @@ -# Update EBICS constants file using latest external code sets files - -import requests -from zipfile import ZipFile -from io import BytesIO -import polars as pl - -# Get XLSX zip file from server -r = requests.get( - "https://www.iso20022.org/sites/default/files/media/file/ExternalCodeSets_XLSX.zip" -) -assert r.status_code == 200 - -# Unzip the XLSX file -zip = ZipFile(BytesIO(r.content)) -files = zip.namelist() -assert len(files) == 1 -file = zip.open(files[0]) - -# Parse excel -df = pl.read_excel(file, sheet_name="AllCodeSets") - -def extractCodeSet(setName: str, className: str) -> str: - out = f"enum class {className}(val isoCode: String, val description: String) {{" - - for row in df.filter(pl.col("Code Set") == setName).sort("Code Value").rows(named=True): - (value, isoCode, description) = ( - row["Code Value"], - row["Code Name"], - row["Code Definition"].split("\n", 1)[0].strip(), - ) - out += f'\n\t{value}("{isoCode}", "{description}"),' - - out += "\n}" - return out - -# Write kotlin file -kt = f"""/* - * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. - - * LibEuFin is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation; either version 3, or - * (at your option) any later version. - - * LibEuFin is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General - * Public License for more details. - - * You should have received a copy of the GNU Affero General Public - * License along with LibEuFin; see the file COPYING. If not, see - * <http://www.gnu.org/licenses/> - */ - -// THIS FILE IS GENERATED, DO NOT EDIT - -package tech.libeufin.ebics - -{extractCodeSet("ExternalStatusReason1Code", "ExternalStatusReasonCode")} - -{extractCodeSet("ExternalPaymentGroupStatus1Code", "ExternalPaymentGroupStatusCode")} -""" -with open("src/main/kotlin/EbicsCodeSets.kt", "w") as file1: - file1.write(kt) diff --git a/ebics/src/main/kotlin/EbicsCodeSets.kt b/ebics/src/main/kotlin/EbicsCodeSets.kt @@ -1,309 +0,0 @@ -/* - * This file is part of LibEuFin. - * Copyright (C) 2024 Taler Systems S.A. - - * LibEuFin is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation; either version 3, or - * (at your option) any later version. - - * LibEuFin is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General - * Public License for more details. - - * You should have received a copy of the GNU Affero General Public - * License along with LibEuFin; see the file COPYING. If not, see - * <http://www.gnu.org/licenses/> - */ - -// THIS FILE IS GENERATED, DO NOT EDIT - -package tech.libeufin.ebics - -enum class ExternalStatusReasonCode(val isoCode: String, val description: String) { - AB01("AbortedClearingTimeout", "Clearing process aborted due to timeout."), - AB02("AbortedClearingFatalError", "Clearing process aborted due to a fatal error."), - AB03("AbortedSettlementTimeout", "Settlement aborted due to timeout."), - AB04("AbortedSettlementFatalError", "Settlement process aborted due to a fatal error."), - AB05("TimeoutCreditorAgent", "Transaction stopped due to timeout at the Creditor Agent."), - AB06("TimeoutInstructedAgent", "Transaction stopped due to timeout at the Instructed Agent."), - AB07("OfflineAgent", "Agent of message is not online."), - AB08("OfflineCreditorAgent", "Creditor Agent is not online."), - AB09("ErrorCreditorAgent", "Transaction stopped due to error at the Creditor Agent."), - AB10("ErrorInstructedAgent", "Transaction stopped due to error at the Instructed Agent."), - AB11("TimeoutDebtorAgent", "Transaction stopped due to timeout at the Debtor Agent."), - AC01("IncorrectAccountNumber", "Account number is invalid or missing."), - AC02("InvalidDebtorAccountNumber", "Debtor account number invalid or missing"), - AC03("InvalidCreditorAccountNumber", "Creditor account number invalid or missing"), - AC04("ClosedAccountNumber", "Account number specified has been closed on the bank of account's books."), - AC05("ClosedDebtorAccountNumber", "Debtor account number closed"), - AC06("BlockedAccount", "Account specified is blocked, prohibiting posting of transactions against it."), - AC07("ClosedCreditorAccountNumber", "Creditor account number closed"), - AC08("InvalidBranchCode", "Branch code is invalid or missing"), - AC09("InvalidAccountCurrency", "Account currency is invalid or missing"), - AC10("InvalidDebtorAccountCurrency", "Debtor account currency is invalid or missing"), - AC11("InvalidCreditorAccountCurrency", "Creditor account currency is invalid or missing"), - AC12("InvalidAccountType", "Account type missing or invalid."), - AC13("InvalidDebtorAccountType", "Debtor account type missing or invalid"), - AC14("InvalidCreditorAccountType", "Creditor account type missing or invalid"), - AC15("AccountDetailsChanged", "The account details for the counterparty have changed."), - AC16("CardNumberInvalid", "Credit or debit card number is invalid."), - AEXR("AlreadyExpiredRTP", "Request-to-pay Expiry Date and Time has already passed."), - AG01("TransactionForbidden", "Transaction forbidden on this type of account (formerly NoAgreement)"), - AG02("InvalidBankOperationCode", "Bank Operation code specified in the message is not valid for receiver"), - AG03("TransactionNotSupported", "Transaction type not supported/authorized on this account"), - AG04("InvalidAgentCountry", "Agent country code is missing or invalid."), - AG05("InvalidDebtorAgentCountry", "Debtor agent country code is missing or invalid"), - AG06("InvalidCreditorAgentCountry", "Creditor agent country code is missing or invalid"), - AG07("UnsuccesfulDirectDebit", "Debtor account cannot be debited for a generic reason."), - AG08("InvalidAccessRights", "Transaction failed due to invalid or missing user or access right"), - AG09("PaymentNotReceived", "Original payment never received."), - AG10("AgentSuspended", "Agent of message is suspended from the Real Time Payment system."), - AG11("CreditorAgentSuspended", "Creditor Agent of message is suspended from the Real Time Payment system."), - AG12("NotAllowedBookTransfer", "Payment orders made by transferring funds from one account to another at the same financial institution (bank or payment institution) are not allowed."), - AG13("ForbiddenReturnPayment", "Returned payments derived from previously returned transactions are not allowed."), - AGNT("IncorrectAgent", "Agent in the payment workflow is incorrect"), - ALAC("AlreadyAcceptedRTP", "Request-to-pay has already been accepted by the Debtor."), - AM01("ZeroAmount", "Specified message amount is equal to zero"), - AM02("NotAllowedAmount", "Specific transaction/message amount is greater than allowed maximum"), - AM03("NotAllowedCurrency", "Specified message amount is an non processable currency outside of existing agreement"), - AM04("InsufficientFunds", "Amount of funds available to cover specified message amount is insufficient."), - AM05("Duplication", "Duplication"), - AM06("TooLowAmount", "Specified transaction amount is less than agreed minimum."), - AM07("BlockedAmount", "Amount specified in message has been blocked by regulatory authorities."), - AM09("WrongAmount", "Amount received is not the amount agreed or expected"), - AM10("InvalidControlSum", "Sum of instructed amounts does not equal the control sum."), - AM11("InvalidTransactionCurrency", "Transaction currency is invalid or missing"), - AM12("InvalidAmount", "Amount is invalid or missing"), - AM13("AmountExceedsClearingSystemLimit", "Transaction amount exceeds limits set by clearing system"), - AM14("AmountExceedsAgreedLimit", "Transaction amount exceeds limits agreed between bank and client"), - AM15("AmountBelowClearingSystemMinimum", "Transaction amount below minimum set by clearing system"), - AM16("InvalidGroupControlSum", "Control Sum at the Group level is invalid"), - AM17("InvalidPaymentInfoControlSum", "Control Sum at the Payment Information level is invalid"), - AM18("InvalidNumberOfTransactions", "Number of transactions is invalid or missing."), - AM19("InvalidGroupNumberOfTransactions", "Number of transactions at the Group level is invalid or missing"), - AM20("InvalidPaymentInfoNumberOfTransactions", "Number of transactions at the Payment Information level is invalid"), - AM21("LimitExceeded", "Transaction amount exceeds limits agreed between bank and client."), - AM22("ZeroAmountNotApplied", "Unable to apply zero amount to designated account. For example, where the rules of a service allow the use of zero amount payments, however the back-office system is unable to apply the funds to the account. If the rules of a service prohibit the use of zero amount payments, then code AM01 is used to report the error condition."), - AM23("AmountExceedsSettlementLimit", "Transaction amount exceeds settlement limit."), - APAR("AlreadyPaidRTP", "Request To Pay has already been paid by the Debtor."), - ARFR("AlreadyRefusedRTP", "Request-to-pay has already been refused by the Debtor."), - ARJR("AlreadyRejectedRTP", "Request-to-pay has already been rejected."), - ATNS("AttachementsNotSupported", "Attachments to the request-to-pay are not supported."), - BE01("InconsistenWithEndCustomer", "Identification of end customer is not consistent with associated account number. (formerly CreditorConsistency)."), - BE04("MissingCreditorAddress", "Specification of creditor's address, which is required for payment, is missing/not correct (formerly IncorrectCreditorAddress)."), - BE05("UnrecognisedInitiatingParty", "Party who initiated the message is not recognised by the end customer"), - BE06("UnknownEndCustomer", "End customer specified is not known at associated Sort/National Bank Code or does no longer exist in the books"), - BE07("MissingDebtorAddress", "Specification of debtor's address, which is required for payment, is missing/not correct."), - BE08("MissingDebtorName", "Debtor name is missing"), - BE09("InvalidCountry", "Country code is missing or Invalid."), - BE10("InvalidDebtorCountry", "Debtor country code is missing or invalid"), - BE11("InvalidCreditorCountry", "Creditor country code is missing or invalid"), - BE12("InvalidCountryOfResidence", "Country code of residence is missing or Invalid."), - BE13("InvalidDebtorCountryOfResidence", "Country code of debtor's residence is missing or Invalid"), - BE14("InvalidCreditorCountryOfResidence", "Country code of creditor's residence is missing or Invalid"), - BE15("InvalidIdentificationCode", "Identification code missing or invalid."), - BE16("InvalidDebtorIdentificationCode", "Debtor or Ultimate Debtor identification code missing or invalid"), - BE17("InvalidCreditorIdentificationCode", "Creditor or Ultimate Creditor identification code missing or invalid"), - BE18("InvalidContactDetails", "Contact details missing or invalid"), - BE19("InvalidChargeBearerCode", "Charge bearer code for transaction type is invalid"), - BE20("InvalidNameLength", "Name length exceeds local rules for payment type."), - BE21("MissingName", "Name missing or invalid. Generic usage if cannot specifically identify debtor or creditor."), - BE22("MissingCreditorName", "Creditor name is missing"), - BE23("AccountProxyInvalid", "Phone number or email address, or any other proxy, used as the account proxy is unknown or invalid."), - CERI("CheckERI", "Credit transfer is not tagged as an Extended Remittance Information (ERI) transaction but contains ERI."), - CH03("RequestedExecutionDateOrRequestedCollectionDateTooFarInFuture", "Value in Requested Execution Date or Requested Collection Date is too far in the future"), - CH04("RequestedExecutionDateOrRequestedCollectionDateTooFarInPast", "Value in Requested Execution Date or Requested Collection Date is too far in the past"), - CH07("ElementIsNotToBeUsedAtB-andC-Level", "Element is not to be used at B- and C-Level"), - CH09("MandateChangesNotAllowed", "Mandate changes are not allowed"), - CH10("InformationOnMandateChangesMissing", "Information on mandate changes are missing"), - CH11("CreditorIdentifierIncorrect", "Value in Creditor Identifier is incorrect"), - CH12("CreditorIdentifierNotUnambiguouslyAtTransaction-Level", "Creditor Identifier is ambiguous at Transaction Level"), - CH13("OriginalDebtorAccountIsNotToBeUsed", "Original Debtor Account is not to be used"), - CH14("OriginalDebtorAgentIsNotToBeUsed", "Original Debtor Agent is not to be used"), - CH15("ElementContentIncludesMoreThan140Characters", "Content Remittance Information/Structured includes more than 140 characters"), - CH16("ElementContentFormallyIncorrect", "Content is incorrect"), - CH17("ElementNotAdmitted", "Element is not allowed"), - CH19("ValuesWillBeSetToNextTARGETday", "Values in Interbank Settlement Date or Requested Collection Date will be set to the next TARGET day"), - CH20("DecimalPointsNotCompatibleWithCurrency", "Number of decimal points not compatible with the currency"), - CH21("RequiredCompulsoryElementMissing", "Mandatory element is missing"), - CH22("COREandB2BwithinOnemessage", "SDD CORE and B2B not permitted within one message"), - CHQC("ChequeSettledOnCreditorAccount", "Cheque has been presented in cheque clearing and settled on the creditor’s account."), - CN01("AuthorisationCancelled", "Authorisation is cancelled."), - CNOR("CreditorBankIsNotRegistered", "Creditor bank is not registered under this BIC in the CSM"), - CURR("IncorrectCurrency", "Currency of the payment is incorrect"), - CUST("RequestedByCustomer", "Cancellation requested by the Debtor"), - DC02("SettlementNotReceived", "Rejection of a payment due to covering FI settlement not being received."), - DNOR("DebtorBankIsNotRegistered", "Debtor bank is not registered under this BIC in the CSM"), - DS01("ElectronicSignaturesCorrect", "The electronic signature(s) is/are correct"), - DS02("OrderCancelled", "An authorized user has cancelled the order"), - DS03("OrderNotCancelled", "The user’s attempt to cancel the order was not successful"), - DS04("OrderRejected", "The order was rejected by the bank side (for reasons concerning content)"), - DS05("OrderForwardedForPostprocessing", "The order was correct and could be forwarded for postprocessing"), - DS06("TransferOrder", "The order was transferred to VEU"), - DS07("ProcessingOK", "All actions concerning the order could be done by the EBICS bank server"), - DS08("DecompressionError", "The decompression of the file was not successful"), - DS09("DecryptionError", "The decryption of the file was not successful"), - DS0A("DataSignRequested", "Data signature is required."), - DS0B("UnknownDataSignFormat", "Data signature for the format is not available or invalid."), - DS0C("SignerCertificateRevoked", "The signer certificate is revoked."), - DS0D("SignerCertificateNotValid", "The signer certificate is not valid (revoked or not active)."), - DS0E("IncorrectSignerCertificate", "The signer certificate is not present."), - DS0F("SignerCertificationAuthoritySignerNotValid", "The authority of the signer certification sending the certificate is unknown."), - DS0G("NotAllowedPayment", "Signer is not allowed to sign this operation type."), - DS0H("NotAllowedAccount", "Signer is not allowed to sign for this account."), - DS0K("NotAllowedNumberOfTransaction", "The number of transaction is over the number allowed for this signer."), - DS10("Signer1CertificateRevoked", "The certificate is revoked for the first signer."), - DS11("Signer1CertificateNotValid", "The certificate is not valid (revoked or not active) for the first signer."), - DS12("IncorrectSigner1Certificate", "The certificate is not present for the first signer."), - DS13("SignerCertificationAuthoritySigner1NotValid", "The authority of signer certification sending the certificate is unknown for the first signer."), - DS14("UserDoesNotExist", "The user is unknown on the server"), - DS15("IdenticalSignatureFound", "The same signature has already been sent to the bank"), - DS16("PublicKeyVersionIncorrect", "The public key version is not correct. This code is returned when a customer sends signature files to the financial institution after conversion from an older program version (old ES format) to a new program version (new ES format) without having carried out re-initialisation with regard to a public key change."), - DS17("DifferentOrderDataInSignatures", "Order data and signatures don’t match"), - DS18("RepeatOrder", "File cannot be tested, the complete order has to be repeated. This code is returned in the event of a malfunction during the signature check, e.g. not enough storage space."), - DS19("ElectronicSignatureRightsInsufficient", "The user’s rights (concerning his signature) are insufficient to execute the order"), - DS20("Signer2CertificateRevoked", "The certificate is revoked for the second signer."), - DS21("Signer2CertificateNotValid", "The certificate is not valid (revoked or not active) for the second signer."), - DS22("IncorrectSigner2Certificate", "The certificate is not present for the second signer."), - DS23("SignerCertificationAuthoritySigner2NotValid", "The authority of signer certification sending the certificate is unknown for the second signer."), - DS24("WaitingTimeExpired", "Waiting time expired due to incomplete order"), - DS25("OrderFileDeleted", "The order file was deleted by the bank server"), - DS26("UserSignedMultipleTimes", "The same user has signed multiple times"), - DS27("UserNotYetActivated", "The user is not yet activated (technically)"), - DT01("InvalidDate", "Invalid date (eg, wrong or missing settlement date)"), - DT02("InvalidCreationDate", "Invalid creation date and time in Group Header (eg, historic date)"), - DT03("InvalidNonProcessingDate", "Invalid non bank processing date (eg, weekend or local public holiday)"), - DT04("FutureDateNotSupported", "Future date not supported"), - DT05("InvalidCutOffDate", "Associated message, payment information block or transaction was received after agreed processing cut-off date, i.e., date in the past."), - DT06("ExecutionDateChanged", "Execution Date has been modified in order for transaction to be processed"), - DU01("DuplicateMessageID", "Message Identification is not unique."), - DU02("DuplicatePaymentInformationID", "Payment Information Block is not unique."), - DU03("DuplicateTransaction", "Transaction is not unique."), - DU04("DuplicateEndToEndID", "End To End ID is not unique."), - DU05("DuplicateInstructionID", "Instruction ID is not unique."), - DUPL("DuplicatePayment", "Payment is a duplicate of another payment"), - ED01("CorrespondentBankNotPossible", "Correspondent bank not possible."), - ED03("BalanceInfoRequest", "Balance of payments complementary info is requested"), - ED05("SettlementFailed", "Settlement of the transaction has failed."), - ED06("SettlementSystemNotAvailable", "Interbank settlement system not available."), - EDTL("ExpiryDateTooLong", "Expiry date time of the request-to-pay is too far in the future."), - EDTR("ExpiryDateTimeReached", "Expiry date time of the request-to-pay is already reached."), - ERIN("ERIOptionNotSupported", "Extended Remittance Information (ERI) option is not supported."), - FF01("InvalidFileFormat", "File Format incomplete or invalid"), - FF02("SyntaxError", "Syntax error reason is provided as narrative information in the additional reason information."), - FF03("InvalidPaymentTypeInformation", "Payment Type Information is missing or invalid."), - FF04("InvalidServiceLevelCode", "Service Level code is missing or invalid"), - FF05("InvalidLocalInstrumentCode", "Local Instrument code is missing or invalid"), - FF06("InvalidCategoryPurposeCode", "Category Purpose code is missing or invalid"), - FF07("InvalidPurpose", "Purpose is missing or invalid"), - FF08("InvalidEndToEndId", "End to End Id missing or invalid"), - FF09("InvalidChequeNumber", "Cheque number missing or invalid"), - FF10("BankSystemProcessingError", "File or transaction cannot be processed due to technical issues at the bank side"), - FF11("ClearingRequestAborted", "Clearing request rejected due it being subject to an abort operation."), - FF12("OriginalTransactionNotEligibleForRequestedReturn", "Original payment is not eligible to be returned given its current status."), - FF13("RequestForCancellationNotFound", "No record of request for cancellation found."), - FOCR("FollowingCancellationRequest", "Return following a cancellation request."), - FR01("Fraud", "Returned as a result of fraud."), - FRAD("FraudulentOrigin", "Cancellation requested following a transaction that was originated fraudulently. The use of the FraudulentOrigin code should be governed by jurisdictions."), - G000("PaymentTransferredAndTracked", "In an FI To FI Customer Credit Transfer: The Status Originator transferred the payment to the next Agent or to a Market Infrastructure. The payment transfer is tracked. No further updates will follow from the Status Originator."), - G001("PaymentTransferredAndNotTracked", "In an FI To FI Customer Credit Transfer: The Status Originator transferred the payment to the next Agent or to a Market Infrastructure. The payment transfer is not tracked. No further updates will follow from the Status Originator."), - G002("CreditDebitNotConfirmed", "In a FIToFI Customer Credit Transfer: Credit to the creditor’s account may not be confirmed same day. Update will follow from the Status Originator."), - G003("CreditPendingDocuments", "In a FIToFI Customer Credit Transfer: Credit to creditor’s account is pending receipt of required documents. The Status Originator has requested creditor to provide additional documentation. Update will follow from the Status Originator."), - G004("CreditPendingFunds", "In a FIToFI Customer Credit Transfer: Credit to the creditor’s account is pending, status Originator is waiting for funds provided via a cover. Update will follow from the Status Originator."), - G005("DeliveredWithServiceLevel", "Payment has been delivered to creditor agent with service level."), - G006("DeliveredWIthoutServiceLevel", "Payment has been delivered to creditor agent without service level."), - ID01("CorrespondingOriginalFileStillNotSent", "Signature file was sent to the bank but the corresponding original file has not been sent yet."), - IEDT("IncorrectExpiryDateTime", "Expiry date time of the request-to-pay is incorrect."), - IRNR("InitialRTPNeverReceived", "No initial request-to-pay has been received."), - MD01("NoMandate", "No Mandate"), - MD02("MissingMandatoryInformationInMandate", "Mandate related information data required by the scheme is missing."), - MD05("CollectionNotDue", "Creditor or creditor's agent should not have collected the direct debit"), - MD06("RefundRequestByEndCustomer", "Return of funds requested by end customer"), - MD07("EndCustomerDeceased", "End customer is deceased."), - MS02("NotSpecifiedReasonCustomerGenerated", "Reason has not been specified by end customer"), - MS03("NotSpecifiedReasonAgentGenerated", "Reason has not been specified by agent."), - NARR("Narrative", "Reason is provided as narrative information in the additional reason information."), - NERI("NoERI", "Credit transfer is tagged as an Extended Remittance Information (ERI) transaction but does not contain ERI."), - NOAR("NonAgreedRTP", "No existing agreement for receiving request-to-pay messages."), - NOAS("NoAnswerFromCustomer", "No response from Beneficiary."), - NOCM("NotCompliantGeneric", "Customer account is not compliant with regulatory requirements, for example FICA (in South Africa) or any other regulatory requirements which render an account inactive for certain processing."), - NOPG("NoPaymentGuarantee", "Requested payment guarantee (by Creditor) related to a request-to-pay cannot be provided."), - NRCH("PayerOrPayerRTPSPNotReachable", "Recipient side of the request-to-pay (payer or its request-to-pay service provider) is not reachable."), - PINS("TypeOfPaymentInstrumentNotSupported", "Type of payment requested in the request-to-pay is not supported by the payer."), - RC01("BankIdentifierIncorrect", "Bank identifier code specified in the message has an incorrect format (formerly IncorrectFormatForRoutingCode)."), - RC02("InvalidBankIdentifier", "Bank identifier is invalid or missing."), - RC03("InvalidDebtorBankIdentifier", "Debtor bank identifier is invalid or missing"), - RC04("InvalidCreditorBankIdentifier", "Creditor bank identifier is invalid or missing"), - RC05("InvalidBICIdentifier", "BIC identifier is invalid or missing."), - RC06("InvalidDebtorBICIdentifier", "Debtor BIC identifier is invalid or missing"), - RC07("InvalidCreditorBICIdentifier", "Creditor BIC identifier is invalid or missing"), - RC08("InvalidClearingSystemMemberIdentifier", "ClearingSystemMemberidentifier is invalid or missing."), - RC09("InvalidDebtorClearingSystemMemberIdentifier", "Debtor ClearingSystemMember identifier is invalid or missing"), - RC10("InvalidCreditorClearingSystemMemberIdentifier", "Creditor ClearingSystemMember identifier is invalid or missing"), - RC11("InvalidIntermediaryAgent", "Intermediary Agent is invalid or missing"), - RC12("MissingCreditorSchemeId", "Creditor Scheme Id is invalid or missing"), - RCON("RMessageConflict", "Conflict with R-Message"), - RECI("ReceiverCustomerInformation", "Further information regarding the intended recipient."), - REPR("RTPReceivedCanBeProcessed", "Request-to-pay has been received and can be processed further."), - RF01("NotUniqueTransactionReference", "Transaction reference is not unique within the message."), - RR01("MissingDebtorAccountOrIdentification", "Specification of the debtor’s account or unique identification needed for reasons of regulatory requirements is insufficient or missing"), - RR02("MissingDebtorNameOrAddress", "Specification of the debtor’s name and/or address needed for regulatory requirements is insufficient or missing."), - RR03("MissingCreditorNameOrAddress", "Specification of the creditor’s name and/or address needed for regulatory requirements is insufficient or missing."), - RR04("RegulatoryReason", "Regulatory Reason"), - RR05("RegulatoryInformationInvalid", "Regulatory or Central Bank Reporting information missing, incomplete or invalid."), - RR06("TaxInformationInvalid", "Tax information missing, incomplete or invalid."), - RR07("RemittanceInformationInvalid", "Remittance information structure does not comply with rules for payment type."), - RR08("RemittanceInformationTruncated", "Remittance information truncated to comply with rules for payment type."), - RR09("InvalidStructuredCreditorReference", "Structured creditor reference invalid or missing."), - RR10("InvalidCharacterSet", "Character set supplied not valid for the country and payment type."), - RR11("InvalidDebtorAgentServiceID", "Invalid or missing identification of a bank proprietary service."), - RR12("InvalidPartyID", "Invalid or missing identification required within a particular country or payment type."), - RTNS("RTPNotSupportedForDebtor", "Debtor does not support request-to-pay transactions."), - RUTA("ReturnUponUnableToApply", "Return following investigation request and no remediation possible."), - S000("ValidRequestForCancellationAcknowledged", "Request for Cancellation is acknowledged following validation."), - S001("UETRFlaggedForCancellation", "Unique End-to-end Transaction Reference (UETR) relating to a payment has been identified as being associated with a Request for Cancellation."), - S002("NetworkStopOfUETR", "Unique End-to-end Transaction Reference (UETR) relating to a payment has been prevent from traveling across a messaging network."), - S003("RequestForCancellationForwarded", "Request for Cancellation has been forwarded to the payment processing/last payment processing agent."), - S004("RequestForCancellationDeliveryAcknowledgement", "Request for Cancellation has been acknowledged as delivered to payment processing/last payment processing agent."), - SL01("SpecificServiceOfferedByDebtorAgent", "Due to specific service offered by the Debtor Agent."), - SL02("SpecificServiceOfferedByCreditorAgent", "Due to specific service offered by the Creditor Agent."), - SL03("ServiceofClearingSystem", "Due to a specific service offered by the clearing system."), - SL11("CreditorNotOnWhitelistOfDebtor", "Whitelisting service offered by the Debtor Agent; Debtor has not included the Creditor on its “Whitelist” (yet). In the Whitelist the Debtor may list all allowed Creditors to debit Debtor bank account."), - SL12("CreditorOnBlacklistOfDebtor", "Blacklisting service offered by the Debtor Agent; Debtor included the Creditor on his “Blacklist”. In the Blacklist the Debtor may list all Creditors not allowed to debit Debtor bank account."), - SL13("MaximumNumberOfDirectDebitTransactionsExceeded", "Due to Maximum allowed Direct Debit Transactions per period service offered by the Debtor Agent."), - SL14("MaximumDirectDebitTransactionAmountExceeded", "Due to Maximum allowed Direct Debit Transaction amount service offered by the Debtor Agent."), - SPII("RTPServiceProviderIdentifierIncorrect", "Identifier of the request-to-pay service provider is incorrect."), - TA01("TransmissonAborted", "The transmission of the file was not successful – it had to be aborted (for technical reasons)"), - TD01("NoDataAvailable", "There is no data available (for download)"), - TD02("FileNonReadable", "The file cannot be read (e.g. unknown format)"), - TD03("IncorrectFileStructure", "The file format is incomplete or invalid"), - TK01("TokenInvalid", "Token is invalid."), - TK02("SenderTokenNotFound", "Token used for the sender does not exist."), - TK03("ReceiverTokenNotFound", "Token used for the receiver does not exist."), - TK09("TokenMissing", "Token required for request is missing."), - TKCM("TokenCounterpartyMismatch", "Token found with counterparty mismatch."), - TKSG("TokenSingleUse", "Single Use Token already used."), - TKSP("TokenSuspended", "Token found with suspended status."), - TKVE("TokenValueLimitExceeded", "Token found with value limit rule violation."), - TKXP("TokenExpired", "Token expired."), - TM01("InvalidCutOffTime", "Associated message, payment information block, or transaction was received after agreed processing cut-off time."), - TS01("TransmissionSuccessful", "The (technical) transmission of the file was successful."), - TS04("TransferToSignByHand", "The order was transferred to pass by accompanying note signed by hand"), - UCRD("UnknownCreditor", "Unknown Creditor."), - UPAY("UnduePayment", "Payment is not justified."), -} - -enum class ExternalPaymentGroupStatusCode(val isoCode: String, val description: String) { - ACCC("AcceptedSettlementCompletedCreditorAccount", "Settlement on the creditor's account has been completed."), - ACCP("AcceptedCustomerProfile", "Preceding check of technical validation was successful. Customer profile check was also successful."), - ACSC("AcceptedSettlementCompletedDebitorAccount", "Settlement on the debtor's account has been completed."), - ACSP("AcceptedSettlementInProcess", "All preceding checks such as technical validation and customer profile were successful and therefore the payment initiation has been accepted for execution."), - ACTC("AcceptedTechnicalValidation", "Authentication and syntactical and semantical validation are successful"), - ACWC("AcceptedWithChange", "Instruction is accepted but a change will be made, such as date or remittance not sent."), - PART("PartiallyAccepted", "A number of transactions have been accepted, whereas another number of transactions have not yet achieved"), - PDNG("Pending", "Payment initiation or individual transaction included in the payment initiation is pending. Further checks and status update will be performed."), - RCVD("Received", "Payment initiation has been received by the receiving agent"), - RJCT("Rejected", "Payment initiation or individual transaction included in the payment initiation has been rejected."), -} diff --git a/ebics/src/main/kotlin/Iso20022CodeSets.kt b/ebics/src/main/kotlin/Iso20022CodeSets.kt @@ -0,0 +1,330 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2024 Taler Systems S.A. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +// THIS FILE IS GENERATED, DO NOT EDIT + +package tech.libeufin.ebics + +enum class ExternalStatusReasonCode(val isoCode: String, val description: String) { + AB01("AbortedClearingTimeout", "Clearing process aborted due to timeout."), + AB02("AbortedClearingFatalError", "Clearing process aborted due to a fatal error."), + AB03("AbortedSettlementTimeout", "Settlement aborted due to timeout."), + AB04("AbortedSettlementFatalError", "Settlement process aborted due to a fatal error."), + AB05("TimeoutCreditorAgent", "Transaction stopped due to timeout at the Creditor Agent."), + AB06("TimeoutInstructedAgent", "Transaction stopped due to timeout at the Instructed Agent."), + AB07("OfflineAgent", "Agent of message is not online."), + AB08("OfflineCreditorAgent", "Creditor Agent is not online."), + AB09("ErrorCreditorAgent", "Transaction stopped due to error at the Creditor Agent."), + AB10("ErrorInstructedAgent", "Transaction stopped due to error at the Instructed Agent."), + AB11("TimeoutDebtorAgent", "Transaction stopped due to timeout at the Debtor Agent."), + AC01("IncorrectAccountNumber", "Account number is invalid or missing."), + AC02("InvalidDebtorAccountNumber", "Debtor account number invalid or missing"), + AC03("InvalidCreditorAccountNumber", "Creditor account number invalid or missing"), + AC04("ClosedAccountNumber", "Account number specified has been closed on the bank of account's books."), + AC05("ClosedDebtorAccountNumber", "Debtor account number closed"), + AC06("BlockedAccount", "Account specified is blocked, prohibiting posting of transactions against it."), + AC07("ClosedCreditorAccountNumber", "Creditor account number closed"), + AC08("InvalidBranchCode", "Branch code is invalid or missing"), + AC09("InvalidAccountCurrency", "Account currency is invalid or missing"), + AC10("InvalidDebtorAccountCurrency", "Debtor account currency is invalid or missing"), + AC11("InvalidCreditorAccountCurrency", "Creditor account currency is invalid or missing"), + AC12("InvalidAccountType", "Account type missing or invalid."), + AC13("InvalidDebtorAccountType", "Debtor account type missing or invalid"), + AC14("InvalidCreditorAccountType", "Creditor account type missing or invalid"), + AC15("AccountDetailsChanged", "The account details for the counterparty have changed."), + AC16("CardNumberInvalid", "Credit or debit card number is invalid."), + AEXR("AlreadyExpiredRTP", "Request-to-pay Expiry Date and Time has already passed."), + AG01("TransactionForbidden", "Transaction forbidden on this type of account (formerly NoAgreement)"), + AG02("InvalidBankOperationCode", "Bank Operation code specified in the message is not valid for receiver"), + AG03("TransactionNotSupported", "Transaction type not supported/authorized on this account"), + AG04("InvalidAgentCountry", "Agent country code is missing or invalid."), + AG05("InvalidDebtorAgentCountry", "Debtor agent country code is missing or invalid"), + AG06("InvalidCreditorAgentCountry", "Creditor agent country code is missing or invalid"), + AG07("UnsuccesfulDirectDebit", "Debtor account cannot be debited for a generic reason."), + AG08("InvalidAccessRights", "Transaction failed due to invalid or missing user or access right"), + AG09("PaymentNotReceived", "Original payment never received."), + AG10("AgentSuspended", "Agent of message is suspended from the Real Time Payment system."), + AG11("CreditorAgentSuspended", "Creditor Agent of message is suspended from the Real Time Payment system."), + AG12("NotAllowedBookTransfer", "Payment orders made by transferring funds from one account to another at the same financial institution (bank or payment institution) are not allowed."), + AG13("ForbiddenReturnPayment", "Returned payments derived from previously returned transactions are not allowed."), + AGNT("IncorrectAgent", "Agent in the payment workflow is incorrect"), + ALAC("AlreadyAcceptedRTP", "Request-to-pay has already been accepted by the Debtor."), + AM01("ZeroAmount", "Specified message amount is equal to zero"), + AM02("NotAllowedAmount", "Specific transaction/message amount is greater than allowed maximum"), + AM03("NotAllowedCurrency", "Specified message amount is an non processable currency outside of existing agreement"), + AM04("InsufficientFunds", "Amount of funds available to cover specified message amount is insufficient."), + AM05("Duplication", "Duplication"), + AM06("TooLowAmount", "Specified transaction amount is less than agreed minimum."), + AM07("BlockedAmount", "Amount specified in message has been blocked by regulatory authorities."), + AM09("WrongAmount", "Amount received is not the amount agreed or expected"), + AM10("InvalidControlSum", "Sum of instructed amounts does not equal the control sum."), + AM11("InvalidTransactionCurrency", "Transaction currency is invalid or missing"), + AM12("InvalidAmount", "Amount is invalid or missing"), + AM13("AmountExceedsClearingSystemLimit", "Transaction amount exceeds limits set by clearing system"), + AM14("AmountExceedsAgreedLimit", "Transaction amount exceeds limits agreed between bank and client"), + AM15("AmountBelowClearingSystemMinimum", "Transaction amount below minimum set by clearing system"), + AM16("InvalidGroupControlSum", "Control Sum at the Group level is invalid"), + AM17("InvalidPaymentInfoControlSum", "Control Sum at the Payment Information level is invalid"), + AM18("InvalidNumberOfTransactions", "Number of transactions is invalid or missing."), + AM19("InvalidGroupNumberOfTransactions", "Number of transactions at the Group level is invalid or missing"), + AM20("InvalidPaymentInfoNumberOfTransactions", "Number of transactions at the Payment Information level is invalid"), + AM21("LimitExceeded", "Transaction amount exceeds limits agreed between bank and client."), + AM22("ZeroAmountNotApplied", "Unable to apply zero amount to designated account. For example, where the rules of a service allow the use of zero amount payments, however the back-office system is unable to apply the funds to the account. If the rules of a service prohibit the use of zero amount payments, then code AM01 is used to report the error condition."), + AM23("AmountExceedsSettlementLimit", "Transaction amount exceeds settlement limit."), + APAR("AlreadyPaidRTP", "Request To Pay has already been paid by the Debtor."), + ARFR("AlreadyRefusedRTP", "Request-to-pay has already been refused by the Debtor."), + ARJR("AlreadyRejectedRTP", "Request-to-pay has already been rejected."), + ATNS("AttachementsNotSupported", "Attachments to the request-to-pay are not supported."), + BE01("InconsistenWithEndCustomer", "Identification of end customer is not consistent with associated account number. (formerly CreditorConsistency)."), + BE04("MissingCreditorAddress", "Specification of creditor's address, which is required for payment, is missing/not correct (formerly IncorrectCreditorAddress)."), + BE05("UnrecognisedInitiatingParty", "Party who initiated the message is not recognised by the end customer"), + BE06("UnknownEndCustomer", "End customer specified is not known at associated Sort/National Bank Code or does no longer exist in the books"), + BE07("MissingDebtorAddress", "Specification of debtor's address, which is required for payment, is missing/not correct."), + BE08("MissingDebtorName", "Debtor name is missing"), + BE09("InvalidCountry", "Country code is missing or Invalid."), + BE10("InvalidDebtorCountry", "Debtor country code is missing or invalid"), + BE11("InvalidCreditorCountry", "Creditor country code is missing or invalid"), + BE12("InvalidCountryOfResidence", "Country code of residence is missing or Invalid."), + BE13("InvalidDebtorCountryOfResidence", "Country code of debtor's residence is missing or Invalid"), + BE14("InvalidCreditorCountryOfResidence", "Country code of creditor's residence is missing or Invalid"), + BE15("InvalidIdentificationCode", "Identification code missing or invalid."), + BE16("InvalidDebtorIdentificationCode", "Debtor or Ultimate Debtor identification code missing or invalid"), + BE17("InvalidCreditorIdentificationCode", "Creditor or Ultimate Creditor identification code missing or invalid"), + BE18("InvalidContactDetails", "Contact details missing or invalid"), + BE19("InvalidChargeBearerCode", "Charge bearer code for transaction type is invalid"), + BE20("InvalidNameLength", "Name length exceeds local rules for payment type."), + BE21("MissingName", "Name missing or invalid. Generic usage if cannot specifically identify debtor or creditor."), + BE22("MissingCreditorName", "Creditor name is missing"), + BE23("AccountProxyInvalid", "Phone number or email address, or any other proxy, used as the account proxy is unknown or invalid."), + CERI("CheckERI", "Credit transfer is not tagged as an Extended Remittance Information (ERI) transaction but contains ERI."), + CH03("RequestedExecutionDateOrRequestedCollectionDateTooFarInFuture", "Value in Requested Execution Date or Requested Collection Date is too far in the future"), + CH04("RequestedExecutionDateOrRequestedCollectionDateTooFarInPast", "Value in Requested Execution Date or Requested Collection Date is too far in the past"), + CH07("ElementIsNotToBeUsedAtB-andC-Level", "Element is not to be used at B- and C-Level"), + CH09("MandateChangesNotAllowed", "Mandate changes are not allowed"), + CH10("InformationOnMandateChangesMissing", "Information on mandate changes are missing"), + CH11("CreditorIdentifierIncorrect", "Value in Creditor Identifier is incorrect"), + CH12("CreditorIdentifierNotUnambiguouslyAtTransaction-Level", "Creditor Identifier is ambiguous at Transaction Level"), + CH13("OriginalDebtorAccountIsNotToBeUsed", "Original Debtor Account is not to be used"), + CH14("OriginalDebtorAgentIsNotToBeUsed", "Original Debtor Agent is not to be used"), + CH15("ElementContentIncludesMoreThan140Characters", "Content Remittance Information/Structured includes more than 140 characters"), + CH16("ElementContentFormallyIncorrect", "Content is incorrect"), + CH17("ElementNotAdmitted", "Element is not allowed"), + CH19("ValuesWillBeSetToNextTARGETday", "Values in Interbank Settlement Date or Requested Collection Date will be set to the next TARGET day"), + CH20("DecimalPointsNotCompatibleWithCurrency", "Number of decimal points not compatible with the currency"), + CH21("RequiredCompulsoryElementMissing", "Mandatory element is missing"), + CH22("COREandB2BwithinOnemessage", "SDD CORE and B2B not permitted within one message"), + CHQC("ChequeSettledOnCreditorAccount", "Cheque has been presented in cheque clearing and settled on the creditor’s account."), + CN01("AuthorisationCancelled", "Authorisation is cancelled."), + CNOR("CreditorBankIsNotRegistered", "Creditor bank is not registered under this BIC in the CSM"), + CURR("IncorrectCurrency", "Currency of the payment is incorrect"), + CUST("RequestedByCustomer", "Cancellation requested by the Debtor"), + DC02("SettlementNotReceived", "Rejection of a payment due to covering FI settlement not being received."), + DNOR("DebtorBankIsNotRegistered", "Debtor bank is not registered under this BIC in the CSM"), + DS01("ElectronicSignaturesCorrect", "The electronic signature(s) is/are correct"), + DS02("OrderCancelled", "An authorized user has cancelled the order"), + DS03("OrderNotCancelled", "The user’s attempt to cancel the order was not successful"), + DS04("OrderRejected", "The order was rejected by the bank side (for reasons concerning content)"), + DS05("OrderForwardedForPostprocessing", "The order was correct and could be forwarded for postprocessing"), + DS06("TransferOrder", "The order was transferred to VEU"), + DS07("ProcessingOK", "All actions concerning the order could be done by the EBICS bank server"), + DS08("DecompressionError", "The decompression of the file was not successful"), + DS09("DecryptionError", "The decryption of the file was not successful"), + DS0A("DataSignRequested", "Data signature is required."), + DS0B("UnknownDataSignFormat", "Data signature for the format is not available or invalid."), + DS0C("SignerCertificateRevoked", "The signer certificate is revoked."), + DS0D("SignerCertificateNotValid", "The signer certificate is not valid (revoked or not active)."), + DS0E("IncorrectSignerCertificate", "The signer certificate is not present."), + DS0F("SignerCertificationAuthoritySignerNotValid", "The authority of the signer certification sending the certificate is unknown."), + DS0G("NotAllowedPayment", "Signer is not allowed to sign this operation type."), + DS0H("NotAllowedAccount", "Signer is not allowed to sign for this account."), + DS0K("NotAllowedNumberOfTransaction", "The number of transaction is over the number allowed for this signer."), + DS10("Signer1CertificateRevoked", "The certificate is revoked for the first signer."), + DS11("Signer1CertificateNotValid", "The certificate is not valid (revoked or not active) for the first signer."), + DS12("IncorrectSigner1Certificate", "The certificate is not present for the first signer."), + DS13("SignerCertificationAuthoritySigner1NotValid", "The authority of signer certification sending the certificate is unknown for the first signer."), + DS14("UserDoesNotExist", "The user is unknown on the server"), + DS15("IdenticalSignatureFound", "The same signature has already been sent to the bank"), + DS16("PublicKeyVersionIncorrect", "The public key version is not correct. This code is returned when a customer sends signature files to the financial institution after conversion from an older program version (old ES format) to a new program version (new ES format) without having carried out re-initialisation with regard to a public key change."), + DS17("DifferentOrderDataInSignatures", "Order data and signatures don’t match"), + DS18("RepeatOrder", "File cannot be tested, the complete order has to be repeated. This code is returned in the event of a malfunction during the signature check, e.g. not enough storage space."), + DS19("ElectronicSignatureRightsInsufficient", "The user’s rights (concerning his signature) are insufficient to execute the order"), + DS20("Signer2CertificateRevoked", "The certificate is revoked for the second signer."), + DS21("Signer2CertificateNotValid", "The certificate is not valid (revoked or not active) for the second signer."), + DS22("IncorrectSigner2Certificate", "The certificate is not present for the second signer."), + DS23("SignerCertificationAuthoritySigner2NotValid", "The authority of signer certification sending the certificate is unknown for the second signer."), + DS24("WaitingTimeExpired", "Waiting time expired due to incomplete order"), + DS25("OrderFileDeleted", "The order file was deleted by the bank server"), + DS26("UserSignedMultipleTimes", "The same user has signed multiple times"), + DS27("UserNotYetActivated", "The user is not yet activated (technically)"), + DT01("InvalidDate", "Invalid date (eg, wrong or missing settlement date)"), + DT02("InvalidCreationDate", "Invalid creation date and time in Group Header (eg, historic date)"), + DT03("InvalidNonProcessingDate", "Invalid non bank processing date (eg, weekend or local public holiday)"), + DT04("FutureDateNotSupported", "Future date not supported"), + DT05("InvalidCutOffDate", "Associated message, payment information block or transaction was received after agreed processing cut-off date, i.e., date in the past."), + DT06("ExecutionDateChanged", "Execution Date has been modified in order for transaction to be processed"), + DU01("DuplicateMessageID", "Message Identification is not unique."), + DU02("DuplicatePaymentInformationID", "Payment Information Block is not unique."), + DU03("DuplicateTransaction", "Transaction is not unique."), + DU04("DuplicateEndToEndID", "End To End ID is not unique."), + DU05("DuplicateInstructionID", "Instruction ID is not unique."), + DUPL("DuplicatePayment", "Payment is a duplicate of another payment"), + ED01("CorrespondentBankNotPossible", "Correspondent bank not possible."), + ED03("BalanceInfoRequest", "Balance of payments complementary info is requested"), + ED05("SettlementFailed", "Settlement of the transaction has failed."), + ED06("SettlementSystemNotAvailable", "Interbank settlement system not available."), + EDTL("ExpiryDateTooLong", "Expiry date time of the request-to-pay is too far in the future."), + EDTR("ExpiryDateTimeReached", "Expiry date time of the request-to-pay is already reached."), + ERIN("ERIOptionNotSupported", "Extended Remittance Information (ERI) option is not supported."), + FF01("InvalidFileFormat", "File Format incomplete or invalid"), + FF02("SyntaxError", "Syntax error reason is provided as narrative information in the additional reason information."), + FF03("InvalidPaymentTypeInformation", "Payment Type Information is missing or invalid."), + FF04("InvalidServiceLevelCode", "Service Level code is missing or invalid"), + FF05("InvalidLocalInstrumentCode", "Local Instrument code is missing or invalid"), + FF06("InvalidCategoryPurposeCode", "Category Purpose code is missing or invalid"), + FF07("InvalidPurpose", "Purpose is missing or invalid"), + FF08("InvalidEndToEndId", "End to End Id missing or invalid"), + FF09("InvalidChequeNumber", "Cheque number missing or invalid"), + FF10("BankSystemProcessingError", "File or transaction cannot be processed due to technical issues at the bank side"), + FF11("ClearingRequestAborted", "Clearing request rejected due it being subject to an abort operation."), + FF12("OriginalTransactionNotEligibleForRequestedReturn", "Original payment is not eligible to be returned given its current status."), + FF13("RequestForCancellationNotFound", "No record of request for cancellation found."), + FOCR("FollowingCancellationRequest", "Return following a cancellation request."), + FR01("Fraud", "Returned as a result of fraud."), + FRAD("FraudulentOrigin", "Cancellation requested following a transaction that was originated fraudulently. The use of the FraudulentOrigin code should be governed by jurisdictions."), + G000("PaymentTransferredAndTracked", "In an FI To FI Customer Credit Transfer: The Status Originator transferred the payment to the next Agent or to a Market Infrastructure. The payment transfer is tracked. No further updates will follow from the Status Originator."), + G001("PaymentTransferredAndNotTracked", "In an FI To FI Customer Credit Transfer: The Status Originator transferred the payment to the next Agent or to a Market Infrastructure. The payment transfer is not tracked. No further updates will follow from the Status Originator."), + G002("CreditDebitNotConfirmed", "In a FIToFI Customer Credit Transfer: Credit to the creditor’s account may not be confirmed same day. Update will follow from the Status Originator."), + G003("CreditPendingDocuments", "In a FIToFI Customer Credit Transfer: Credit to creditor’s account is pending receipt of required documents. The Status Originator has requested creditor to provide additional documentation. Update will follow from the Status Originator."), + G004("CreditPendingFunds", "In a FIToFI Customer Credit Transfer: Credit to the creditor’s account is pending, status Originator is waiting for funds provided via a cover. Update will follow from the Status Originator."), + G005("DeliveredWithServiceLevel", "Payment has been delivered to creditor agent with service level."), + G006("DeliveredWIthoutServiceLevel", "Payment has been delivered to creditor agent without service level."), + ID01("CorrespondingOriginalFileStillNotSent", "Signature file was sent to the bank but the corresponding original file has not been sent yet."), + IEDT("IncorrectExpiryDateTime", "Expiry date time of the request-to-pay is incorrect."), + IRNR("InitialRTPNeverReceived", "No initial request-to-pay has been received."), + MD01("NoMandate", "No Mandate"), + MD02("MissingMandatoryInformationInMandate", "Mandate related information data required by the scheme is missing."), + MD05("CollectionNotDue", "Creditor or creditor's agent should not have collected the direct debit"), + MD06("RefundRequestByEndCustomer", "Return of funds requested by end customer"), + MD07("EndCustomerDeceased", "End customer is deceased."), + MS02("NotSpecifiedReasonCustomerGenerated", "Reason has not been specified by end customer"), + MS03("NotSpecifiedReasonAgentGenerated", "Reason has not been specified by agent."), + NARR("Narrative", "Reason is provided as narrative information in the additional reason information."), + NERI("NoERI", "Credit transfer is tagged as an Extended Remittance Information (ERI) transaction but does not contain ERI."), + NOAR("NonAgreedRTP", "No existing agreement for receiving request-to-pay messages."), + NOAS("NoAnswerFromCustomer", "No response from Beneficiary."), + NOCM("NotCompliantGeneric", "Customer account is not compliant with regulatory requirements, for example FICA (in South Africa) or any other regulatory requirements which render an account inactive for certain processing."), + NOPG("NoPaymentGuarantee", "Requested payment guarantee (by Creditor) related to a request-to-pay cannot be provided."), + NRCH("PayerOrPayerRTPSPNotReachable", "Recipient side of the request-to-pay (payer or its request-to-pay service provider) is not reachable."), + PINS("TypeOfPaymentInstrumentNotSupported", "Type of payment requested in the request-to-pay is not supported by the payer."), + RC01("BankIdentifierIncorrect", "Bank identifier code specified in the message has an incorrect format (formerly IncorrectFormatForRoutingCode)."), + RC02("InvalidBankIdentifier", "Bank identifier is invalid or missing."), + RC03("InvalidDebtorBankIdentifier", "Debtor bank identifier is invalid or missing"), + RC04("InvalidCreditorBankIdentifier", "Creditor bank identifier is invalid or missing"), + RC05("InvalidBICIdentifier", "BIC identifier is invalid or missing."), + RC06("InvalidDebtorBICIdentifier", "Debtor BIC identifier is invalid or missing"), + RC07("InvalidCreditorBICIdentifier", "Creditor BIC identifier is invalid or missing"), + RC08("InvalidClearingSystemMemberIdentifier", "ClearingSystemMemberidentifier is invalid or missing."), + RC09("InvalidDebtorClearingSystemMemberIdentifier", "Debtor ClearingSystemMember identifier is invalid or missing"), + RC10("InvalidCreditorClearingSystemMemberIdentifier", "Creditor ClearingSystemMember identifier is invalid or missing"), + RC11("InvalidIntermediaryAgent", "Intermediary Agent is invalid or missing"), + RC12("MissingCreditorSchemeId", "Creditor Scheme Id is invalid or missing"), + RCON("RMessageConflict", "Conflict with R-Message"), + RECI("ReceiverCustomerInformation", "Further information regarding the intended recipient."), + REPR("RTPReceivedCanBeProcessed", "Request-to-pay has been received and can be processed further."), + RF01("NotUniqueTransactionReference", "Transaction reference is not unique within the message."), + RR01("MissingDebtorAccountOrIdentification", "Specification of the debtor’s account or unique identification needed for reasons of regulatory requirements is insufficient or missing"), + RR02("MissingDebtorNameOrAddress", "Specification of the debtor’s name and/or address needed for regulatory requirements is insufficient or missing."), + RR03("MissingCreditorNameOrAddress", "Specification of the creditor’s name and/or address needed for regulatory requirements is insufficient or missing."), + RR04("RegulatoryReason", "Regulatory Reason"), + RR05("RegulatoryInformationInvalid", "Regulatory or Central Bank Reporting information missing, incomplete or invalid."), + RR06("TaxInformationInvalid", "Tax information missing, incomplete or invalid."), + RR07("RemittanceInformationInvalid", "Remittance information structure does not comply with rules for payment type."), + RR08("RemittanceInformationTruncated", "Remittance information truncated to comply with rules for payment type."), + RR09("InvalidStructuredCreditorReference", "Structured creditor reference invalid or missing."), + RR10("InvalidCharacterSet", "Character set supplied not valid for the country and payment type."), + RR11("InvalidDebtorAgentServiceID", "Invalid or missing identification of a bank proprietary service."), + RR12("InvalidPartyID", "Invalid or missing identification required within a particular country or payment type."), + RTNS("RTPNotSupportedForDebtor", "Debtor does not support request-to-pay transactions."), + RUTA("ReturnUponUnableToApply", "Return following investigation request and no remediation possible."), + S000("ValidRequestForCancellationAcknowledged", "Request for Cancellation is acknowledged following validation."), + S001("UETRFlaggedForCancellation", "Unique End-to-end Transaction Reference (UETR) relating to a payment has been identified as being associated with a Request for Cancellation."), + S002("NetworkStopOfUETR", "Unique End-to-end Transaction Reference (UETR) relating to a payment has been prevent from traveling across a messaging network."), + S003("RequestForCancellationForwarded", "Request for Cancellation has been forwarded to the payment processing/last payment processing agent."), + S004("RequestForCancellationDeliveryAcknowledgement", "Request for Cancellation has been acknowledged as delivered to payment processing/last payment processing agent."), + SL01("SpecificServiceOfferedByDebtorAgent", "Due to specific service offered by the Debtor Agent."), + SL02("SpecificServiceOfferedByCreditorAgent", "Due to specific service offered by the Creditor Agent."), + SL03("ServiceofClearingSystem", "Due to a specific service offered by the clearing system."), + SL11("CreditorNotOnWhitelistOfDebtor", "Whitelisting service offered by the Debtor Agent; Debtor has not included the Creditor on its “Whitelist” (yet). In the Whitelist the Debtor may list all allowed Creditors to debit Debtor bank account."), + SL12("CreditorOnBlacklistOfDebtor", "Blacklisting service offered by the Debtor Agent; Debtor included the Creditor on his “Blacklist”. In the Blacklist the Debtor may list all Creditors not allowed to debit Debtor bank account."), + SL13("MaximumNumberOfDirectDebitTransactionsExceeded", "Due to Maximum allowed Direct Debit Transactions per period service offered by the Debtor Agent."), + SL14("MaximumDirectDebitTransactionAmountExceeded", "Due to Maximum allowed Direct Debit Transaction amount service offered by the Debtor Agent."), + SPII("RTPServiceProviderIdentifierIncorrect", "Identifier of the request-to-pay service provider is incorrect."), + TA01("TransmissonAborted", "The transmission of the file was not successful – it had to be aborted (for technical reasons)"), + TD01("NoDataAvailable", "There is no data available (for download)"), + TD02("FileNonReadable", "The file cannot be read (e.g. unknown format)"), + TD03("IncorrectFileStructure", "The file format is incomplete or invalid"), + TK01("TokenInvalid", "Token is invalid."), + TK02("SenderTokenNotFound", "Token used for the sender does not exist."), + TK03("ReceiverTokenNotFound", "Token used for the receiver does not exist."), + TK09("TokenMissing", "Token required for request is missing."), + TKCM("TokenCounterpartyMismatch", "Token found with counterparty mismatch."), + TKSG("TokenSingleUse", "Single Use Token already used."), + TKSP("TokenSuspended", "Token found with suspended status."), + TKVE("TokenValueLimitExceeded", "Token found with value limit rule violation."), + TKXP("TokenExpired", "Token expired."), + TM01("InvalidCutOffTime", "Associated message, payment information block, or transaction was received after agreed processing cut-off time."), + TS01("TransmissionSuccessful", "The (technical) transmission of the file was successful."), + TS04("TransferToSignByHand", "The order was transferred to pass by accompanying note signed by hand"), + UCRD("UnknownCreditor", "Unknown Creditor."), + UPAY("UnduePayment", "Payment is not justified."), +} + +enum class ExternalPaymentGroupStatusCode(val isoCode: String, val description: String) { + ACCC("AcceptedSettlementCompletedCreditorAccount", "Settlement on the creditor's account has been completed."), + ACCP("AcceptedCustomerProfile", "Preceding check of technical validation was successful. Customer profile check was also successful."), + ACSC("AcceptedSettlementCompletedDebitorAccount", "Settlement on the debtor's account has been completed."), + ACSP("AcceptedSettlementInProcess", "All preceding checks such as technical validation and customer profile were successful and therefore the payment initiation has been accepted for execution."), + ACTC("AcceptedTechnicalValidation", "Authentication and syntactical and semantical validation are successful"), + ACWC("AcceptedWithChange", "Instruction is accepted but a change will be made, such as date or remittance not sent."), + PART("PartiallyAccepted", "A number of transactions have been accepted, whereas another number of transactions have not yet achieved"), + PDNG("Pending", "Payment initiation or individual transaction included in the payment initiation is pending. Further checks and status update will be performed."), + RCVD("Received", "Payment initiation has been received by the receiving agent"), + RJCT("Rejected", "Payment initiation or individual transaction included in the payment initiation has been rejected."), +} + +enum class ExternalPaymentTransactionStatusCode(val isoCode: String, val description: String) { + ACCC("AcceptedSettlementCompletedCreditorAccount", "Settlement on the creditor's account has been completed."), + ACCP("AcceptedCustomerProfile", "Preceding check of technical validation was successful. Customer profile check was also successful."), + ACFC("AcceptedFundsChecked", "Preceding check of technical validation and customer profile was successful and an automatic funds check was positive."), + ACIS("AcceptedandChequeIssued", "Payment instruction to issue a cheque has been accepted, and the cheque has been issued but not yet been deposited or cleared."), + ACPD("AcceptedClearingProcessed", "Status of transaction released from the Debtor Agent and accepted by the clearing."), + ACSC("AcceptedSettlementCompletedDebitorAccount", "Settlement completed."), + ACSP("AcceptedSettlementInProcess", "All preceding checks such as technical validation and customer profile were successful and therefore the payment instruction has been accepted for execution."), + ACTC("AcceptedTechnicalValidation", "Authentication and syntactical and semantical validation are successful"), + ACWC("AcceptedWithChange", "Instruction is accepted but a change will be made, such as date or remittance not sent."), + ACWP("AcceptedWithoutPosting", "Payment instruction included in the credit transfer is accepted without being posted to the creditor customer’s account."), + BLCK("Blocked", "Payment transaction previously reported with status 'ACWP' is blocked, for example, funds will neither be posted to the Creditor's account, nor be returned to the Debtor."), + CANC("Cancelled", "Payment initiation has been successfully cancelled after having received a request for cancellation."), + CPUC("CashPickedUpByCreditor", "Cash has been picked up by the Creditor."), + PATC("PartiallyAcceptedTechnicalCorrect", "Payment initiation needs multiple authentications, where some but not yet all have been performed. Syntactical and semantical validations are successful."), + PDNG("Pending", "Payment instruction is pending. Further checks and status update will be performed."), + PRES("Presented", "Request for Payment has been presented to the Debtor."), + RCVD("Received", "Payment instruction has been received."), + RJCT("Rejected", "Payment instruction has been rejected."), +} diff --git a/ebics/src/main/kotlin/Iso20022Constants.kt b/ebics/src/main/kotlin/Iso20022Constants.kt @@ -0,0 +1,35 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2024 Taler Systems S.A. + + * LibEuFin is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation; either version 3, or + * (at your option) any later version. + + * LibEuFin is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General + * Public License for more details. + + * You should have received a copy of the GNU Affero General Public + * License along with LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +package tech.libeufin.ebics + +enum class HacAction(val description: String) { + FILE_UPLOAD("File submitted to the bank"), + FILE_DOWNLOAD("File downloaded from the bank"), + ES_UPLOAD("Electronic signature submitted to the bank"), + ES_DOWNLOAD("Electronic signature downloaded from the bank"), + ES_VERIFICATION("Signature verification"), + VEU_FORWARDING("Forwarding to EDS"), + VEU_VERIFICATION("EDS signature verification"), + VEU_VERIFICATION_END("VEU_VERIFICATION_END"), + VEU_CANCEL_ORDER("Cancellation of EDS order"), + ADDITIONAL("Additional information"), + ORDER_HAC_FINAL_POS("HAC end of order (positive)"), + ORDER_HAC_FINAL_NEG("ORDER_HAC_FINAL_NEG") +} +\ No newline at end of file diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt @@ -25,6 +25,7 @@ import io.ktor.client.* import kotlinx.coroutines.* import tech.libeufin.nexus.ebics.* import tech.libeufin.common.* +import tech.libeufin.ebics.* import tech.libeufin.ebics.ebics_h005.Ebics3Request import java.io.IOException import java.time.Instant @@ -286,12 +287,31 @@ private fun ingestDocument( SupportedDocument.PAIN_002_LOGS -> { val acks = parseCustomerAck(xml) for (ack in acks) { - println(ack) + when (ack.actionType) { + HacAction.FILE_DOWNLOAD -> logger.trace("$ack") + HacAction.ORDER_HAC_FINAL_POS -> { + // TODO update pending transaction status + logger.debug("$ack") + logger.info("Order '${ack.orderId}' was accepted at ${ack.timestamp.fmtDateTime()}") + } + HacAction.ORDER_HAC_FINAL_NEG -> { + // TODO update pending transaction status + logger.debug("$ack") + logger.warn("Order '${ack.orderId}' was refused at ${ack.timestamp.fmtDateTime()}") + } + else -> { + // TODO update pending transaction status + logger.debug("$ack") + } + } } } SupportedDocument.PAIN_002 -> { val status = parseCustomerPaymentStatusReport(xml) - logger.debug("$status") // TODO ingest in db + if (status.paymentCode == ExternalPaymentGroupStatusCode.RJCT) + logger.warn("Transaction '${status.id()}' was rejected") + // TODO update pending transaction status + logger.debug("$status") } else -> logger.warn("Not ingesting ${whichDocument}. Only camt.054 notifications supported.") } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt @@ -122,15 +122,19 @@ fun createPain001( } data class CustomerAck( - val actionType: String, + val actionType: HacAction, + val orderId: String?, val code: ExternalStatusReasonCode?, val timestamp: Instant ) { override fun toString(): String { - return if (code != null) - "${timestamp.fmtDateTime()} ${actionType} ${code.isoCode} '${code.description}'" - else - "${timestamp.fmtDateTime()} ${actionType}" + var str = "${timestamp.fmtDateTime()}" + if (orderId != null) str += " ${orderId}" + str += " ${actionType}" + if (code != null) str += " ${code.isoCode}" + str += " - '${actionType.description}'" + if (code != null) str += " '${code.description}'" + return str } } @@ -142,9 +146,10 @@ data class CustomerAck( fun parseCustomerAck(xml: ByteArray): List<CustomerAck> { return destructXml(xml, "Document") { one("CstmrPmtStsRpt").map("OrgnlPmtInfAndSts") { - val actionType = one("OrgnlPmtInfId").text() + val actionType = one("OrgnlPmtInfId").enum<HacAction>() one("StsRsnInf") { var timestamp: Instant? = null; + var orderId: String? = null one("Orgtr").one("Id").one("OrgId").each("Othr") { val value = one("Id") val key = one("SchmeNm").one("Prtry").text() @@ -152,11 +157,12 @@ fun parseCustomerAck(xml: ByteArray): List<CustomerAck> { "TimeStamp" -> { timestamp = value.dateTime().toInstant(ZoneOffset.UTC) } + "OrderID" -> orderId = value.text() // TODO extract ids ? } } val code = opt("Rsn")?.one("Cd")?.enum<ExternalStatusReasonCode>() - CustomerAck(actionType, code, timestamp!!) + CustomerAck(actionType, orderId, code, timestamp!!) } } } @@ -164,15 +170,35 @@ fun parseCustomerAck(xml: ByteArray): List<CustomerAck> { data class PaymentStatus( val msgId: String, - val code: ExternalPaymentGroupStatusCode, + val paymentId: String?, + val txId: String?, + val paymentCode: ExternalPaymentGroupStatusCode, + val txCode: ExternalPaymentTransactionStatusCode?, val reasons: List<Reason> -) { +) { + fun id(): String { + var str = "$msgId" + if (paymentId != null) str += ".$paymentId" + if (txId != null) str += ".$txId" + return str + } + + fun code(): String = txCode?.isoCode ?: paymentCode.isoCode + + fun description(): String = txCode?.description ?: paymentCode.description + override fun toString(): String { - var builder = "'${msgId}' ${code.isoCode} '${code.description}'" - for (reason in reasons) { - builder += " - ${reason.code.isoCode} '${reason.code.description}'" + return if (reasons.isEmpty()) { + "'${id()}' ${code()} '${description()}'" + } else if (reasons.size == 1) { + "'${id()}' ${code()} ${reasons[0].code.isoCode} - '${description()}' '${reasons[0].code.description}'" + } else { + var str = "'${id()}' ${code()} '${description()}' - " + for (reason in reasons) { + str += "${reason.code.isoCode} '${reason.code.description}'" + } + str } - return builder } } @@ -195,6 +221,7 @@ fun parseCustomerPaymentStatusReport(xml: ByteArray): PaymentStatus { } } return destructXml(xml, "Document") { + // TODO handle batch status one("CstmrPmtStsRpt") { val (msgId, msgCode, msgReasons) = one("OrgnlGrpInfAndSts") { val id = one("OrgnlMsgId").text() @@ -202,19 +229,17 @@ fun parseCustomerPaymentStatusReport(xml: ByteArray): PaymentStatus { val reasons = reasons() Triple(id, code, reasons) } - val paymentInfo = opt("OrgnlPmtInfAndSts") { - val code = one("PmtInfSts").enum<ExternalPaymentGroupStatusCode>() - val reasons = reasons() - Pair(code, reasons) - } - - // TODO handle multi level code better - if (paymentInfo != null) { - val (code, reasons) = paymentInfo - PaymentStatus(msgId, code, reasons) - } else { - PaymentStatus(msgId, msgCode!!, msgReasons) - } + opt("OrgnlPmtInfAndSts") { + val payId = one("OrgnlPmtInfId").text() + val payCode = one("PmtInfSts").enum<ExternalPaymentGroupStatusCode>() + val payReasons = reasons() + opt("TxInfAndSts") { + val txId = one("OrgnlInstrId").text() + val txCode = one("TxSts").enum<ExternalPaymentTransactionStatusCode>() + val txReasons = reasons() + PaymentStatus(msgId, payId, txId, payCode, txCode, txReasons) + } ?: PaymentStatus(msgId, payId, null, payCode, null, payReasons) + } ?: PaymentStatus(msgId, null, null, msgCode!!, null, msgReasons) } } }