Skip to content

axelbergh/emv-qrcode

 
 

Repository files navigation

EMV QRCode

Java CI with Maven Quality Gate Status Coverage Maven Central Hex.pm

Java Based EMV QR Code Generator and Parser (MPM, CPM)

  • This library supports BRCode aswell

Specification

1. Quick Start

This chapter will show you how to get started with EMV QR Code.

1.1 Prerequisite

In order to use EMV QR Code within a Maven project, simply add the following dependency to your pom.xml. There are no other dependencies for EMV QR Code, which means other unwanted libraries will not overwhelm your project.

You can pull it from the central Maven repositories:

<dependency>
    <groupId>com.github.mvallim</groupId>
    <artifactId>emv-qrcode</artifactId>
    <version>0.0.7</version>
</dependency>

If you want to try a snapshot version, add the following repository:

<repository>
    <id>sonatype-snapshots</id>
    <name>Sonatype Snapshots</name>
    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    <snapshots>
        <enabled>true</enabled>
    </snapshots>
</repository>

MPM (Merchant Presented Mode) Encode

final AdditionalDataFieldTemplate additionalDataField = getAddtionalDataField();
final MerchantAccountInformationTemplate merchantAccountInformation = getMerchanAccountInformation();
final MerchantInformationLanguageTemplate merchantInformationLanguage = getMerchantInformationLanguage();
final UnreservedTemplate unreserved = getUnreserved();
final TagLengthString rFUforEMVCo = new TagLengthString("65", "00");

final MerchantPresentedMode merchantPresentMode = new MerchantPresentedMode();
merchantPresentMode.setAdditionalDataField(additionalDataField);
merchantPresentMode.setCountryCode("CN");
merchantPresentMode.setMerchantCategoryCode("4111");
merchantPresentMode.setMerchantCity("BEIJING");
merchantPresentMode.setMerchantInformationLanguage(merchantInformationLanguage);
merchantPresentMode.setMerchantName("BEST TRANSPORT");
merchantPresentMode.setPayloadFormatIndicator("01");
merchantPresentMode.setPointOfInitiationMethod("11");
merchantPresentMode.setPostalCode("1234567");
merchantPresentMode.setTipOrConvenienceIndicator("02");
merchantPresentMode.setTransactionAmount("23.72");
merchantPresentMode.setTransactionCurrency("156");
merchantPresentMode.setValueOfConvenienceFeeFixed("500");
merchantPresentMode.addMerchantAccountInformation(merchantAccountInformation);
merchantPresentMode.addRFUforEMVCo(rFUforEMVCo);
merchantPresentMode.addUnreserved(unreserved);

System.out.println(merchantPresentMode.toString());
    //00020101021102160004hoge0104abcd520441115303156540523.7255020256035005802CN5
    //914BEST TRANSPORT6007BEIJING6107123456762800205678900305098760505abcde0705klm
    //no0805pqres0903tuv1004abcd5016000412340104ijkl64280002ZH0102北京0204最佳运输030
    //4abcd65020080320016A01122334499887707081234567863046325

private MerchantAccountInformationTemplate getMerchanAccountInformation() {

    final TagLengthString paymentNetworkSpecific = new TagLengthString();
    paymentNetworkSpecific.setTag("01");
    paymentNetworkSpecific.setValue("abcd");

    final MerchantAccountInformation merchantAccountInformationValue = new MerchantAccountInformation();
    merchantAccountInformationValue.setGloballyUniqueIdentifier("hoge");
    merchantAccountInformationValue.addPaymentNetworkSpecific(paymentNetworkSpecific);

    final MerchantAccountInformationTemplate merchantAccountInformation = new MerchantAccountInformationTemplate();
    merchantAccountInformation.setValue(merchantAccountInformationValue);
    merchantAccountInformation.setTag("02");

    return merchantAccountInformation;
}

private UnreservedTemplate getUnreserved() {

    final TagLengthString contextSpecificData = new TagLengthString();
    contextSpecificData.setTag("07");
    contextSpecificData.setValue("12345678");

    final Unreserved value = new Unreserved();
    value.setGloballyUniqueIdentifier("A011223344998877");
    value.addContextSpecificData(contextSpecificData);

    final UnreservedTemplate unreserved = new UnreservedTemplate();
    unreserved.setValue(value);
    unreserved.setTag("80");

    return unreserved;
}

private MerchantInformationLanguageTemplate getMerchantInformationLanguage() {

    final TagLengthString rFUforEMVCo = new TagLengthString();
    rFUforEMVCo.setTag("03");
    rFUforEMVCo.setValue("abcd");

    final MerchantInformationLanguage merchantInformationLanguageValue = new MerchantInformationLanguage();
    merchantInformationLanguageValue.setLanguagePreference("ZH");
    merchantInformationLanguageValue.setMerchantName("北京");
    merchantInformationLanguageValue.setMerchantCity("最佳运输");
    merchantInformationLanguageValue.addRFUforEMVCo(rFUforEMVCo);

    final MerchantInformationLanguageTemplate merchantInformationLanguage = new MerchantInformationLanguageTemplate();
    merchantInformationLanguage.setValue(merchantInformationLanguageValue);

    return merchantInformationLanguage;
}

private AdditionalDataFieldTemplate getAddtionalDataField() {

    final PaymentSystemSpecific paymentSystemSpecific = new PaymentSystemSpecific();
    paymentSystemSpecific.setGloballyUniqueIdentifier("1234");
    paymentSystemSpecific.addPaymentSystemSpecific(new TagLengthString("01", "ijkl"));

    final PaymentSystemSpecificTemplate paymentSystemSpecificTemplate = new PaymentSystemSpecificTemplate();
    paymentSystemSpecificTemplate.setTag("50");
    paymentSystemSpecificTemplate.setValue(paymentSystemSpecific);

    final TagLengthString rFUforEMVCo = new TagLengthString();
    rFUforEMVCo.setTag("10");
    rFUforEMVCo.setValue("abcd");

    final AdditionalDataField additionalDataFieldValue = new AdditionalDataField();
    additionalDataFieldValue.setAdditionalConsumerDataRequest("tuv");
    additionalDataFieldValue.setMobileNumber("67890");
    additionalDataFieldValue.setPurposeTransaction("pqres");
    additionalDataFieldValue.setReferenceLabel("abcde");
    additionalDataFieldValue.setStoreLabel("09876");
    additionalDataFieldValue.setTerminalLabel("klmno");
    additionalDataFieldValue.addPaymentSystemSpecific(paymentSystemSpecificTemplate);
    additionalDataFieldValue.addRFUforEMVCo(rFUforEMVCo);

    final AdditionalDataFieldTemplate additionalDataField = new AdditionalDataFieldTemplate();
    additionalDataField.setValue(additionalDataFieldValue);

    return additionalDataField;
}

MPM (Merchant Presented Mode) Decode

@Test
public void testSuccessDecode() {

    final String encoded = "00020101021102160004hoge0104abcd520441115303156540523.7255020256035005802CN5"
        + "914BEST TRANSPORT6007BEIJING6107123456762800205678900305098760505abcde0705klmno0805pqres0903t"
        + "uv1004abcd5016000412340104ijkl64280002ZH0102北京0204最佳运输0304abcd65020080320016A011223344998"
        + "87707081234567863046325";

    final MerchantPresentedMode merchantPresentedMode = Decoder.decode(encoded, MerchantPresentedMode.class);

    assertThat(merchantPresentedMode.getCountryCode().getValue(), equalTo("CN"));
    assertThat(merchantPresentedMode.getMerchantCategoryCode().getValue(), equalTo("4111"));
    assertThat(merchantPresentedMode.getMerchantCity().getValue(), equalTo("BEIJING"));
    assertThat(merchantPresentedMode.getMerchantName().getValue(), equalTo("BEST TRANSPORT"));
    assertThat(merchantPresentedMode.getPayloadFormatIndicator().getValue(), equalTo("01"));
    assertThat(merchantPresentedMode.getPointOfInitiationMethod().getValue(), equalTo("11"));
    assertThat(merchantPresentedMode.getPostalCode().getValue(), equalTo("1234567"));
    assertThat(merchantPresentedMode.getTipOrConvenienceIndicator().getValue(), equalTo("02"));
    assertThat(merchantPresentedMode.getTransactionAmount().getValue(), equalTo("23.72"));
    assertThat(merchantPresentedMode.getTransactionCurrency().getValue(), equalTo("156"));
    assertThat(merchantPresentedMode.getValueOfConvenienceFeeFixed().getValue(), equalTo("500"));
}

MPM (Merchant Presented Mode) Validate

@Test
public void testSuccessValidate() {

    final MerchantPresentedMode merchantPresentMode = new MerchantPresentedMode();

    merchantPresentMode.setCountryCode("CN");
    merchantPresentMode.setMerchantCategoryCode("4111");
    merchantPresentMode.setMerchantCity("BEIJING");
    merchantPresentMode.setMerchantName("BEST TRANSPORT");
    merchantPresentMode.setPayloadFormatIndicator("01");
    merchantPresentMode.setPointOfInitiationMethod("11");
    merchantPresentMode.setPostalCode("1234567");
    merchantPresentMode.setTipOrConvenienceIndicator("02");
    merchantPresentMode.setTransactionAmount("23.72");
    merchantPresentMode.setTransactionCurrency("156");
    merchantPresentMode.setValueOfConvenienceFeeFixed("500");

    final ValidationResult validationResult = MerchantPresentedModeValidate.validate(merchantPresentMode);

    assertTrue(validationResult.isValid());
}

MPM (Merchant Presented Mode) Validate CRC16

@Test
public void testSuccessCrc16Sample1() {

    final String encoded = "00020101021229300012D156000000000510A93FO3230Q31280012D156000000010308123456"
        + "78520441115802CN5914BEST TRANSPORT6007BEIJING64200002ZH0104最佳运输0202北京540523.7253031565502"
        + "016233030412340603***0708A60086670902ME91320016A0112233449988770708123456786304A13A";

    final ValidationResult validationResult = Crc16Validate.validate(encoded);

    assertThat(validationResult.isValid(), equalTo(true));
}

@Test
public void testFailValidateWhenWithoutCRCDecoded() {

    final String encoded = "00020101021102160004hoge0104abcd520441115303156540523.7255020256035005802CN5"
        + "914BEST TRANSPORT6007BEIJING6107123456762800205678900305098760505abcde0705klmno0805pqres0903t"
        + "uv1004abcd5016000412340104ijkl64280002ZH0102北京0204最佳运输0304abcd65020080320016A011223344998"
        + "877070812345678";

    final ValidationResult validationResult = Crc16Validate.validate(encoded);

    assertThat(validationResult.isValid(), equalTo(false));
    assertThat(validationResult.getErrors(), hasSize(1));
    assertThat(validationResult.getErrors(), hasItem(hasProperty("message", equalTo("Invalid CRC16"))));
    assertThat(validationResult.getErrors(), hasItem(hasProperty("attemptedValue", equalTo("5678"))));
}

CPM (Consumer Presented Mode)

// Coming soon

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Versioning

We use GitHub for versioning. For the versions available, see the tags on this repository.

Authors

  • Marcos Vallim - Initial work, Development, Test, Documentation - mvallim

See also the list of contributors who participated in this project.

License

This project is licensed under the Apache License - see the LICENSE file for details

About

Java Based EMV QR Code Generator and Parser (MPM, CPM)

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 100.0%