Skip to content
/ bank4j Public

Generates ISO 20022 Payment Initiation XML messages and provides IBAN and BIC validation

License

Notifications You must be signed in to change notification settings

inisos/bank4j

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

64 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

License: MIT

bank4j

Easily generate XML Credit Transfers based on ISO 20022 Payments Initiation (pain):

  • pain.001.001.03
  • pain.001.001.03.ch.02
  • pain.001.001.09
  • pain.001.003.03

Provides IBAN, BIC and Character set (for reference elements) validation with annotations.

Using JAXB and iban4j.

Installation

<dependency>
    <groupId>io.inisos.bank4j</groupId>
    <artifactId>bank4j</artifactId>
    <version>4.1.0</version>
</dependency>
  • Use version 3+ for jarkarta.* dependencies (Java 11, JAXB 3+, Bean Validation 3+)
  • Use version 2 for javax.* dependencies (Java 8, JAXB 2, Bean Validation 2)

Usage

Validation

class MyRecord {
    
    @IBAN
    private String iban;

    @BIC
    private String bic;
    
    @Iso20022CharacterSet
    private String reference;
    
}

Only accepts valid IBAN, BIC8/BIC11 and valid ISO 20022 patterns.

Generation of XML messages

For Payments Initiation, simply provide bank account details and transactions.

Minimal example with V09 (omitted optional fields):

import io.inisos.bank4j.Bank;

import java.time.ZonedDateTime;

import static io.inisos.bank4j.CustomerCreditTransferInitiationVersion.V09;

class MyApp {

    public static void main(String... args) {

        // Debtor account
        BankAccount debtorAccount = Bank.simpleBankAccount()
                .iban("FR7610011000201234567890188") // IBAN
                .build();

        // Transactions
        Transaction transaction1 = Bank.simpleTransaction()
                .account(Bank.simpleBankAccount()   // Creditor account
                        .iban("FR7630001007941234567890185") // IBAN
                        .build())
                .amount("12.34")                                // Amount, converted to BigDecimal
                .currency("EUR")                                // Currency code
                .endToEndId("Transfer reference 1")             // End-to-end identifier
                .build();

        // Transfer
        CreditTransferOperation creditTransfer = Bank.jaxbCreditTransferSepa(V09) // version 09
                .debtorAccount(debtorAccount)                                // Mandatory debtor account
                .transaction(transaction1)                                   // At least 1 transaction
                .requestedExecutionDateTime(ZonedDateTime.now()              // Optional requested execution date and time,
                        .plusDays(1)                                         // defaults to tomorrow
                        .withSecond(0)
                        .withNano(0))                                        
                .build();

        // export to string
        String formattedOutput = creditTransfer.marshal(true); // true: enables formatting

        // or export to file
        creditTransfer.marshal(new FileWriter("myFile.xml")); // default: disables formatting
    }
}

Output with formatting:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.09">
    <CstmrCdtTrfInitn>
        <GrpHdr>
            <MsgId>20251002092807</MsgId>
            <CreDtTm>2025-10-02T09:28:07.3573245</CreDtTm>
            <NbOfTxs>1</NbOfTxs>
            <CtrlSum>12.34</CtrlSum>
            <InitgPty/>
        </GrpHdr>
        <PmtInf>
            <PmtInfId>20251002092807</PmtInfId>
            <PmtMtd>TRF</PmtMtd>
            <BtchBookg>false</BtchBookg>
            <NbOfTxs>1</NbOfTxs>
            <CtrlSum>12.34</CtrlSum>
            <PmtTpInf>
                <SvcLvl>
                    <Cd>SEPA</Cd>
                </SvcLvl>
            </PmtTpInf>
            <ReqdExctnDt>
                <DtTm>2025-10-03T09:30:00+02:00</DtTm>
            </ReqdExctnDt>
            <Dbtr/>
            <DbtrAcct>
                <Id>
                    <IBAN>FR7610011000201234567890188</IBAN>
                </Id>
            </DbtrAcct>
            <DbtrAgt>
                <FinInstnId/>
            </DbtrAgt>
            <ChrgBr>SLEV</ChrgBr>
            <CdtTrfTxInf>
                <PmtId>
                    <EndToEndId>Transfer reference 1</EndToEndId>
                </PmtId>
                <Amt>
                    <InstdAmt Ccy="EUR">12.34</InstdAmt>
                </Amt>
                <Cdtr/>
                <CdtrAcct>
                    <Id>
                        <IBAN>FR7630001007941234567890185</IBAN>
                    </Id>
                </CdtrAcct>
            </CdtTrfTxInf>
        </PmtInf>
    </CstmrCdtTrfInitn>
</Document>

Eg. with V03 and optional fields:

import io.inisos.bank4j.Bank;

class MyApp {

    public static void main(String... args) {

        // Optional debtor identification
        Party debtor = Bank.simpleParty()
                .name("Debtor Name") // Optional name
                .build();

        // Debtor account
        BankAccount debtorAccount = Bank.simpleBankAccount()
                .iban("FR7610011000201234567890188") // IBAN
                .bic("PSSTFRPP")                     // Optional BIC
                .build();

        // Transactions
        Transaction transaction1 = Bank.simpleTransaction()
                .party(Bank.simpleParty()           // Optional creditor identification
                        .name("BANQUE DE FRANCE")                 // Optional name
                        .postalAddress(Bank.simplePostalAddress() // Optional postal address
                                .addressLine("1, rue de La Vrillière")
                                .addressLine("75001 PARIS")
                                .country("FR")
                                .build())
                        .build())
                .account(Bank.simpleBankAccount()   // Creditor account
                        .iban("FR7630001007941234567890185") // IBAN
                        .bic("BDFEFRPP")                     // Optional BIC
                        .build())
                .amount("12.34")                                // Amount, converted to BigDecimal
                .currency("EUR")                                // Currency code
                .endToEndId("Transfer reference 1")             // End to end identifier
                .id("Optional identifier 1")                    // Optional Transaction identifier
                .chargeBearer(ChargeBearer.CRED)                // Optional charge bearer code defines who is bearing the charges of the transfer
                .batchBooking(true)                             // Optional batch booking, defaults to false
                .remittanceInformationUnstructured("Your remittance information")   // Unstructured Remittance Information 
                .build();
        Transaction transaction2 = Bank.simpleTransaction()
                .party(Bank.simpleParty()           // Optional creditor identification
                        .name("Creditor Name")               // Optional name
                        .build())
                .account(Bank.simpleBankAccount()   // Creditor account
                        .otherId("1234567890")               // Other identification
                        .bic("BDFEFRPP")                     // BIC
                        .build())
                .amount(new BigDecimal("56.78"))    // Amount as BigDecimal
                .currency("EUR")                    // Currency code
                .endToEndId("Transfer reference 2") // End to end identifier
                .id("Optional identifier 2")        // Optional transaction identifier
                .chargeBearerCode(ChargeBearer.DEBT)   // Optional charge bearer code defines who is bearing the charges of the transfer
                .intermediaryAgent(Bank.simpleBankAccount() // Optional intermediary agent 1
                        .name("BNP PARIBAS")                 // Optional name
                        .otherId("12345")                    // Optional other identification
                        .bic("BNPAFRPP")                     // Optional BIC
                        .build())
                .intermediaryAgent(Bank.simpleBankAccount() // Optional intermediary agent 2
                        .name("BNP PARIBAS")                 // Optional name
                        .otherId("67890")                    // Optional other identification
                        .bic("BNPAFRPP")                     // Optional BIC
                        .build())
                .intermediaryAgent(Bank.simpleBankAccount() // Optional intermediary agent 3
                        .name("BNP PARIBAS")                 // Optional name
                        .otherId("00000")                    // Optional other identification
                        .bic("BNPAFRPP")                     // Optional BIC
                        .build())
                .build();

        // Transfer
        CreditTransferOperation creditTransfer = Bank.jaxbCreditTransferSepa() // defaults to V03
                .instructionPriority(Priority.HIGH)                  // Optional instruction priority
                .debtor(debtor)                                      // Optional debtor
                .debtorAccount(debtorAccount)                        // Mandatory debtor account
                .transaction(transaction1)                           // At least 1 transaction
                .transaction(transaction2)                           // Optional additional transaction
                .creationDateTime(LocalDateTime.now())               // Optional message creation date and time, defaults to now
                .requestedExecutionDate(LocalDate.now().plusDays(1)) // Optional requested execution date, defaults to tomorrow
                .id("MYID")                                          // Optional identifier, defaults to creation date and time as yyyyMMddhhmmss
                .chargeBearer(ChargeBearerType1Code.DEBT)        // Optional charge bearer code defines who is bearing the charges of the transfer 
                .build();

        // export to string
        String formattedOutput = creditTransfer.marshal(true); // true: enables formatting

        // or export to file
        creditTransfer.marshal(new FileWriter("myFile.xml")); // default: disables formatting
    }
}

Output with formatting:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
    <CstmrCdtTrfInitn>
        <GrpHdr>
            <MsgId>MYID</MsgId>
            <CreDtTm>2023-10-31T11:09:49.921</CreDtTm>
            <NbOfTxs>2</NbOfTxs>
            <CtrlSum>69.12</CtrlSum>
            <InitgPty>
                <Nm>Debtor Name</Nm>
            </InitgPty>
        </GrpHdr>
        <PmtInf>
            <PmtInfId>MYID</PmtInfId>
            <PmtMtd>TRF</PmtMtd>
            <BtchBookg>true</BtchBookg>
            <NbOfTxs>2</NbOfTxs>
            <CtrlSum>69.12</CtrlSum>
            <PmtTpInf>
                <InstrPrty>HIGH</InstrPrty>
                <SvcLvl>
                    <Cd>SEPA</Cd>
                </SvcLvl>
            </PmtTpInf>
            <ReqdExctnDt>2023-11-01</ReqdExctnDt>
            <Dbtr>
                <Nm>Debtor Name</Nm>
            </Dbtr>
            <DbtrAcct>
                <Id>
                    <IBAN>FR7610011000201234567890188</IBAN>
                </Id>
            </DbtrAcct>
            <DbtrAgt>
                <FinInstnId>
                    <BIC>PSSTFRPP</BIC>
                </FinInstnId>
            </DbtrAgt>
            <ChrgBr>SLEV</ChrgBr>
            <CdtTrfTxInf>
                <PmtId>
                    <InstrId>Optional identifier 1</InstrId>
                    <EndToEndId>Transfer reference 1</EndToEndId>
                </PmtId>
                <Amt>
                    <InstdAmt Ccy="EUR">12.34</InstdAmt>
                </Amt>
                <CdtrAgt>
                    <FinInstnId>
                        <BIC>BDFEFRPP</BIC>
                    </FinInstnId>
                </CdtrAgt>
                <Cdtr>
                    <Nm>BANQUE DE FRANCE</Nm>
                    <PstlAdr>
                        <Ctry>FR</Ctry>
                        <AdrLine>1, rue de La Vrillière</AdrLine>
                        <AdrLine>75001 PARIS</AdrLine>
                    </PstlAdr>
                </Cdtr>
                <CdtrAcct>
                    <Id>
                        <IBAN>FR7630001007941234567890185</IBAN>
                    </Id>
                </CdtrAcct>
            </CdtTrfTxInf>
            <CdtTrfTxInf>
                <PmtId>
                    <InstrId>Optional identifier 2</InstrId>
                    <EndToEndId>Transfer reference 2</EndToEndId>
                </PmtId>
                <Amt>
                    <InstdAmt Ccy="EUR">56.78</InstdAmt>
                </Amt>
                <IntrmyAgt1>
                    <FinInstnId>
                        <BIC>BNPAFRPP</BIC>
                    </FinInstnId>
                </IntrmyAgt1>
                <IntrmyAgt1Acct>
                    <Id>
                        <Othr>
                            <Id>12345</Id>
                        </Othr>
                    </Id>
                    <Nm>BNP PARIBAS</Nm>
                </IntrmyAgt1Acct>
                <IntrmyAgt2>
                    <FinInstnId>
                        <BIC>BNPAFRPP</BIC>
                    </FinInstnId>
                </IntrmyAgt2>
                <IntrmyAgt2Acct>
                    <Id>
                        <Othr>
                            <Id>67890</Id>
                        </Othr>
                    </Id>
                    <Nm>BNP PARIBAS</Nm>
                </IntrmyAgt2Acct>
                <IntrmyAgt3>
                    <FinInstnId>
                        <BIC>BNPAFRPP</BIC>
                    </FinInstnId>
                </IntrmyAgt3>
                <IntrmyAgt3Acct>
                    <Id>
                        <Othr>
                            <Id>00000</Id>
                        </Othr>
                    </Id>
                    <Nm>BNP PARIBAS</Nm>
                </IntrmyAgt3Acct>
                <CdtrAgt>
                    <FinInstnId>
                        <BIC>BDFEFRPP</BIC>
                    </FinInstnId>
                </CdtrAgt>
                <Cdtr>
                    <Nm>Creditor Name</Nm>
                </Cdtr>
                <CdtrAcct>
                    <Id>
                        <Othr>
                            <Id>1234567890</Id>
                        </Othr>
                    </Id>
                </CdtrAcct>
                <RmtInf>
                    <Ustrd>Your remittance information</Ustrd>
                </RmtInf>
            </CdtTrfTxInf>
        </PmtInf>
    </CstmrCdtTrfInitn>
</Document>

Go further

Using the Iso20022ReferenceElementValidator class

The Iso20022ReferenceElementValidator class provides methods for validating and processing reference elements according to ISO 20022 standards.

By default, these reference fields are validated with an annotation.

Sanitization of reference elements

If you can't anticipate the value for these reference fields and the validation doesn't pass, you can use this class we provide to automatically sanitize your fields using the sanitizeToCharacterSet method :

String input = "/endTo#EndId";
Map<Character, Character> customReplacements = new HashMap<>();
customReplacements.put('#', '-'); // will replace all "#" chars with "-"

Iso20022ReferenceElementValidator.sanitizeToCharacterSet(input, customReplacements);
// Display "endTo-EndId"

Note: the mapping for replacements (customRemplacements) is optional, we offer a default replacement (.) by default.

Input validation

The isValidCharacterSet method checks whether a string respects the valid characters defined by the ISO 20022 standard. It returns a boolean. Here's an example:

Iso20022ReferenceElementValidator.isValidCharacterSet("ABCDEF1234"); // true

Interfaces

You can use your own implementations of Transaction, BankAccount, Party and PostalAddress but simple defaults are provided.

About

Generates ISO 20022 Payment Initiation XML messages and provides IBAN and BIC validation

Topics

Resources

License

Stars

Watchers

Forks

Contributors 4

  •  
  •  
  •  
  •  

Languages