From 927cba9953f21d44789e852b0747487c21c83c4c Mon Sep 17 00:00:00 2001 From: Chintan Soni Date: Thu, 12 Oct 2023 17:25:03 +0100 Subject: [PATCH] PIMOB: Java sample app updated for CVV tokenization --- app/src/main/AndroidManifest.xml | 3 + .../CVVTokenizationActivity.java | 144 ++++++++++++++++++ .../checkout_android/CheckoutActivity.java | 60 +++++--- .../checkout/checkout_android/Constants.java | 56 ++++--- .../viewmodels/CVVTokenizationViewModel.java | 17 +++ app/src/main/res/layout/activity_checkout.xml | 14 +- .../res/layout/activity_cvv_tokenization.xml | 70 +++++++++ app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/strings.xml | 7 +- .../cvvinputfield/api/CVVComponentMediator.kt | 9 ++ .../api/InternalCVVComponentMediator.kt | 8 + .../models/CVVComponentConfig.kt | 2 +- 13 files changed, 339 insertions(+), 54 deletions(-) create mode 100644 app/src/main/java/checkout/checkout_android/CVVTokenizationActivity.java create mode 100644 app/src/main/java/checkout/checkout_android/viewmodels/CVVTokenizationViewModel.java create mode 100644 app/src/main/res/layout/activity_cvv_tokenization.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c7d575641..d77607cc1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,6 +24,9 @@ + diff --git a/app/src/main/java/checkout/checkout_android/CVVTokenizationActivity.java b/app/src/main/java/checkout/checkout_android/CVVTokenizationActivity.java new file mode 100644 index 000000000..110b0c41a --- /dev/null +++ b/app/src/main/java/checkout/checkout_android/CVVTokenizationActivity.java @@ -0,0 +1,144 @@ +package checkout.checkout_android; + +import static checkout.checkout_android.Constants.PUBLIC_KEY_CVV_TOKENIZATION; + +import android.app.AlertDialog; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; + +import androidx.activity.ComponentActivity; +import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; + +import com.checkout.base.model.CardScheme; +import com.checkout.base.model.Environment; +import com.checkout.frames.cvvinputfield.CVVComponentApiFactory; +import com.checkout.frames.cvvinputfield.api.CVVComponentApi; +import com.checkout.frames.cvvinputfield.api.CVVComponentMediator; +import com.checkout.frames.cvvinputfield.models.CVVComponentConfig; +import com.checkout.frames.cvvinputfield.style.DefaultCVVInputFieldStyle; +import com.checkout.frames.model.CornerRadius; +import com.checkout.frames.model.Shape; +import com.checkout.frames.style.component.base.ContainerStyle; +import com.checkout.frames.style.component.base.InputFieldIndicatorStyle; +import com.checkout.frames.style.component.base.InputFieldStyle; +import com.checkout.frames.style.component.base.TextStyle; +import com.checkout.tokenization.model.CVVTokenDetails; +import com.checkout.tokenization.model.CVVTokenizationResultHandler; + +import checkout.checkout_android.viewmodels.CVVTokenizationViewModel; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + +public class CVVTokenizationActivity extends ComponentActivity { + private Button amexCVVTokenizationButton; + private Button cvvTokenizationButton; + private LinearLayout cvvComponentLinearLayout; + private LinearLayout amexCustomCVVComponentLinearLayout; + private Function1 resultHandler; + private CVVTokenizationViewModel cvvTokenizationViewModel; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_cvv_tokenization); + initViews(); + + cvvTokenizationViewModel = new ViewModelProvider(this).get(CVVTokenizationViewModel.class); + + setupObservers(); + + // Create cvvComponentApi + CVVComponentApi cvvComponentApi = CVVComponentApiFactory.create(PUBLIC_KEY_CVV_TOKENIZATION, Environment.SANDBOX, this); + + // initialise CVVTokenizationResultHandler for tokenization + resultHandler = result -> { + if (result instanceof CVVTokenizationResultHandler.Success) { + CVVTokenDetails tokenDetails = ((CVVTokenizationResultHandler.Success) result).getTokenDetails(); + displayTokenResultDialog(tokenDetails.getToken(), "Token Created Successfully"); + } else if (result instanceof CVVTokenizationResultHandler.Failure) { + String errorMessage = ((CVVTokenizationResultHandler.Failure) result).getErrorMessage(); + displayTokenResultDialog(errorMessage, "Token Failure"); + } + return Unit.INSTANCE; + }; + + createCVVComponentMediator(cvvComponentApi); + + createAMEXCVVComponentMediator(cvvComponentApi); + } + + private void setupObservers() { + cvvTokenizationViewModel.getIsEnteredAMEXCVVValid().observe(this, isCVVValid -> { + if (isCVVValid) + amexCVVTokenizationButton.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark, this.getTheme())); + else { + amexCVVTokenizationButton.setBackgroundColor(getResources().getColor(R.color.colorGray, this.getTheme())); + } + amexCVVTokenizationButton.setEnabled(isCVVValid); + }); + } + + private void initViews() { + amexCVVTokenizationButton = findViewById(R.id.btnAmexCVVTokenization); + cvvTokenizationButton = findViewById(R.id.btnCVVTokenization); + cvvComponentLinearLayout = findViewById(R.id.linearCVVComponent); + amexCustomCVVComponentLinearLayout = findViewById(R.id.linearAMEXCustomCVVComponent); + cvvTokenizationButton.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark, this.getTheme())); + } + + private void createCVVComponentMediator(CVVComponentApi cvvComponentApi) { + // Create config for CVV component + CVVComponentConfig visaCVVComponentConfig = new CVVComponentConfig( + CardScheme.Companion.fromString("unknown"), + isEnteredCVVValid -> Unit.INSTANCE, + DefaultCVVInputFieldStyle.INSTANCE.create() + ); + + // Create CVVComponentMediator for CVV component + CVVComponentMediator defaultCVVComponentMediator = cvvComponentApi.createComponentMediator(visaCVVComponentConfig); + View defaultCVVComponentView = defaultCVVComponentMediator.provideCvvComponentContent(cvvComponentLinearLayout); + + // Add defaultCVVComponent as view in parent layout + cvvComponentLinearLayout.addView(defaultCVVComponentView); + + cvvTokenizationButton.setOnClickListener(v -> defaultCVVComponentMediator.createToken(resultHandler)); + } + + + private void createAMEXCVVComponentMediator(CVVComponentApi cvvComponentApi) { + CVVComponentConfig visaCVVComponentConfig = new CVVComponentConfig( + CardScheme.Companion.fromString("American_express"), + isEnteredCVVValid -> { + cvvTokenizationViewModel.setIsAmexCVVValid(isEnteredCVVValid); + return Unit.INSTANCE; + }, + new InputFieldStyle(new TextStyle(), "Enter cvv", + null, new TextStyle(), + new ContainerStyle(Constants.backgroundColor + , Shape.RoundCorner, + new CornerRadius(9)), + new InputFieldIndicatorStyle.Underline() + ) + ); + + CVVComponentMediator amexCVVComponentMediator = cvvComponentApi.createComponentMediator(visaCVVComponentConfig); + + View amexCVVComponentView = amexCVVComponentMediator.provideCvvComponentContent(amexCustomCVVComponentLinearLayout); + amexCustomCVVComponentLinearLayout.addView(amexCVVComponentView); + + amexCVVTokenizationButton.setOnClickListener(v -> amexCVVComponentMediator.createToken(resultHandler)); + } + + + private void displayTokenResultDialog(String message, String title) { + new AlertDialog.Builder(this).setTitle(title) + .setMessage(message) + .setCancelable(false) + .setNeutralButton("Ok", (dialog, id) -> dialog.dismiss()) + .show(); + } + +} diff --git a/app/src/main/java/checkout/checkout_android/CheckoutActivity.java b/app/src/main/java/checkout/checkout_android/CheckoutActivity.java index 02b2461c5..9de3643ad 100644 --- a/app/src/main/java/checkout/checkout_android/CheckoutActivity.java +++ b/app/src/main/java/checkout/checkout_android/CheckoutActivity.java @@ -1,9 +1,11 @@ package checkout.checkout_android; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; +import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; @@ -14,29 +16,37 @@ import java.util.Locale; public class CheckoutActivity extends Activity { - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_checkout); - - initItemUI("Simple Bike", Constants.PAYMENT_AMOUNT, R.drawable.bike); - - findViewById(R.id.button_buy).setOnClickListener(this::onBuyButtonClicked); - } - - private void initItemUI(String name, long price, @DrawableRes int imageResource) { - TextView itemName = findViewById(R.id.text_item_name); - ImageView itemImage = findViewById(R.id.image_item_image); - TextView itemPrice = findViewById(R.id.text_item_price); - - itemName.setText(name); - itemImage.setImageResource(imageResource); - NumberFormat numberFormat = NumberFormat.getCurrencyInstance(Locale.getDefault()); - itemPrice.setText(numberFormat.format((double) price / 100)); - } - - private void onBuyButtonClicked(View view) { - startActivity(new Intent(this, DemoActivity.class)); - } + private Button cvvToknizationButton; + private Button cardTokenizationButton; + + @SuppressLint("CutPasteId") + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_checkout); + + cvvToknizationButton = findViewById(R.id.button_cvv_tokenization); + cardTokenizationButton = findViewById(R.id.button_buy); + + initItemUI("Simple Bike", Constants.PAYMENT_AMOUNT, R.drawable.bike, cardTokenizationButton); + + findViewById(R.id.button_buy).setOnClickListener(this::onBuyButtonClicked); + + cvvToknizationButton.setOnClickListener(v -> { + startActivity(new Intent(this, CVVTokenizationActivity.class)); + }); + } + + private void initItemUI(String name, long price, @DrawableRes int imageResource, Button cvvToknizationButton) { + TextView itemName = findViewById(R.id.text_item_name); + ImageView itemImage = findViewById(R.id.image_item_image); + itemName.setText(name); + itemImage.setImageResource(imageResource); + NumberFormat numberFormat = NumberFormat.getCurrencyInstance(Locale.getDefault()); + cvvToknizationButton.setText(getText(R.string.button_buy) + numberFormat.format((double) price / 100)); + } + + private void onBuyButtonClicked(View view) { + startActivity(new Intent(this, DemoActivity.class)); + } } diff --git a/app/src/main/java/checkout/checkout_android/Constants.java b/app/src/main/java/checkout/checkout_android/Constants.java index 0a1da92cc..ea94a64cc 100644 --- a/app/src/main/java/checkout/checkout_android/Constants.java +++ b/app/src/main/java/checkout/checkout_android/Constants.java @@ -3,30 +3,36 @@ import com.checkout.base.model.Environment; public class Constants { - private Constants() { } + /** + * Target platform environment + */ + public static final Environment ENVIRONMENT = Environment.SANDBOX; + /** + * Replace with public key from Hub in Sandbox Environment + */ + public static final String PUBLIC_KEY = "pk_test_b37b8b6b-fc9a-483f-a77e-3386b606f90e"; - /** - * Target platform environment - */ - public static final Environment ENVIRONMENT = Environment.SANDBOX; - /** - * Replace with public key from Hub in Sandbox Environment - */ - public static final String PUBLIC_KEY = "pk_test_b37b8b6b-fc9a-483f-a77e-3386b606f90e"; - /** - * Replace with Secret key from Hub in Sandbox Environment - */ - public static final String SECRET_KEY = "sk_test_568e6077-a08f-4692-9237-cc6c48dcf6aa"; - /** - * Replace with Success/Failure Urls from Hub in Sandbox Environment - */ - public static final String SUCCESS_URL = "https://httpstat.us/200?q=Success"; - public static final String FAILURE_URL = "https://httpstat.us/200?q=Failure"; - /** - * The payment amount to used when creating a payment for 3DS authentication. - *

- * Using specific amount values will trigger certain failure modes. - *

- */ - public static final Long PAYMENT_AMOUNT = 10000L; + /** + * Replace with public key from Hub in Sandbox Environment, testing key for CVV Tokenization + */ + public static final String PUBLIC_KEY_CVV_TOKENIZATION = "pk_6b30805a-1f3b-4c63-8b75-eb3030109173"; + /** + * Replace with Secret key from Hub in Sandbox Environment + */ + public static final String SECRET_KEY = "sk_test_568e6077-a08f-4692-9237-cc6c48dcf6aa"; + /** + * Replace with Success/Failure Urls from Hub in Sandbox Environment + */ + public static final String SUCCESS_URL = "https://httpstat.us/200?q=Success"; + public static final String FAILURE_URL = "https://httpstat.us/200?q=Failure"; + /** + * The payment amount to used when creating a payment for 3DS authentication. + *

+ * Using specific amount values will trigger certain failure modes. + *

+ */ + public static final Long PAYMENT_AMOUNT = 10000L; + + + public static Long backgroundColor = 0XFFFFCDC2L; } diff --git a/app/src/main/java/checkout/checkout_android/viewmodels/CVVTokenizationViewModel.java b/app/src/main/java/checkout/checkout_android/viewmodels/CVVTokenizationViewModel.java new file mode 100644 index 000000000..92ae29eda --- /dev/null +++ b/app/src/main/java/checkout/checkout_android/viewmodels/CVVTokenizationViewModel.java @@ -0,0 +1,17 @@ +package checkout.checkout_android.viewmodels; + +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class CVVTokenizationViewModel extends ViewModel { + + private final MutableLiveData isEnteredAMEXCVVValid = new MutableLiveData<>(); + + public MutableLiveData getIsEnteredAMEXCVVValid() { + return isEnteredAMEXCVVValid; + } + + public void setIsAmexCVVValid(Boolean isCVVValid) { + isEnteredAMEXCVVValid.setValue(isCVVValid); + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_checkout.xml b/app/src/main/res/layout/activity_checkout.xml index 971abaffc..4a08aef21 100755 --- a/app/src/main/res/layout/activity_checkout.xml +++ b/app/src/main/res/layout/activity_checkout.xml @@ -44,19 +44,29 @@ - + + + - + diff --git a/app/src/main/res/layout/activity_cvv_tokenization.xml b/app/src/main/res/layout/activity_cvv_tokenization.xml new file mode 100644 index 000000000..6349256bf --- /dev/null +++ b/app/src/main/res/layout/activity_cvv_tokenization.xml @@ -0,0 +1,70 @@ + + + + + + + + + +