All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kh.gov.nbc.bakong_khqr.model.KHQRValidation Maven / Gradle / Ivy

Go to download

The standardization of KHQR code specifications will help promote wider use of mobile retail payments in Cambodia and provide consistent user experience for merchants and consumers. It can enable interoperability in the payment industry. A common QR code would facilitate payments among different schemes, e-wallets and banks and would encourage small merchants to adopt KHQR code as payment method. KHQR is created for retail or remittance in Cambodia and Cross-Border. It only requires a single QR for receiving transactions from any payment provider through Bakong including Bakong App.

The newest version!
package kh.gov.nbc.bakong_khqr.model;


import kh.gov.nbc.bakong_khqr.exception.KHQRException;
import kh.gov.nbc.bakong_khqr.utils.BakongKHQRUtils;
import kh.gov.nbc.bakong_khqr.utils.NumberUtils;
import kh.gov.nbc.bakong_khqr.utils.StringUtils;
import org.apache.commons.validator.routines.UrlValidator;

import java.math.BigDecimal;
import java.util.regex.Pattern;

public class KHQRValidation {

    private static int TWENTY_FIVE = 25;
    private static int THIRTY_TWO = 32;


    private static boolean isMerchantNameLengthInvalid(String merchantName) {
        return merchantName.length() > TWENTY_FIVE;
    }

    private static boolean isMerchantIdEmpty(String merchantId) {
        return StringUtils.isBlank(merchantId);
    }

    private static boolean isMerchantIdLengthInvalid(String merchantId) {
        return merchantId.length() > THIRTY_TWO;
    }

    private static boolean isAccountInformationLengthInvalid(String accountInformation) {
        return accountInformation.length() > THIRTY_TWO;
    }

    public static boolean isAcquiringBankLengthInvalid(String acquiringBank) {
        return acquiringBank.length() > THIRTY_TWO;
    }

    private static boolean isAmountInvalid(Double amount, KHQRCurrency currency) {
        int AMOUNT_LENGTH = 13;
        if (amount != null) {

            BigDecimal bd = BigDecimal.valueOf(amount);
            String valueAmount = bd.stripTrailingZeros().toPlainString();
            int index = valueAmount.indexOf(".");
            int decimalCount = index < 0 ? 0 : valueAmount.length() - index - 1;

            if (amount < 0 || valueAmount.length() > AMOUNT_LENGTH) {
                return true;
            }
            if (currency == KHQRCurrency.USD) {
                //0.##
                return decimalCount >= 3;

            } else {
                //0
                return decimalCount != 0;
            }
        } else return false;
    }

    private static boolean isStoreLabelLengthInvalid(String storeLabel) {
        return storeLabel != null && storeLabel.length() > TWENTY_FIVE;

    }

    private static boolean isTerminalLabelLengthInvalid(String terminalLabel) {
        return terminalLabel != null && terminalLabel.length() > TWENTY_FIVE;

    }


    private static boolean isBillNumberLengthInvalid(String billNumber) {
        return billNumber != null && billNumber.length() > TWENTY_FIVE;

    }

    private static boolean isMobileNumberLengthInvalid(String mobileNumber) {
        return mobileNumber != null && mobileNumber.length() > TWENTY_FIVE;

    }

    private static boolean isPurposeOfTrxLengthInvalid(String purposeOfTransaction) {
        return purposeOfTransaction != null && purposeOfTransaction.length() > TWENTY_FIVE;

    }

    private static boolean isAccountIdLengthInvalid(String accountId) {
        return accountId.length() > THIRTY_TWO;
    }

    private static boolean isAccountIdInvalidFormat(String accountId) {
        String FORMAT_ACCOUNT_ID = "^[^@]+[@][^@]+$";
        return !Pattern.matches(FORMAT_ACCOUNT_ID, accountId);
    }

    private static void validatePayloadFormatIndicator(String payloadFormatIndicator) throws KHQRException {
        if (StringUtils.isBlank(payloadFormatIndicator)) {
            KHQRException.throwCustomerException(KHQRErrorCode.PAYLOAD_FORMAT_INDICATOR_REQUIRED);
        } else if (payloadFormatIndicator.length() > 2) {
            KHQRException.throwCustomerException(KHQRErrorCode.PAYLOAD_FORMAT_INDICATOR_LENGTH_INVALID);
        }
    }

    private static void validatePointOfInitiationMethod(String pointOfInitiationMethod) throws KHQRException {
        if (StringUtils.isNotBlank(pointOfInitiationMethod) && pointOfInitiationMethod.length() > 2) {
            KHQRException.throwCustomerException(KHQRErrorCode.POINT_OF_INITIATION_METHOD_LENGTH_INVALID);
        }
    }

    private static void validateMerchantType(String merchantType) throws KHQRException {
        if (StringUtils.isBlank(merchantType))
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_TYPE_REQUIRED);
    }

    private static void validateMerchantCategoryCode(String merchantCategoryCode) throws KHQRException {
        if (StringUtils.isBlank(merchantCategoryCode))
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_CATEGORY_CODE_REQUIRED);
        else if (merchantCategoryCode.length() != 4) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_CATEGORY_CODE_LENGTH_INVALID);
        }
    }

    private static void validateTransactionCurrency(String transactionCurrency) throws KHQRException {
        if (StringUtils.isBlank(transactionCurrency)) {
            KHQRException.throwCustomerException(KHQRErrorCode.CURRENCY_TYPE_REQUIRED);
        } else if (transactionCurrency.length() != 3) {
            KHQRException.throwCustomerException(KHQRErrorCode.TRANSACTION_CURRENCY_LENGTH_INVALID);
        }
        if (!KHQRCurrency.USD.getValue().equals(transactionCurrency) && !KHQRCurrency.KHR.getValue().equals(transactionCurrency)) {
            KHQRException.throwCustomerException(KHQRErrorCode.UNSUPPORTED_TRANSACTION_CURRENCY);
        }
    }

    public static boolean isQrValidCrc(String qr) {
        String expectCrc = qr.substring(qr.length() - 4).toUpperCase();
        String qrWithoutCrc = qr.substring(0, qr.length() - 8);
        String crc = BakongKHQRUtils.getCRC(qrWithoutCrc).toUpperCase();
        return expectCrc.equals(crc);
    }

    private static void validateCrc(String crc) throws KHQRException {
        if (StringUtils.isBlank(crc)) {
            KHQRException.throwCustomerException(KHQRErrorCode.CRC_REQUIRED);
        } else if (crc.length() > 4) {
            KHQRException.throwCustomerException(KHQRErrorCode.CRC_LENGTH_INVALID);
        }
    }

    public static boolean isContainCrcTagValue(String qr) {
        String crc = qr.substring(qr.length() - 8).toUpperCase();
        return Pattern.matches("6304[A-F0-9]{4}$", crc);
    }

    private static boolean isMerchantCityLengthInvalid(String merchantCity) {
        int MERCHANT_CITY_LENGTH = 15;
        return merchantCity.length() > MERCHANT_CITY_LENGTH;
    }

    private static boolean isCountryCodeLengthInvalid(String countryCode) {
        int COUNTRY_CODE_LENGTH = 2;
        return countryCode.length() > COUNTRY_CODE_LENGTH;
    }

    public static boolean isUrlInvalid(String url) {
        String[] schemes = {"http", "https"}; // DEFAULT schemes = "http", "https", "ftp"
        UrlValidator urlValidator = new UrlValidator(schemes);
        return !urlValidator.isValid(url);
    }

    public static boolean isEmpty(String text) {
        return text == null || text.isEmpty();
    }

    public static boolean isSourceInfoInvalid(SourceInfo sourceInfo) {
        if (sourceInfo == null) return false;
        String appName = sourceInfo.getAppName();
        String appDeepLinkCallback = sourceInfo.getAppDeepLinkCallback();
        String appIconUrl = sourceInfo.getAppIconUrl();

        return isEmpty(appName) || isEmpty(appDeepLinkCallback) || isEmpty(appIconUrl);
    }

    private static void validateMerchantName(String merchantName) throws KHQRException {
        if (StringUtils.isBlank(merchantName)) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_NAME_REQUIRED);
        } else if (isMerchantNameLengthInvalid(merchantName)) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_NAME_LENGHT_INVALID);
        }
    }


    private static void validateAmount(Double amount, KHQRCurrency currency) throws KHQRException {
        if (isAmountInvalid(amount, currency)) {
            KHQRException.throwCustomerException(KHQRErrorCode.TRANSACTION_AMOUNT_INVALID);
        }
    }

    private static void validateMerchantCity(String merchantCity) throws KHQRException {
        if (isMerchantCityLengthInvalid(merchantCity)) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_CITY_LENGTH_INVALID);
        }
    }

    private static void validateMerchantCityForDecode(String merchantCity) throws KHQRException {
        if (StringUtils.isBlank(merchantCity)) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_CITY_REQUIRED);
        } else if (KHQRValidation.isMerchantCityLengthInvalid(merchantCity)) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_CITY_LENGTH_INVALID);
        }
    }

    public static String getQrType(Double amount) {
        if (amount == null || amount == 0) {
            return KHQRType.STATIC.getType();
        } else return KHQRType.DYNAMIC.getType();
    }

    private static void validateBakongAccountId(String bakongAccountID) throws KHQRException {
        if (StringUtils.isBlank(bakongAccountID)) {
            KHQRException.throwCustomerException(KHQRErrorCode.BAKONG_ACCOUNT_ID_REQUIRED);
        } else if (isAccountIdLengthInvalid(bakongAccountID)) {
            KHQRException.throwCustomerException(KHQRErrorCode.BAKONG_ACCOUNT_ID_LENGTH_INVALID);
        } else if (isAccountIdInvalidFormat(bakongAccountID)) {
            KHQRException.throwCustomerException(KHQRErrorCode.BAKONG_ACCOUNT_ID_INVALID);
        }
    }

    private static void validateCountryCode(String countryCode) throws KHQRException {
        if (StringUtils.isBlank(countryCode)) {
            KHQRException.throwCustomerException(KHQRErrorCode.COUNTRY_CODE_REQUIRED);
        } else if (KHQRValidation.isCountryCodeLengthInvalid(countryCode)) {
            KHQRException.throwCustomerException(KHQRErrorCode.COUNTRY_CODE_LENGTH_INVALID);
        }
    }

    private static void validateTerminalLabel(String terminalLabel) throws KHQRException {
        if (isTerminalLabelLengthInvalid(terminalLabel)) {
            KHQRException.throwCustomerException(KHQRErrorCode.TERMINAL_LABEL_LENGTH_INVALID);
        }
    }

    private static void validateStoreLabel(String storeLabel) throws KHQRException {
        if (isStoreLabelLengthInvalid(storeLabel)) {
            KHQRException.throwCustomerException(KHQRErrorCode.STORE_LABEL_LENGTH_INVALID);
        }
    }

    private static void validateBillNumber(String billNumber) throws KHQRException {
        if (isBillNumberLengthInvalid(billNumber)) {
            KHQRException.throwCustomerException(KHQRErrorCode.BILL_NUMBER_LENGTH_INVALID);
        }
    }


    private static void validateMobileNumber(String mobileNumber) throws KHQRException {
        if (isMobileNumberLengthInvalid(mobileNumber)) {
            KHQRException.throwCustomerException(KHQRErrorCode.MOBILE_NUMBER_LENGTH_INVALID);
        }
    }

    private static void validatePurposeOfTransaction(String purposeOfTransaction) throws KHQRException {
        if (StringUtils.isNoneBlank(purposeOfTransaction)) {
            if (isPurposeOfTrxLengthInvalid(purposeOfTransaction)) {
                KHQRException.throwCustomerException(KHQRErrorCode.PURPOSE_OF_TRANSACTION_LENGTH_INVALID);
            }
        }
    }

    private static void validateMerchantId(String merchantId) throws KHQRException {
        if (isMerchantIdEmpty(merchantId)) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_ID_REQUIRED);
        } else if (isMerchantIdLengthInvalid(merchantId)) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_ID_LENGTH_INVALID);
        }
    }

    private static void validateAccountInformation(String accountInformation) throws KHQRException {
        if (accountInformation != null && isAccountInformationLengthInvalid(accountInformation)) {
            KHQRException.throwCustomerException(KHQRErrorCode.ACCOUNT_INFORMATION_LENGTH_INVALID);
        }
    }

    private static void validateAcquiringBankForMerchant(String acquiringBank) throws KHQRException {
        if (isMerchantIdEmpty(acquiringBank)) {
            KHQRException.throwCustomerException(KHQRErrorCode.ACQUIRING_BANK_REQUIRED);
        } else if (isMerchantIdLengthInvalid(acquiringBank)) {
            KHQRException.throwCustomerException(KHQRErrorCode.ACQUIRING_BANK_LENGTH_INVALID);
        }
    }

    private static void validateAcquiringBankForIndividual(String acquiringBank) throws KHQRException {
        if (acquiringBank != null && isMerchantIdLengthInvalid(acquiringBank)) {
            KHQRException.throwCustomerException(KHQRErrorCode.ACQUIRING_BANK_LENGTH_INVALID);
        }
    }

    private static void validateMerchantInfoLanguageTemplate(String languagePreference, String merchantNameAlternateLanguage, String merchantCityAlternateLanguage) throws KHQRException {
        if (StringUtils.isNoneBlank(merchantCityAlternateLanguage) && StringUtils.isNoneBlank(merchantNameAlternateLanguage) && isEmpty(languagePreference) ||
                StringUtils.isNoneBlank(merchantCityAlternateLanguage) && StringUtils.isBlank(merchantNameAlternateLanguage) && StringUtils.isBlank(languagePreference)) {
            KHQRException.throwCustomerException(KHQRErrorCode.LANGUAGE_PREFERENCE_REQUIRED);
        } else if (StringUtils.isNoneBlank(merchantCityAlternateLanguage) && StringUtils.isNoneBlank(languagePreference) && StringUtils.isBlank(merchantNameAlternateLanguage)) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_NAME_ALTERNATE_LANGUAGE_REQUIRED);
        }
    }

    private static void validateLanguagePreference(String languagePreference) throws KHQRException {
        if (languagePreference != null && languagePreference.length() != 2) {
            KHQRException.throwCustomerException(KHQRErrorCode.LANGUAGE_PREFERENCE_LENGTH_INVALID);
        }
    }

    private static void validateMerchantNameAlternateLanguage(String merchantNameAlternateLanguage) throws KHQRException {
        if (merchantNameAlternateLanguage != null && merchantNameAlternateLanguage.length() > TWENTY_FIVE) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_NAME_ALTERNATE_LANGUAGE_LENGTH_INVALID);
        }
    }


    private static void validateMerchantCityAlternateLanguage(String merchantCityAlternateLanguage) throws KHQRException {
        if (merchantCityAlternateLanguage != null && merchantCityAlternateLanguage.length() > 15) {
            KHQRException.throwCustomerException(KHQRErrorCode.MERCHANT_CITY_ALTERNATE_LANGUAGE_LENGTH_INVALID);
        }
    }

    private static void validateUpiAccountInformationForGenerate(String upiAccountInformation, KHQRCurrency currency) throws KHQRException {
        if (currency == KHQRCurrency.USD && StringUtils.isNoneBlank(upiAccountInformation)) {
            KHQRException.throwCustomerException(KHQRErrorCode.KHQR_NOT_SUPPORT_UPI_WITH_USD);
        } else if (upiAccountInformation != null && upiAccountInformation.length() > 99) {
            KHQRException.throwCustomerException(KHQRErrorCode.UPI_ACCOUNT_INFORMATION_LENGTH_INVALID);
        }
    }

    private static void validateUpiAccountInformationForDecode(String upiAccountInformation, KHQRCurrency currency, String countryCode) throws KHQRException {
        if (countryCode.equals("KH")) {
            if (upiAccountInformation != null && upiAccountInformation.length() > 99)
                KHQRException.throwCustomerException(KHQRErrorCode.UPI_ACCOUNT_INFORMATION_LENGTH_INVALID);
            return;
        }
        if (currency == KHQRCurrency.USD && StringUtils.isNoneBlank(upiAccountInformation)) {
            KHQRException.throwCustomerException(KHQRErrorCode.KHQR_NOT_SUPPORT_UPI_WITH_USD);
        } else if (upiAccountInformation != null && upiAccountInformation.length() > 99) {
            KHQRException.throwCustomerException(KHQRErrorCode.UPI_ACCOUNT_INFORMATION_LENGTH_INVALID);
        }
    }

    public static void validate(MerchantInfo merchantInfo) throws KHQRException {
        validateUpiAccountInformationForGenerate(merchantInfo.getUpiAccountInformation(), merchantInfo.getCurrency());
        validateBakongAccountId(merchantInfo.getBakongAccountId());
        validateAmount(merchantInfo.getAmount(), merchantInfo.getCurrency());
        validateMerchantId(merchantInfo.getMerchantId());
        validateAcquiringBankForMerchant(merchantInfo.getAcquiringBank());
        validateMerchantName(merchantInfo.getMerchantName());
        validateMerchantCity(merchantInfo.getMerchantCity());
        validateTerminalLabel(merchantInfo.getTerminalLabel());
        validateStoreLabel(merchantInfo.getStoreLabel());
        validateBillNumber(merchantInfo.getBillNumber());
        validateMobileNumber(merchantInfo.getMobileNumber());
        validatePurposeOfTransaction(merchantInfo.getPurposeOfTransaction());
        validateMerchantInfoLanguageTemplate(merchantInfo.getMerchantAlternateLanguagePreference(), merchantInfo.getMerchantNameAlternateLanguage(), merchantInfo.getMerchantCityAlternateLanguage());
        validateLanguagePreference(merchantInfo.getMerchantAlternateLanguagePreference());
        validateMerchantNameAlternateLanguage(merchantInfo.getMerchantNameAlternateLanguage());
        validateMerchantCityAlternateLanguage(merchantInfo.getMerchantCityAlternateLanguage());
    }

    public static void validate(IndividualInfo individualInfo) throws KHQRException {
        validateUpiAccountInformationForGenerate(individualInfo.getUpiAccountInformation(), individualInfo.getCurrency());
        validateBakongAccountId(individualInfo.getBakongAccountId());
        validateAmount(individualInfo.getAmount(), individualInfo.getCurrency());
        validateMerchantName(individualInfo.getMerchantName());
        validateMerchantCity(individualInfo.getMerchantCity());
        validateAccountInformation(individualInfo.getAccountInformation());
        validateAcquiringBankForIndividual(individualInfo.getAcquiringBank());
        validateTerminalLabel(individualInfo.getTerminalLabel());
        validateStoreLabel(individualInfo.getStoreLabel());
        validateBillNumber(individualInfo.getBillNumber());
        validateMobileNumber(individualInfo.getMobileNumber());
        validatePurposeOfTransaction(individualInfo.getPurposeOfTransaction());
        validateMerchantInfoLanguageTemplate(individualInfo.getMerchantAlternateLanguagePreference(), individualInfo.getMerchantNameAlternateLanguage(), individualInfo.getMerchantCityAlternateLanguage());
        validateLanguagePreference(individualInfo.getMerchantAlternateLanguagePreference());
        validateMerchantNameAlternateLanguage(individualInfo.getMerchantNameAlternateLanguage());
        validateMerchantCityAlternateLanguage(individualInfo.getMerchantCityAlternateLanguage());
    }

    public static void validate(KHQRDecodeData decodeData) throws KHQRException {
        validateCrc(decodeData.getCrc());
        validatePayloadFormatIndicator(decodeData.getPayloadFormatIndicator());
        validatePointOfInitiationMethod(decodeData.getPointOfInitiationMethod());
        validateMerchantType(decodeData.getMerchantType());
        validateBakongAccountId(decodeData.getBakongAccountID());
        if (decodeData.getMerchantType() != null && decodeData.getMerchantType().equals(KHQRMerchantType.MERCHANT.getType())) {
            validateMerchantId(decodeData.getMerchantId());
        }
        validateAccountInformation(decodeData.getAccountInformation());
        validateAcquiringBankForIndividual(decodeData.getAcquiringBank());
        validateMerchantCategoryCode(decodeData.getMerchantCategoryCode());
        validateTransactionCurrency(decodeData.getTransactionCurrency());
        String amount = decodeData.getTransactionAmount();

        KHQRCurrency currency = null;
        if (decodeData.getTransactionCurrency().equals(KHQRCurrency.KHR.getValue())) {
            currency = KHQRCurrency.KHR;
        } else if (decodeData.getTransactionCurrency().equals(KHQRCurrency.USD.getValue())) {
            currency = KHQRCurrency.USD;
        }
        if (amount != null && !NumberUtils.isCreatable(amount) || amount != null && currency != null && isAmountInvalid(Double.parseDouble(amount), currency)) {
            KHQRException.throwCustomerException(KHQRErrorCode.TRANSACTION_AMOUNT_INVALID);
        }

        validateCountryCode(decodeData.getCountryCode());
        validateMerchantName(decodeData.getMerchantName());
        validateMerchantCityForDecode(decodeData.getMerchantCity());
        validateBillNumber(decodeData.getBillNumber());
        validateMobileNumber(decodeData.getMobileNumber());
        validateStoreLabel(decodeData.getStoreLabel());
        validateTerminalLabel(decodeData.getTerminalLabel());
        validateUpiAccountInformationForDecode(decodeData.getUpiAccountInformation(), currency, decodeData.getCountryCode());

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy