ru.tinkoff.acquiring.sdk.EnterCardFragment.java Source code

Java tutorial

Introduction

Here is the source code for ru.tinkoff.acquiring.sdk.EnterCardFragment.java

Source

/*
 * Copyright  2016 Tinkoff Bank
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ru.tinkoff.acquiring.sdk;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.transition.TransitionManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.text.TextUtils;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.wallet.AutoResolveHelper;
import com.google.android.gms.wallet.CardRequirements;
import com.google.android.gms.wallet.IsReadyToPayRequest;
import com.google.android.gms.wallet.PaymentData;
import com.google.android.gms.wallet.PaymentDataRequest;
import com.google.android.gms.wallet.PaymentMethodTokenizationParameters;
import com.google.android.gms.wallet.PaymentsClient;
import com.google.android.gms.wallet.TransactionInfo;
import com.google.android.gms.wallet.Wallet;
import com.google.android.gms.wallet.WalletConstants;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

import ru.tinkoff.acquiring.sdk.inflate.pay.PayCellInflater;
import ru.tinkoff.acquiring.sdk.inflate.pay.PayCellType;
import ru.tinkoff.acquiring.sdk.requests.InitRequestBuilder;
import ru.tinkoff.acquiring.sdk.utils.Base64;
import ru.tinkoff.acquiring.sdk.views.BankKeyboard;
import ru.tinkoff.acquiring.sdk.views.EditCardView;

import static android.widget.Toast.makeText;
import static com.google.android.gms.wallet.fragment.WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_DARK;
import static com.google.android.gms.wallet.fragment.WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_LIGHT;
import static com.google.android.gms.wallet.fragment.WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_LIGHT_WITH_BORDER;

/**
 * @author a.shishkin1
 */
public class EnterCardFragment extends Fragment
        implements ICardInterest, IChargeRejectPerformer, OnBackPressedListener {

    public static final int LOAD_PAYMENT_DATA_REQUEST_CODE = 5;

    private static final int PAY_FORM_MAX_LENGTH = 20;

    private static final int AMOUNT_POSITION_INDEX = 0;
    private static final int BUTTON_POSITION_INDEX = 1;
    private static final int PAY_WITH_AMOUNT_FORMAT_INDEX = 2;
    private static final int MONEY_AMOUNT_FORMAT_INDEX = 3;

    private static final int AMOUNT_POSITION_OVER_FIELDS = 0;

    private static final int BUTTON_UNDER_FIELDS_ICONS_ON_BOTTOM = 0;
    private static final int ICONS_ON_BOTTOM_BUTTON_UNDER_ICONS = 1;
    private static final int ICONS_UNDER_FIELDS_BUTTON_ON_BOTTOM = 2;
    private static final int ICONS_UNDER_FIELDS_BUTTON_UNDER_ICONS = 3;
    private static final int BUTTON_UNDER_FIELDS_ICONS_UNDER_BOTTOM = 4;

    private static final String RECURRING_TYPE_KEY = "recurringType";
    private static final String RECURRING_TYPE_VALUE = "12";
    private static final String FAIL_MAPI_SESSION_ID = "failMapiSessionId";

    @Nullable
    private TextView tvTitle;
    @Nullable
    private TextView tvDescription;
    @Nullable
    private EditText etEmail;
    private EditCardView ecvCard;
    private TextView tvAmount;
    private TextView tvSrcCardLabel;
    private TextView tvChooseCardButton;
    private Button btnPay;
    private View srcCardChooser;
    private View btnGooglePay;

    private GoogleApiClient googleApiClient;

    private AcquiringSdk sdk;
    private FullCardScanner cardScanner;

    private Pattern emailPattern = Patterns.EMAIL_ADDRESS;

    private BankKeyboard customKeyboard;

    private GooglePayParams googlePayParams;

    private RejectAction rejectAction;

    private boolean chargeMode;
    private int amountPositionMode;
    private int buttonAndIconsPositionMode;
    private String payAmountFormat;
    private String moneyAmountFormat;
    private PaymentInfo rejectedPaymentInfo;

    private PaymentsClient paymentsClient;

    public static EnterCardFragment newInstance(boolean chargeMode) {
        Bundle args = new Bundle();
        args.putBoolean(PayFormActivity.EXTRA_CHARGE_MODE, chargeMode);
        EnterCardFragment fragment = new EnterCardFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(new int[] { R.attr.acqPayAmountPosition,
                R.attr.acqPayButtonAndIconPosition, R.attr.acqPayWithAmountFormat, R.attr.acqMoneyAmountFormat });
        amountPositionMode = typedArray.getInt(AMOUNT_POSITION_INDEX, AMOUNT_POSITION_OVER_FIELDS);
        buttonAndIconsPositionMode = typedArray.getInt(BUTTON_POSITION_INDEX, BUTTON_UNDER_FIELDS_ICONS_ON_BOTTOM);
        payAmountFormat = typedArray.getString(PAY_WITH_AMOUNT_FORMAT_INDEX);
        moneyAmountFormat = typedArray.getString(MONEY_AMOUNT_FORMAT_INDEX);
        typedArray.recycle();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        int[] intTypes = getActivity().getIntent().getIntArrayExtra(PayFormActivity.EXTRA_DESIGN_CONFIGURATION);
        PayCellType[] cellTypes = PayCellType.toPayCellTypeArray(intTypes);
        View view = PayCellInflater.from(inflater, cellTypes).inflate(container);

        ecvCard = view.findViewById(R.id.ecv_card);

        tvSrcCardLabel = view.findViewById(R.id.tv_src_card_label);
        tvDescription = view.findViewById(R.id.tv_description);
        tvTitle = view.findViewById(R.id.tv_title);
        tvAmount = view.findViewById(R.id.tv_amount);
        tvChooseCardButton = view.findViewById(R.id.tv_src_card_choose_btn);
        btnPay = view.findViewById(R.id.btn_pay);
        btnGooglePay = view.findViewById(R.id.rl_google_play_button);
        if (btnGooglePay != null) {
            btnGooglePay.setEnabled(false);
        }
        srcCardChooser = view.findViewById(R.id.ll_src_card_chooser);

        etEmail = view.findViewById(R.id.et_email);

        final FragmentActivity activity = getActivity();
        cardScanner = new FullCardScanner(this, (ICameraCardScanner) activity.getIntent()
                .getSerializableExtra(PayFormActivity.EXTRA_CAMERA_CARD_SCANNER));
        ecvCard.setCardSystemIconsHolder(new ThemeCardLogoCache(activity));
        ecvCard.setActions(cardScanner);
        if (!cardScanner.isScanEnable()) {
            ecvCard.setBtnScanIcon(View.NO_ID);
        }

        customKeyboard = view.findViewById(R.id.acq_keyboard);

        googlePayParams = activity.getIntent().getParcelableExtra(PayFormActivity.EXTRA_ANDROID_PAY_PARAMS);
        initGooglePayButton();

        boolean isUsingCustomKeyboard = ((PayFormActivity) activity).shouldUseCustomKeyboard();
        if (isUsingCustomKeyboard) {

            // disable soft keyboard while custom keyboard is not attached to edit card view
            Window window = getActivity().getWindow();
            window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);

            ecvCard.disableCopyPaste();
        } else {
            customKeyboard.hide();
        }

        tvChooseCardButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Card[] cards = ((PayFormActivity) activity).getCards();
                if (cards != null) {
                    startChooseCard();
                }
            }
        });

        srcCardChooser.setVisibility(View.GONE);

        chargeMode = getArguments().getBoolean(PayFormActivity.EXTRA_CHARGE_MODE);
        if (chargeMode) {
            setRecurrentModeForCardView(true);
            ecvCard.setRecurrentPaymentMode(true);
        }

        if (amountPositionMode != AMOUNT_POSITION_OVER_FIELDS) {
            View amountLayout = view.findViewById(R.id.ll_price_layout);
            if (amountLayout != null) {
                amountLayout.setVisibility(View.GONE);
            }
        }

        resolveButtonAndIconsPosition(view);

        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        sdk = ((PayFormActivity) getActivity()).getSdk();

        final Intent intent = getActivity().getIntent();

        final String email = intent.getStringExtra(PayFormActivity.EXTRA_E_MAIL);
        if (email != null && etEmail != null) {
            etEmail.setText(email);
        }

        final String title = intent.getStringExtra(PayFormActivity.EXTRA_TITLE);
        if (tvTitle != null) {
            tvTitle.setText(title);
        }

        String description = intent.getStringExtra(PayFormActivity.EXTRA_DESCRIPTION);
        if (tvDescription != null) {
            tvDescription.setText(description);
        }

        final Money amount = (Money) intent.getSerializableExtra(PayFormActivity.EXTRA_AMOUNT);

        String amountTextWithRubbles = amount != null ? amount.toHumanReadableString() : "";
        String amountText = amount != null ? amount.toString() : "";

        if (amountPositionMode == AMOUNT_POSITION_OVER_FIELDS && tvAmount != null) {
            if (TextUtils.isEmpty(moneyAmountFormat)) {
                tvAmount.setText(amountTextWithRubbles);
            } else {
                tvAmount.setText(String.format(moneyAmountFormat, amountText));
            }
        } else if (TextUtils.isEmpty(payAmountFormat)) {
            String text = btnPay.getText().toString();
            btnPay.setText(text + " " + amountTextWithRubbles);
        } else {
            btnPay.setText(String.format(payAmountFormat, amountText));
        }

        btnPay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (customKeyboard != null) {
                    customKeyboard.hide();
                }

                final String enteredEmail = getEmail();
                if (!validateInput(ecvCard, enteredEmail)) {
                    return;
                }
                final PayFormActivity activity = (PayFormActivity) getActivity();
                final CardData cardData = getCardData(activity);
                activity.showProgressDialog();

                InitRequestBuilder requestBuilder = createInitRequestBuilder(intent);
                initPayment(sdk, requestBuilder, cardData, null, enteredEmail, chargeMode);
            }
        });

        if (btnGooglePay != null) {
            btnGooglePay.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    PaymentDataRequest request = createPaymentDataRequest();
                    if (request != null) {
                        AutoResolveHelper.resolveTask(paymentsClient.loadPaymentData(request), getActivity(),
                                LOAD_PAYMENT_DATA_REQUEST_CODE);
                    }
                }
            });
        }
        if (rejectAction != null) {
            rejectAction.onPerformReject();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        boolean isUsingCustomKeyboard = ((PayFormActivity) getActivity()).shouldUseCustomKeyboard();
        if (customKeyboard != null && isUsingCustomKeyboard) {
            customKeyboard.attachToView(ecvCard);

            Window window = getActivity().getWindow();
            window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        hideSoftKeyboard();
    }

    @Override
    public void onStart() {
        super.onStart();

        PayFormActivity activity = (PayFormActivity) getActivity();
        if (activity == null && googlePayParams != null) {
            initGoogleApiClient();
            initGooglePay();
            return;
        }

        Card[] cards = activity.getCards();
        if (cards != null) {
            onCardReady();
        }

        if (googlePayParams != null) {
            initGoogleApiClient();
            initGooglePay();
        } else {
            hideGooglePayButton();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (googleApiClient != null && googleApiClient.isConnected()) {
            googleApiClient.disconnect();
        }
    }

    private void initGoogleApiClient() {
        Wallet.WalletOptions options = new Wallet.WalletOptions.Builder()
                .setEnvironment(googlePayParams.getEnvironment()).setTheme(googlePayParams.getTheme()).build();

        googleApiClient = new GoogleApiClient.Builder(getActivity()).addApi(Wallet.API, options).build();
        googleApiClient.connect();
    }

    private void initGooglePayButton() {
        if (googlePayParams != null) {
            ImageView gPayImage = btnGooglePay.findViewById(R.id.rl_google_play_image);
            ImageView gPayOverlay = btnGooglePay.findViewById(R.id.rl_google_play_overlay);
            switch (googlePayParams.getBuyButtonAppearance()) {
            case ANDROID_PAY_DARK: {
                btnGooglePay.setBackgroundResource(R.drawable.gpay_button_no_shadow_background_dark);
                gPayImage.setImageResource(R.drawable.gpay_button_content_dark);
                gPayOverlay.setImageResource(R.drawable.gpay_button_overlay_dark);
                break;
            }
            case ANDROID_PAY_LIGHT_WITH_BORDER: {
                btnGooglePay.setBackgroundResource(R.drawable.gpay_button_background);
                gPayImage.setImageResource(R.drawable.gpay_button_content);
                gPayOverlay.setImageResource(R.drawable.gpay_button_overlay);
                break;
            }
            default: {
                btnGooglePay.setBackgroundResource(R.drawable.gpay_button_no_shadow_background);
                gPayImage.setImageResource(R.drawable.gpay_button_content);
                gPayOverlay.setImageResource(R.drawable.gpay_button_overlay);
            }
            }
        }
    }

    private void initGooglePay() {
        IsReadyToPayRequest isReadyTpPayRequest = IsReadyToPayRequest.newBuilder()
                .addAllowedCardNetwork(WalletConstants.CARD_NETWORK_VISA)
                .addAllowedCardNetwork(WalletConstants.CARD_NETWORK_MASTERCARD)
                .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD)
                .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD).build();

        Wallet.WalletOptions options = new Wallet.WalletOptions.Builder()
                .setEnvironment(googlePayParams.getEnvironment()).setTheme(googlePayParams.getTheme()).build();

        paymentsClient = Wallet.getPaymentsClient(getActivity(), options);
        paymentsClient.isReadyToPay(isReadyTpPayRequest).addOnCompleteListener(new OnCompleteListener<Boolean>() {
            @Override
            public void onComplete(@NonNull Task<Boolean> task) {
                try {
                    boolean result = task.getResult(ApiException.class);
                    if (result) {
                        showGooglePayButton();
                    } else {
                        hideGooglePayButton();
                    }
                } catch (ApiException e) {
                    hideGooglePayButton();
                }
            }
        });
    }

    private void showGooglePayButton() {
        if (btnGooglePay != null) {
            btnGooglePay.setEnabled(true);
        }
    }

    private void hideGooglePayButton() {
        View view = getView();
        if (view != null) {
            View container = view.findViewById(R.id.fl_android_pay_placeholder);
            TransitionManager.beginDelayedTransition((ViewGroup) view.findViewById(R.id.pay_buttons_layout));
            container.setVisibility(View.GONE);
        }
    }

    private PaymentDataRequest createPaymentDataRequest() {
        Intent intent = getActivity().getIntent();

        String description = intent.getStringExtra(PayFormActivity.EXTRA_DESCRIPTION);
        String price = getFormattedPrice();

        TransactionInfo transactionInfo = TransactionInfo.newBuilder()
                .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL).setTotalPrice(price)
                .setCurrencyCode(GooglePayParams.CURRENCY_CODE).build();

        CardRequirements cardRequirements = CardRequirements.newBuilder()
                .addAllowedCardNetworks(
                        Arrays.asList(WalletConstants.CARD_NETWORK_VISA, WalletConstants.CARD_NETWORK_MASTERCARD))
                .build();

        PaymentMethodTokenizationParameters params = PaymentMethodTokenizationParameters.newBuilder()
                .setPaymentMethodTokenizationType(WalletConstants.PAYMENT_METHOD_TOKENIZATION_TYPE_PAYMENT_GATEWAY)
                .addParameter("gatewayMerchantId", sdk.getTerminalKey()).addParameter("gateway", "tinkoff").build();

        PaymentDataRequest.Builder request = PaymentDataRequest.newBuilder().setTransactionInfo(transactionInfo)
                .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD)
                .addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD)
                .setPhoneNumberRequired(googlePayParams.isPhoneRequired())
                .setShippingAddressRequired(googlePayParams.isAddressRequired())
                .setCardRequirements(cardRequirements).setPaymentMethodTokenizationParameters(params);
        return request.build();
    }

    private String getFormattedPrice() {
        Intent intent = getActivity().getIntent();
        Money money = (Money) intent.getSerializableExtra(PayFormActivity.EXTRA_AMOUNT);
        BigDecimal bigDecimal = new BigDecimal(money.getCoins());
        bigDecimal = bigDecimal.setScale(2, BigDecimal.ROUND_HALF_EVEN);
        return bigDecimal.toString();
    }

    private void setRecurrentModeForCardView(boolean recurrentMode) {
        if (recurrentMode) {
            ecvCard.setEnabled(false);
            ecvCard.setFocusable(false);
            ecvCard.clearFocus();
            ecvCard.setFullCardNumberModeEnable(false);
            ecvCard.setCardHint(getString(R.string.acq_recurrent_mode_card_hint));
        } else {
            ecvCard.setEnabled(true);
            ecvCard.setFocusable(true);
            ecvCard.setFullCardNumberModeEnable(true);
            ecvCard.setCardHint(ecvCard.getCardNumberHint());
        }
    }

    private void resolveButtonAndIconsPosition(View root) {
        LinearLayout containerLayout = (LinearLayout) root.findViewById(R.id.ll_container_layout);
        View buttons = root.findViewById(R.id.pay_buttons_layout);
        View space = root.findViewById(R.id.space);
        View secureIcons = root.findViewById(R.id.iv_secure_icons);
        switch (buttonAndIconsPositionMode) {
        case BUTTON_UNDER_FIELDS_ICONS_ON_BOTTOM:
            break;
        case ICONS_ON_BOTTOM_BUTTON_UNDER_ICONS:
            containerLayout.removeView(buttons);
            containerLayout.addView(buttons);
            break;
        case ICONS_UNDER_FIELDS_BUTTON_ON_BOTTOM:
            containerLayout.removeView(secureIcons);
            removeSpace(containerLayout, space);
            containerLayout.removeView(buttons);
            containerLayout.addView(secureIcons);
            addSpace(containerLayout, space);
            containerLayout.addView(buttons);
            break;
        case ICONS_UNDER_FIELDS_BUTTON_UNDER_ICONS:
            containerLayout.removeView(secureIcons);
            removeSpace(containerLayout, space);
            containerLayout.removeView(buttons);
            containerLayout.addView(secureIcons);
            containerLayout.addView(buttons);
            break;
        case BUTTON_UNDER_FIELDS_ICONS_UNDER_BOTTOM:
            removeSpace(containerLayout, space);
            break;
        }
    }

    private void addSpace(ViewGroup container, View space) {
        if (space != null) {
            container.addView(space);
        }
    }

    private void removeSpace(ViewGroup container, View space) {
        if (space != null) {
            container.removeView(space);
        }
    }

    private Language resolveLanguage() {
        Locale locale = getResources().getConfiguration().locale;
        String language = locale.getLanguage();

        if (language != null && language.toLowerCase().startsWith("ru")) {
            return null;
        } else {
            return Language.ENGLISH;
        }
    }

    private boolean validateInput(EditCardView cardView, String enteredEmail) {
        int errorMessage = 0;
        if (!cardView.isFilledAndCorrect()) {
            errorMessage = R.string.acq_invalid_card;
        } else if (!isEmailValid(enteredEmail)) {
            errorMessage = R.string.acq_invalid_email;
        }

        if (errorMessage != 0) {
            makeText(getActivity(), errorMessage, Toast.LENGTH_SHORT).show();
            return false;
        }

        return true;
    }

    private boolean isEmailValid(String email) {
        return TextUtils.isEmpty(email) || emailPattern.matcher(email).matches();
    }

    private InitRequestBuilder createInitRequestBuilder(Intent intent) {
        final String password = intent.getStringExtra(PayFormActivity.EXTRA_PASSWORD);
        final String terminalKey = intent.getStringExtra(PayFormActivity.EXTRA_TERMINAL_KEY);

        final InitRequestBuilder builder = new InitRequestBuilder(password, terminalKey);

        final String orderId = intent.getStringExtra(PayFormActivity.EXTRA_ORDER_ID);
        final String customerKey = intent.getStringExtra(PayFormActivity.EXTRA_CUSTOMER_KEY);
        String title = intent.getStringExtra(PayFormActivity.EXTRA_TITLE);
        if (title != null && title.length() > PAY_FORM_MAX_LENGTH) {
            title = title.substring(0, PAY_FORM_MAX_LENGTH);
        }
        final Money amount = (Money) intent.getSerializableExtra(PayFormActivity.EXTRA_AMOUNT);
        final boolean recurrentPayment = intent.getBooleanExtra(PayFormActivity.EXTRA_RECURRENT_PAYMENT, false);

        builder.setOrderId(orderId).setCustomerKey(customerKey).setPayForm(title).setAmount(amount.getCoins())
                .setRecurrent(recurrentPayment);

        final Language language = resolveLanguage();
        if (language != null) {
            builder.setLanguage(language.toString());
        }

        final Receipt receiptValue = (Receipt) intent.getSerializableExtra(PayFormActivity.EXTRA_RECEIPT_VALUE);
        if (receiptValue != null) {
            builder.setReceipt(receiptValue);
        }

        final List<Receipt> receipts = (List<Receipt>) intent
                .getSerializableExtra(PayFormActivity.EXTRA_RECEIPTS_VALUE);
        final List<Shop> shops = (List<Shop>) intent.getSerializableExtra(PayFormActivity.EXTRA_SHOPS_VALUE);
        if (shops != null) {
            builder.setShops(shops, receipts);
        }

        final Map<String, String> dataValue = (Map<String, String>) intent
                .getSerializableExtra(PayFormActivity.EXTRA_DATA_VALUE);
        if (dataValue != null) {
            builder.setData(dataValue);
        }

        if (rejectedPaymentInfo != null) { // rejected recurrent payment
            HashMap<String, String> map = new HashMap<>();
            map.put(RECURRING_TYPE_KEY, RECURRING_TYPE_VALUE);
            map.put(FAIL_MAPI_SESSION_ID, Long.toString(rejectedPaymentInfo.getPaymentId()));
            builder.addData(map);
            rejectedPaymentInfo = null;
        }

        return builder;
    }

    private CardData getCardData(PayFormActivity activity) {
        final Card card = activity.getSourceCard();

        if (chargeMode) {
            return new CardData(card.getRebillId());
        } else if (card == null) {
            return new CardData(ecvCard.getCardNumber(), ecvCard.getExpireDate(), ecvCard.getCvc());
        } else {
            String cardId = card.getCardId();
            String cvc = ecvCard.getCvc();
            return new CardData(cardId, cvc);
        }
    }

    private String getEmail() {
        if (etEmail == null) {
            return null;
        }
        String input = etEmail.getText().toString().trim();
        return input.isEmpty() ? null : input;
    }

    private void startChooseCard() {
        ((PayFormActivity) getActivity()).startChooseCard();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (cardScanner.hasCameraResult(requestCode, data)) {
            ICreditCard card = cardScanner.parseCameraData(data);
            setCreditCardData(card);
        } else if (cardScanner.hasNfcResult(requestCode, resultCode)) {
            ICreditCard card = cardScanner.parseNfcData(data);
            setCreditCardData(card);
        } else if (cardScanner.isNfcError(requestCode, resultCode)) {
            Toast.makeText(getContext(), R.string.acq_nfc_scan_failed, Toast.LENGTH_SHORT).show();
        } else {
            if (resultCode == Activity.RESULT_OK && (requestCode == LOAD_PAYMENT_DATA_REQUEST_CODE)) {
                ((PayFormActivity) getActivity()).showProgressDialog();
            }

            int googlePayErrorCode = -1;
            if (data != null) {
                googlePayErrorCode = data.getIntExtra(WalletConstants.EXTRA_ERROR_CODE, -1);
            }

            if (requestCode == LOAD_PAYMENT_DATA_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
                initGooglePayRequest(data);
            } else if (requestCode == LOAD_PAYMENT_DATA_REQUEST_CODE && resultCode != Activity.RESULT_CANCELED) {
                handleGooglePayError(googlePayErrorCode);
            }

            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    private void initGooglePayRequest(Intent data) {
        String token = PaymentData.getFromIntent(data).getPaymentMethodToken().getToken();
        AcquiringSdk.log("GPay token: " + token);
        String googlePayToken = Base64.encodeToString(token.getBytes(), Base64.DEFAULT).trim();

        final String enteredEmail = getEmail();
        if (!isEmailValid(enteredEmail)) {
            makeText(getActivity(), R.string.acq_invalid_email, Toast.LENGTH_SHORT).show();
            return;
        }

        final PayFormActivity activity = (PayFormActivity) getActivity();
        activity.showProgressDialog();
        InitRequestBuilder requestBuilder = createInitRequestBuilder(activity.getIntent());
        initPayment(sdk, requestBuilder, null, googlePayToken, enteredEmail, chargeMode);
    }

    private void setCreditCardData(ICreditCard card) {
        chargeMode = false;
        ecvCard.setRecurrentPaymentMode(false);
        setRecurrentModeForCardView(false);
        prepareEditableCardView(null, false, true);

        ecvCard.setCardNumber(card.getCardNumber());
        ecvCard.setExpireDate(card.getExpireDate());
    }

    private void handleGooglePayError(int errorCode) {
        PayFormHandler.INSTANCE.obtainMessage(PayFormHandler.ANDROID_PAY_ERROR).sendToTarget();
    }

    private static void initPayment(final AcquiringSdk sdk, final InitRequestBuilder requestBuilder,
            final CardData cardData, final String googlePayToken, final String email, final boolean chargeMode) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    requestBuilder.setChargeFlag(chargeMode && googlePayToken == null);
                    final Long paymentId = sdk.init(requestBuilder);
                    PayFormHandler.INSTANCE.obtainMessage(PayFormHandler.PAYMENT_INIT_COMPLETED, paymentId)
                            .sendToTarget();

                    if (!chargeMode || googlePayToken != null) {
                        final ThreeDsData threeDsData;
                        if (googlePayToken == null) {
                            threeDsData = sdk.finishAuthorize(paymentId, cardData, email);
                        } else {
                            threeDsData = sdk.finishAuthorize(paymentId, googlePayToken, email);
                        }
                        if (threeDsData.isThreeDsNeed()) {
                            CommonSdkHandler.INSTANCE.obtainMessage(CommonSdkHandler.START_3DS, threeDsData)
                                    .sendToTarget();
                        } else {
                            CommonSdkHandler.INSTANCE.obtainMessage(CommonSdkHandler.SUCCESS).sendToTarget();
                        }
                    } else {
                        PaymentInfo paymentInfo = sdk.charge(paymentId, cardData.getRebillId());
                        if (paymentInfo.isSuccess()) {
                            CommonSdkHandler.INSTANCE.obtainMessage(CommonSdkHandler.SUCCESS).sendToTarget();
                        } else {
                            PayFormHandler.INSTANCE
                                    .obtainMessage(PayFormHandler.CHARGE_REQUEST_REJECTED, paymentInfo)
                                    .sendToTarget();
                        }
                    }
                } catch (Exception e) {
                    Throwable cause = e.getCause();
                    Message msg;
                    if (cause != null && cause instanceof NetworkException) {
                        msg = CommonSdkHandler.INSTANCE.obtainMessage(CommonSdkHandler.NO_NETWORK);
                    } else {
                        msg = CommonSdkHandler.INSTANCE.obtainMessage(CommonSdkHandler.EXCEPTION, e);
                    }
                    msg.sendToTarget();
                }
            }
        }).start();
    }

    @Override
    public void onCardReady() {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                PayFormActivity activity = (PayFormActivity) getActivity();
                if (activity == null) {
                    return;
                }
                Card[] cards = activity.getCards();
                Card sourceCard = activity.getSourceCard();
                boolean hasCard = sourceCard != null;
                boolean needClearCardView = needClearCardView(activity);
                srcCardChooser.setVisibility(cards != null && cards.length > 0 ? View.VISIBLE : View.GONE);
                tvSrcCardLabel.setText(getLabel(chargeMode, hasCard));
                if (chargeMode) {
                    if (hasCard) {
                        ecvCard.setRecurrentPaymentMode(true);
                        ecvCard.setCardNumber(sourceCard.getPan());
                        hideSoftKeyboard();
                        if (customKeyboard != null) {
                            customKeyboard.hide();
                        }
                    } else if (cards == null || cards.length == 0 || needClearCardView) {
                        chargeMode = false;
                        ecvCard.setRecurrentPaymentMode(false);
                        setRecurrentModeForCardView(false);
                        prepareEditableCardView(null, false, needClearCardView);
                    } else {
                        ecvCard.setRecurrentPaymentMode(false);
                        setRecurrentModeForCardView(true);
                        prepareEditableCardView(null, false, false);
                    }
                } else {
                    prepareEditableCardView(sourceCard, hasCard, needClearCardView);
                }
            }
        });
    }

    @Override
    public void onChargeRequestRejected(PaymentInfo paymentInfo) {
        chargeMode = false;
        rejectedPaymentInfo = paymentInfo;
        final PayFormActivity activity = (PayFormActivity) getActivity();
        if (!TextUtils.isEmpty(paymentInfo.getCardId())) {
            activity.selectCardById(paymentInfo.getCardId());
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
        builder.setTitle(R.string.acq_complete_payment)
                .setPositiveButton(R.string.acq_complete_payment_ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Card selectedCard = activity.getSourceCard();
                        View googlePayButton = getView().findViewById(R.id.fl_android_pay_placeholder);
                        googlePayButton.setVisibility(View.INVISIBLE);
                        googlePayButton.setEnabled(false);
                        srcCardChooser.setVisibility(View.INVISIBLE);
                        srcCardChooser.setEnabled(false);
                        ecvCard.setRecurrentPaymentMode(false);
                        ecvCard.setCardNumber(selectedCard.getPan());
                        ecvCard.dispatchFocus();
                    }
                }).setCancelable(false).show();
    }

    private void prepareEditableCardView(Card sourceCard, boolean hasCard, boolean needClearCardView) {
        ecvCard.setSavedCardState(hasCard);
        if (hasCard && sourceCard != null) {
            ecvCard.setCardNumber(sourceCard.getPan());
        } else {
            if (needClearCardView) {
                ecvCard.clear();
            } else {
                ecvCard.dispatchFocus();
            }
        }
    }

    private boolean needClearCardView(PayFormActivity activity) {
        Bundle bundle = activity.getFragmentsCommunicator().getResult(PayFormActivity.RESULT_CODE_CLEAR_CARD);
        return bundle != null;
    }

    private String getLabel(boolean chargeMode, boolean hasCard) {
        if (hasCard) {
            return getString(R.string.acq_saved_card_label);
        } else if (chargeMode) {
            return "";
        } else {
            return getString(R.string.acq_new_card_label);
        }
    }

    private void hideSoftKeyboard() {
        View view = getActivity().getCurrentFocus();
        if (view != null) {
            InputMethodManager imm = (InputMethodManager) view.getContext()
                    .getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    }

    @Override
    public boolean onBackPressed() {
        boolean isUsingCustomKeyboard = ((PayFormActivity) getActivity()).shouldUseCustomKeyboard();
        if (customKeyboard != null && isUsingCustomKeyboard) {
            return customKeyboard.hide();
        } else {
            return false;
        }
    }

    protected final void setRejectAction(RejectAction rejectAction) {
        this.rejectAction = rejectAction;
    }

    protected interface RejectAction {

        void onPerformReject();
    }
}