From e8d08c0d586bea59cdb2cab41518d96103c65bc0 Mon Sep 17 00:00:00 2001 From: Natthawut Haematulin Date: Fri, 20 Oct 2023 18:07:49 +0700 Subject: [PATCH 1/5] [MIT-1845] Add FLAG_SECURE (#279) * feat: add FLAG_SECURE to Activity classes * test: add UI tests * test: fix failed test * feat: add EXTRA_IS_SECURE * test: add unit tests * docs: update README.md * chore: enable FLAG_SECURE by default * docs: update README.md * Update README.md Co-authored-by: Aashish Gurung <101558497+aashishgurung@users.noreply.github.com> --------- Co-authored-by: Aashish Gurung <101558497+aashishgurung@users.noreply.github.com> --- README.md | 4 +++ .../ui/AuthorizingPaymentActivityTest.kt | 19 ++++++++++++ .../android/ui/AuthorizingPaymentActivity.kt | 6 ++++ .../co/omise/android/ui/CreditCardActivity.kt | 5 +++ .../java/co/omise/android/ui/OmiseActivity.kt | 6 ++++ .../android/ui/PaymentCreatorActivity.kt | 9 +++++- .../android/ui/CreditCardActivityTest.kt | 26 ++++++++++++---- .../android/ui/PaymentCreatorActivityTest.kt | 31 +++++++++++++++---- 8 files changed, 93 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 73deeba2e..d34a37a18 100644 --- a/README.md +++ b/README.md @@ -521,6 +521,10 @@ If you enable ProGuard, then add this rules in your ProGuard file. -keep class com.nimbusds.jose.** { *; } ``` +## Protecting screenshot and screen recording + +**Omise Android SDK** comes with built-in protection against screenshoot and screen recording. If you wish to disable this feature, you can pass `OmiseActivity.EXTRA_IS_SECURE` with a value of `false` when starting the following activities: `CreditCardActivity`, `PaymentCreatorActivity`, and `AuthorizingPaymentActivity`. + ## Contributing Pull requests and bug fixes are welcome. For larger scope of work, please pop on to our [forum](https://forum.omise.co) to discuss first. diff --git a/sdk/src/androidTest/java/co/omise/android/ui/AuthorizingPaymentActivityTest.kt b/sdk/src/androidTest/java/co/omise/android/ui/AuthorizingPaymentActivityTest.kt index 6e99169b7..fd4923ff1 100644 --- a/sdk/src/androidTest/java/co/omise/android/ui/AuthorizingPaymentActivityTest.kt +++ b/sdk/src/androidTest/java/co/omise/android/ui/AuthorizingPaymentActivityTest.kt @@ -4,6 +4,7 @@ import android.app.Activity import android.app.Instrumentation import android.content.Intent import android.net.Uri +import android.view.WindowManager import android.widget.ProgressBar import androidx.arch.core.executor.testing.CountingTaskExecutorRule import androidx.lifecycle.MutableLiveData @@ -55,6 +56,7 @@ import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.containsString import org.hamcrest.CoreMatchers.instanceOf import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before @@ -331,4 +333,21 @@ class AuthorizingPaymentActivityTest { ) ) } + + @Test + fun flagSecure_whenParameterIsFalseThenAttributesMustNotContainFlagSecure() { + intent.putExtra(OmiseActivity.EXTRA_IS_SECURE, false) + val scenario = ActivityScenario.launchActivityForResult(intent) + scenario.onActivity { + assertNotEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE) + } + } + + @Test + fun flagSecure_whenParameterNotSetThenAttributesMustContainFlagSecure() { + val scenario = ActivityScenario.launchActivityForResult(intent) + scenario.onActivity { + assertEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE) + } + } } diff --git a/sdk/src/main/java/co/omise/android/ui/AuthorizingPaymentActivity.kt b/sdk/src/main/java/co/omise/android/ui/AuthorizingPaymentActivity.kt index 1f330a297..29ef8bf56 100644 --- a/sdk/src/main/java/co/omise/android/ui/AuthorizingPaymentActivity.kt +++ b/sdk/src/main/java/co/omise/android/ui/AuthorizingPaymentActivity.kt @@ -6,6 +6,7 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.View +import android.view.WindowManager import android.webkit.CookieManager import android.webkit.JsPromptResult import android.webkit.JsResult @@ -54,6 +55,11 @@ class AuthorizingPaymentActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + if (intent.getBooleanExtra(OmiseActivity.EXTRA_IS_SECURE, true)) { + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + setContentView(R.layout.activity_authorizing_payment) supportActionBar?.title = threeDSConfig.uiCustomization?.toolbarCustomization?.headerText diff --git a/sdk/src/main/java/co/omise/android/ui/CreditCardActivity.kt b/sdk/src/main/java/co/omise/android/ui/CreditCardActivity.kt index e13897987..b92f7296b 100644 --- a/sdk/src/main/java/co/omise/android/ui/CreditCardActivity.kt +++ b/sdk/src/main/java/co/omise/android/ui/CreditCardActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.os.Bundle import android.view.MenuItem import android.view.View +import android.view.WindowManager import android.widget.Button import android.widget.EditText import android.widget.ImageButton @@ -125,6 +126,10 @@ class CreditCardActivity : OmiseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + if (intent.getBooleanExtra(EXTRA_IS_SECURE, true)) { + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + setContentView(R.layout.activity_credit_card) require(intent.hasExtra(EXTRA_PKEY)) { "Could not find ${::EXTRA_PKEY.name}." } diff --git a/sdk/src/main/java/co/omise/android/ui/OmiseActivity.kt b/sdk/src/main/java/co/omise/android/ui/OmiseActivity.kt index df8d8c52a..132d45474 100644 --- a/sdk/src/main/java/co/omise/android/ui/OmiseActivity.kt +++ b/sdk/src/main/java/co/omise/android/ui/OmiseActivity.kt @@ -23,6 +23,12 @@ abstract class OmiseActivity : AppCompatActivity() { const val EXTRA_TOKEN = "OmiseActivity.token" const val EXTRA_TOKEN_OBJECT = "OmiseActivity.tokenObject" const val EXTRA_CARD_OBJECT = "OmiseActivity.cardObject" + + /** + * Applies [android.view.WindowManager.LayoutParams.FLAG_SECURE] to the activity. + * This will prevent the activity from being captured by screenshots and video recordings. + */ + const val EXTRA_IS_SECURE = "OmiseActivity.isSecure" } @VisibleForTesting diff --git a/sdk/src/main/java/co/omise/android/ui/PaymentCreatorActivity.kt b/sdk/src/main/java/co/omise/android/ui/PaymentCreatorActivity.kt index 5eabd8c17..45eb421a3 100644 --- a/sdk/src/main/java/co/omise/android/ui/PaymentCreatorActivity.kt +++ b/sdk/src/main/java/co/omise/android/ui/PaymentCreatorActivity.kt @@ -3,7 +3,7 @@ package co.omise.android.ui import android.app.Activity import android.content.Intent import android.os.Bundle -import android.util.Log +import android.view.WindowManager import androidx.annotation.VisibleForTesting import androidx.fragment.app.Fragment import co.omise.android.R @@ -18,6 +18,7 @@ import co.omise.android.ui.OmiseActivity.Companion.EXTRA_CURRENCY import co.omise.android.ui.OmiseActivity.Companion.EXTRA_GOOGLEPAY_MERCHANT_ID import co.omise.android.ui.OmiseActivity.Companion.EXTRA_GOOGLEPAY_REQUEST_BILLING_ADDRESS import co.omise.android.ui.OmiseActivity.Companion.EXTRA_GOOGLEPAY_REQUEST_PHONE_NUMBER +import co.omise.android.ui.OmiseActivity.Companion.EXTRA_IS_SECURE import co.omise.android.ui.OmiseActivity.Companion.EXTRA_PKEY import co.omise.android.ui.OmiseActivity.Companion.EXTRA_SOURCE_OBJECT import com.google.android.material.snackbar.Snackbar @@ -64,6 +65,11 @@ class PaymentCreatorActivity : OmiseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + if (intent.getBooleanExtra(EXTRA_IS_SECURE, true)) { + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + } + setContentView(R.layout.activity_payment_creator) initialize() @@ -201,6 +207,7 @@ private class PaymentCreatorNavigationImpl( override fun navigateToCreditCardForm() { val intent = Intent(activity, CreditCardActivity::class.java).apply { putExtra(EXTRA_PKEY, pkey) + putExtra(EXTRA_IS_SECURE, activity.intent.getBooleanExtra(EXTRA_IS_SECURE, true)) } activity.startActivityForResult(intent, requestCode) } diff --git a/sdk/src/sharedTest/java/co/omise/android/ui/CreditCardActivityTest.kt b/sdk/src/sharedTest/java/co/omise/android/ui/CreditCardActivityTest.kt index 378c301fe..21f40a3af 100644 --- a/sdk/src/sharedTest/java/co/omise/android/ui/CreditCardActivityTest.kt +++ b/sdk/src/sharedTest/java/co/omise/android/ui/CreditCardActivityTest.kt @@ -6,6 +6,7 @@ import android.app.Application import android.content.Intent import android.os.Bundle import android.view.View +import android.view.WindowManager import androidx.recyclerview.widget.RecyclerView.ViewHolder import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario.launchActivityForResult @@ -42,8 +43,8 @@ import org.hamcrest.CoreMatchers.not import org.hamcrest.Matcher import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any @@ -54,7 +55,6 @@ import org.mockito.kotlin.reset import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) -@Ignore class CreditCardActivityTest { private lateinit var scenario: ActivityScenario @@ -295,10 +295,7 @@ class CreditCardActivityTest { @Test fun submitForm_disableFormWhenPressSubmit() { - whenever(mockClient.send(any(), any())).doAnswer { invocation -> - val callback = invocation.getArgument>(1) - callback.onRequestSucceed(Token()) - } + whenever(mockClient.send(any(), any())).doAnswer {} onView(withId(R.id.edit_card_number)).perform(typeText("4242424242424242")) onView(withId(R.id.edit_card_name)).perform(typeText("John Doe")) onView(withId(R.id.edit_expiry_date)).perform(typeText("1234")) @@ -386,6 +383,23 @@ class CreditCardActivityTest { val result = scenario.result assertEquals(RESULT_CANCELED, result.resultCode) } + + @Test + fun flagSecure_whenParameterIsFalseThenAttributesMustNotContainFlagSecure() { + intent.putExtra(OmiseActivity.EXTRA_IS_SECURE, false) + val scenario = ActivityScenario.launchActivityForResult(intent) + scenario.onActivity { + assertNotEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE) + } + } + + @Test + fun flagSecure_whenParameterNotSetThenAttributesMustContainFlagSecure() { + val scenario = ActivityScenario.launchActivityForResult(intent) + scenario.onActivity { + assertEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE) + } + } } private fun typeNumberText(numberText: String): ViewAction = diff --git a/sdk/src/sharedTest/java/co/omise/android/ui/PaymentCreatorActivityTest.kt b/sdk/src/sharedTest/java/co/omise/android/ui/PaymentCreatorActivityTest.kt index 572b6717e..930dd8d7f 100644 --- a/sdk/src/sharedTest/java/co/omise/android/ui/PaymentCreatorActivityTest.kt +++ b/sdk/src/sharedTest/java/co/omise/android/ui/PaymentCreatorActivityTest.kt @@ -2,6 +2,7 @@ package co.omise.android.ui import android.app.Activity.RESULT_OK import android.content.Intent +import android.view.WindowManager import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView @@ -9,7 +10,7 @@ import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.matcher.ComponentNameMatchers.hasClassName import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent -import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.intent.rule.IntentsRule import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -17,7 +18,9 @@ import co.omise.android.R import co.omise.android.models.Capability import co.omise.android.models.Token import co.omise.android.ui.OmiseActivity.Companion.EXTRA_TOKEN +import org.junit.Assert import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -26,9 +29,8 @@ import org.junit.runner.RunWith class PaymentCreatorActivityTest { @get:Rule - val intentRule = IntentsTestRule(TestFragmentActivity::class.java) + val intentRule = IntentsRule() - private lateinit var scenario: ActivityScenario private val capability = Capability() private val intent = Intent( ApplicationProvider.getApplicationContext(), @@ -42,7 +44,7 @@ class PaymentCreatorActivityTest { @Test fun initialActivity_collectExtrasIntent() { - scenario = ActivityScenario.launch(intent) + ActivityScenario.launchActivityForResult(intent) onView(withId(R.id.payment_creator_container)).check(matches(isDisplayed())) } @@ -50,7 +52,7 @@ class PaymentCreatorActivityTest { @Test fun navigateToCreditCardForm_startCreditCartActivity() { var activity: PaymentCreatorActivity? = null - scenario = ActivityScenario.launch(intent).onActivity { + ActivityScenario.launchActivityForResult(intent).onActivity { activity = it } @@ -64,10 +66,27 @@ class PaymentCreatorActivityTest { val creditCardIntent = Intent().apply { putExtra(EXTRA_TOKEN, Token()) } - scenario = ActivityScenario.launchActivityForResult(intent).onActivity { + val scenario = ActivityScenario.launchActivityForResult(intent).onActivity { it.performActivityResult(100, RESULT_OK, creditCardIntent) } assertEquals(RESULT_OK, scenario.result.resultCode) } + + @Test + fun flagSecure_whenParameterIsFalseThenAttributesMustNotContainFlagSecure() { + intent.putExtra(OmiseActivity.EXTRA_IS_SECURE, false) + val scenario = ActivityScenario.launchActivityForResult(intent) + scenario.onActivity { + assertNotEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE) + } + } + + @Test + fun flagSecure_whenParameterNotSetThenAttributesMustContainFlagSecure() { + val scenario = ActivityScenario.launchActivityForResult(intent) + scenario.onActivity { + assertEquals(WindowManager.LayoutParams.FLAG_SECURE, it.window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE) + } + } } From 9aa447aa4253f3612d0c6551b803393c2b5cb877 Mon Sep 17 00:00:00 2001 From: muthuswamyopn <95462571+muthuswamyopn@users.noreply.github.com> Date: Wed, 1 Nov 2023 09:20:50 +0530 Subject: [PATCH 2/5] Add deeplink information (#281) * Update README.md - Changed links to docs.opn.ooo * Update README.md - Updated support email --- README.md | 82 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index d34a37a18..8b6830571 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,25 @@ -# Omise Android SDK +# Opn Payments Android SDK [![](https://img.shields.io/maven-central/v/co.omise/omise-android.svg?style=flat-square)](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22co.omise%22%20AND%20a%3A%22omise-android%22) -[![](https://img.shields.io/badge/email-support-yellow.svg?style=flat-square)](mailto:support@omise.co) +[![](https://img.shields.io/badge/email-support-yellow.svg?style=flat-square)](mailto:support@opn.ooo) [![Android CI](https://github.com/omise/omise-android/workflows/Android%20CI/badge.svg)](https://github.com/omise/omise-android/actions) -Omise is a payment service provider currently operating in Thailand. Omise provides a set of clean APIs +Opn Payments is a payment service provider currently operating in Thailand. Opn Payments provides a set of clean APIs that help merchants of any size accept credit cards online. -Omise Android SDK provides Android bindings for the Omise [Token](https://www.omise.co/tokens-api) -and [Source](https://www.omise.co/sources-api) API as well as components for entering credit card information. +Opn Payments Android SDK provides Android bindings for the Opn Payments [Token](https://docs.opn.ooo/tokens-api) +and [Source](https://docs.opn.ooo/sources-api) API, as well as components for entering credit card information. ## Requirements -* Public key. [Register for an Omise account](https://dashboard.omise.co/signup) to obtain your API keys. +* Public key. [Register for an Opn Payments account](https://dashboard.omise.co/signup) to obtain your API keys. * Android 5.0+ (API 21) target or higher. * Android Studio and Gradle build system. ## Merchant Compliance **Card data should never transit through your server. We recommend that you follow our guide on how to safely -[collect credit information](https://www.omise.co/collecting-card-information).** +[collect credit information](https://docs.opn.ooo/collecting-card-information).** To be authorized to create tokens server-side you must have a currently valid PCI-DSS Attestation of Compliance (AoC) delivered by a certified QSA Auditor. @@ -29,7 +29,7 @@ having to go through your server. ## Installation -Add the following line to your project's build.gradle file inside the `dependencies` +Add the following line to your project's `build.gradle` file inside the `dependencies` block: ```gradle @@ -43,7 +43,7 @@ implementation 'co.omise:omise-android:4.+' The simplest way to use this SDK is to integrate the provided `CreditCardActivity` directly into your application. This activity contains a pre-made credit form and will automatically [tokenize credit card -information](https://www.omise.co/security-best-practices) for you. +information](https://docs.opn.ooo/security-best-practices) for you. To use it, first declare the availability of the activity in your `AndroidManifest.xml` file as follows: @@ -67,10 +67,10 @@ private fun showCreditCardForm() { } ``` -Replace the string `pkey_test_123` with the public key obtained from your Omise dashboard. +Replace the string `pkey_test_123` with the public key obtained from your Opn Payments dashboard. After the end-user completes entering credit card information, the activity result -callback will be called, handle it like so: +callback will be called; handle it as follows: ```kotlin override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -91,16 +91,16 @@ A number of results are returned from the activity. You can obtain them from the resulting `Intent` with the following code: * `data.getStringExtra(OmiseActivity.EXTRA_TOKEN)` - The string ID of the token. Use - this if you only needs the ID and not the card data. + this if you only need the ID and not the card data. * `data.getParcelableExtra(OmiseActivity.EXTRA_TOKEN_OBJECT)` - The full `Token` - object returned from the Omise API. + object returned from the Opn Payments API. * `data.getParcelableExtra(OmiseActivity.EXTRA_CARD_OBJECT)` - The `Card` object - which is part of the `Token` object returned from the Omise API. + that is part of the `Token` object returned from the Opn Payments API. ### Custom Credit Card Form If you need to build your own credit card form, components inside `CreditCardActivity` -can be used on their own. For example, the `CreditCardEditText` can be used in XML in this way: +can be used on their own. For example, the `CreditCardEditText` can be used in XML as demonstrated: ```xml { @@ -171,14 +171,14 @@ client.send(request, object : RequestListener{ ``` The `Client` class will automatically dispatch the network call on an internal background -thread and will call listener methods on the thread that initially calls the `send` +thread, and will call listener methods on the thread that initially calls the `send` method. ### Payment Creator activity -Another way to use the Omise Android SDK is to integrate the `PaymentCreatorActivity` +Another way to use the Opn Payments Android SDK is to integrate the `PaymentCreatorActivity` to allow users to create a payment source from the list of sources available for the account. -To use it, first declare the availability of the activity in your AndroidManifest.xml file as follows: +To use it, first declare the availability of the activity in your `AndroidManifest.xml` file as follows: ```xml **Note** > Ensure you are adding payment methods supported by the account. - > If not, you won't be able to create a source to continue the payment process.. + > If not, you won't be able to create a source to continue the payment process. After the end user selects and creates a payment source, the activity result callback will be called; handle it as follows: @@ -257,7 +257,7 @@ Two different results that could be returned are: ### Google Pay activity -We support GooglePay as a tokenization method in our payment gateway. This activity contains a pre-made `Pay with Google Pay` button and will automatically [tokenize the Google Pay token](https://www.omise.co/security-best-practices) for you. +We support GooglePay as a tokenization method in our payment gateway. This activity contains a pre-made `Pay with Google Pay` button and will automatically [tokenize the Google Pay token](https://docs.opn.ooo/security-best-practices) for you. To use it, first declare the availability of the activity in your `AndroidManifest.xml` file as follows: @@ -295,10 +295,10 @@ override fun navigateToGooglePayForm() { } ``` -- Replace the `OMISE_PKEY` with your Omise public key obtained from our dashboard. +- Replace the `OMISE_PKEY` with your Opn Payments public key obtained from our dashboard. - Replace the `amount` with the amount you want to charge with, in subunits. - Replace the `currency` with your currency in the ISO 4217 format. -- Replace the `cardBrands` with the list from our [capability api](https://www.omise.co/capability-api) or leave blank to use default values. +- Replace the `cardBrands` with the list from our [capability api](https://docs.opn.ooo/capability-api) or leave blank to use default values. - Replace the `googlepayMerchantId` with your [Google Pay merchant ID](https://developers.google.com/pay/api/web/guides/setup) (not needed in test mode). - Set the `googlepayRequestBillingAddress` to `true` if you want to attach the cardholder's name and billing address to the token. - When the cardholder's billing address is requested, set the `googlepayRequestPhoneNumber` to `true` to also attach the cardholder's phone number to the token. @@ -311,21 +311,21 @@ resulting `Intent` with the following code: * `data.getStringExtra(OmiseActivity.EXTRA_TOKEN)` - The string ID of the token. Use this if you only need the ID and not the card data. * `data.getParcelableExtra(OmiseActivity.EXTRA_TOKEN_OBJECT)` - The full `Token` - object returned from the Omise API. + object returned from the Opn Payments API. * `data.getParcelableExtra(OmiseActivity.EXTRA_CARD_OBJECT)` - The `Card` object - which is part of the `Token` object returned from the Omise API. + that is part of the `Token` object returned from the Opn Payments API. #### Use your own activity You can use your own activity if you prefer to. We recommend that you follow [Google's tutorial and guidelines](https://developers.google.com/pay/api/android/overview) and make sure that you follow their [brand guidelines](https://developers.google.com/pay/api/android/guides/brand-guidelines) as well. -You can make use of our Google Pay request builder `request/GooglePay.kt`, which will include request builders you can use to request the Google Pay token. +You can make use of our Google Pay request builder `request/GooglePay.kt`, which includes request builders that you can use to request the Google Pay token. Configurations to the builders are modifiable through the class' constructor to suit your needs. However, you are also welcome to make your own integration and call our tokens builder yourself. ### Creating a source -If you need to create a payment source on your own and use it outside of the provided SDK context, you can do follow these steps. First build the Client and supply your public key in this manner: +If you need to create a payment source on your own and use it outside the provided SDK context, follow these steps. First build the Client and supply your public key in this manner: ```kotlin private val client = Client("pkey_test_123") @@ -358,7 +358,7 @@ client.send(request, object : RequestListener{ }) ``` -The `Client` class will automatically dispatch the network call on an internal background thread and will call listener methods on the thread that initially calls the send method. +The `Client` class will automatically dispatch the network call on an internal background thread, and will call listener methods on the thread that initially calls the send method. ### Retrieve Capabilities You can retrieve all of your capabilities and available payment sources through the SDK in the following manner. @@ -375,7 +375,7 @@ Then construct the Capability request: val request = Capability.GetCapabilitiesRequestBuilder().build() ``` -And then send the request using the client we constructed earlier: +And then send the request using the client that you constructed earlier: ```kotlin client.send(request, object : RequestListener { @@ -389,7 +389,7 @@ client.send(request, object : RequestListener { }) ``` -The `Client` class will automatically dispatch the network call on an internal background thread and will call listener methods on the thread that initially calls the send method. +The `Client` class will automatically dispatch the network call on an internal background thread, and will call listener methods on the thread that initially calls the send method. ### Theme customization If you wish to customize the elements on the `CreditCardActivity` in order to @@ -458,7 +458,9 @@ style.xml ``` ## Authorizing Payment -Some payment methods require the customer to authorize the payment via an authorization URL. This includes the [3-D Secure verification](https://www.omise.co/fraud-protection#3-d-secure), [Internet Banking payment](https://www.omise.co/offsite-payment), [Alipay](https://www.omise.co/alipay), etc. Omise Android SDK provides a built in class to handle the authorization. +Some payment methods require the customer to authorize the payment via an authorization URL. This includes the [3-D Secure verification](https://docs.opn.ooo/fraud-protection#3-d-secure), [Internet Banking payment](https://docs.opn.ooo/internet-banking), [Alipay](https://docs.opn.ooo/alipay), etc. Opn Payments Android SDK provides a built in class to handle the authorization. + +On payment methods that require opening the external app (e.g. mobile banking app) to authorize the transaction, set the *return_uri* to a **deeplink** or **applink** to be able to open the merchant app. Else, after the card holder completes authorizing the transaction on the external app, the flow redirects to the normal link in the *return_uri* and opens it on the browser app, and therefore results in the payment not being completed. ### Authorizing Payment activity @@ -472,7 +474,7 @@ file as follows: android:theme="@style/OmiseTheme" /> ``` -Then in your activity, declare the method that will start this activity this way: +Then in your activity, declare the method that will start this activity as follows: ```kotlin private fun showAuthorizingPaymentForm() { @@ -486,7 +488,7 @@ private fun showAuthorizingPaymentForm() { Replace the string `AUTHORIZED_URL` with the authorized URL that comes with the created charge and the array of string `EXPECTED_URL_PATTERNS` with the expected pattern of redirected URLs array. After the end-user completes the authorizing payment process, the activity result -callback will be called, handle it in this manner: +callback will be called. Handle it in this manner: ```kotlin override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -500,7 +502,7 @@ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) ### Authorizing Payment via an external app -Some request methods allow the user to authorize the payment with an external app, for example Alipay. When a user would like to authorize the payment with an external app, `AuthorizingPaymentActivity` will automatically open an external app. However merchant developers must handle the `Intent` callback by themselves. +Some request methods allow the user to authorize the payment with an external app, for example Alipay. When a user needs to authorize the payment with an external app, `AuthorizingPaymentActivity` will automatically open an external app. However merchant developers must handle the `Intent` callback by themselves. ### 3D Secure 2 @@ -508,7 +510,7 @@ To support 3D Secure 2, you can check out the [3D Secure guide](docs/3d-secure-v ## ProGuard Rules -If you enable ProGuard, then add this rules in your ProGuard file. +If you enable ProGuard, then add these rules in your ProGuard file. ```ProGuard -dontwarn okio.** From cdcbb0e31f7b368318d043e66f70df9132fe34de Mon Sep 17 00:00:00 2001 From: Anas Naouchi <113893333+AnasNaouchi@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:31:28 +0700 Subject: [PATCH 3/5] Remove instrumentation tests from CI workflow and generate test coverage from CI workflow (#284) * wip * Add new task to gradle * Add coverage and caching to ci * Remove instrumentation files from being included in report * Generate report by excluding instrumentation test execution * Remove sonar instrumentation tests path * Remove redundant command * Explicit file upload * Change to correct coverage path * Remove explicit upload and combine commands * Add missing env --------- Co-authored-by: Daniel Fowler --- .github/workflows/ci.yml | 91 ++++++++++------------------------ .github/workflows/coverage.yml | 58 ---------------------- sdk/build.gradle | 35 ++++++++++++- 3 files changed, 58 insertions(+), 126 deletions(-) delete mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f95afdec..a5b3d7aeb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,3 @@ -# This workflow will build and test the project with Gradle -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle - name: Android CI on: push @@ -12,50 +9,35 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v3 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '17' + + - name: Cache Gradle dependencies + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Build project run: ./gradlew sdk:build -x test unit-test: - name: Unit test + name: Unit tests with coverage report runs-on: ubuntu-latest needs: build steps: - name: Checkout code uses: actions/checkout@v3 - - - name: Set up JDK 17 - uses: actions/setup-java@v3 with: - distribution: 'zulu' - java-version: '17' - - - name: Run unit tests - run: | - ./gradlew sdk:testProductionDebugUnitTest - - - name: Upload test report - if: always() - uses: actions/upload-artifact@v2 - with: - name: test-report - path: sdk/build/reports - - instrumentation-test: - name: Instrumentation test - # Due to Android emulator issue on Ubuntu, we use macOS instead. - runs-on: macos-latest - needs: build - strategy: - matrix: - api-level: [ 29 ] - steps: - - name: Checkout code - uses: actions/checkout@v3 + fetch-depth: 0 # required for SonarCloud - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -63,42 +45,19 @@ jobs: distribution: 'zulu' java-version: '17' - - name: Gradle cache - uses: gradle/gradle-build-action@v2 - - - name: AVD cache + - name: Cache Gradle dependencies uses: actions/cache@v3 - id: avd-cache with: path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ matrix.api-level }} - - - name: Create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: false - profile: Nexus 6 - script: echo "Generated AVD snapshot for caching." + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- - - name: Run instrumentation tests - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - disable-animations: true - force-avd-creation: false - profile: Nexus 6 - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - script: ./gradlew sdk:connectedProductionDebugAndroidTest + - name: Run unit tests + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + # Switch to jacocoTestReport when instrumentation flakiness is fixed + run: ./gradlew sdk:jacocoUnitTestReport sdk:sonar - - name: Upload test report - if: always() - uses: actions/upload-artifact@v2 - with: - name: test-report - path: sdk/build/reports diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 2e4e54e0a..000000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Code Coverage - -on: push - -jobs: - coverage-report: - name: Coverage Report - # Due to Android emulator issue on Ubuntu, we use macOS instead. - runs-on: macos-latest - strategy: - matrix: - api-level: [ 29 ] - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 # required for SonarCloud - - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '17' - - - name: Gradle cache - uses: gradle/gradle-build-action@v2 - - - name: AVD cache - uses: actions/cache@v3 - id: avd-cache - with: - path: | - ~/.android/avd/* - ~/.android/adb* - key: avd-${{ matrix.api-level }} - - - name: Create AVD and generate snapshot for caching - if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@v2 - with: - api-level: ${{ matrix.api-level }} - force-avd-creation: false - emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - disable-animations: false - profile: Nexus 6 - script: echo "Generated AVD snapshot for caching." - - - name: Coverage report - uses: reactivecircus/android-emulator-runner@v2 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - with: - api-level: ${{ matrix.api-level }} - disable-animations: true - force-avd-creation: false - profile: Nexus 6 - emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none - script: ./gradlew sdk:jacocoTestReport sdk:sonar diff --git a/sdk/build.gradle b/sdk/build.gradle index 3f7c07adf..5343c1606 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -215,7 +215,8 @@ repositories { mavenCentral() maven { url 'https://jitpack.io' } } - +// Use locally to generate full report +// TODO: Fix flaky tests so that this can be used in CI to reflect full coverage tasks.create(name: 'jacocoTestReport', type: JacocoReport, dependsOn: ['testProductionDebugUnitTest', 'lint', 'createProductionDebugCoverageReport']) { reports { xml.required = true @@ -244,6 +245,35 @@ tasks.create(name: 'jacocoTestReport', type: JacocoReport, dependsOn: ['testProd '**/*.ec' ]) } +// Use in CI to coverage generate report without instrumentation testing +tasks.create(name: 'jacocoUnitTestReport', type: JacocoReport, dependsOn: ['testProductionDebugUnitTest', 'lint']) { + reports { + xml.required = true + html.required = true + } + + // Main source code + sourceDirectories.from = file("${project.projectDir}/src/main/java") + classDirectories.from = files(fileTree( + // Generated Kotlin classes + dir: "${project.buildDir}/tmp/kotlin-classes/productionDebug", + // Exclude generated code + excludes: [ + '**/R.class', + '**/R$*.class', + '**/BuildConfig.*', + '**/Manifest*.*', + '**/*Test*.*', + 'android/**/*.*' + ] + )) + executionData.from = fileTree(dir: project.buildDir, includes: [ + // Execution data for unit tests + '**/*.exec', + // Execution data for instrumentation tests + // '**/*.ec' // Removed to exclude instrumentation tests + ]) +} sonar { properties { @@ -251,7 +281,8 @@ sonar { property 'sonar.organization', 'omise' property 'sonar.projectName', 'omise-android' property 'sonar.projectKey', 'omise_omise-android' - property 'sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/coverage/androidTest/production/debug/connected/report.xml" + // TODO: Change to the correct path once the instrumentation tests are not flaky + property 'sonar.coverage.jacoco.xmlReportPaths', "${project.buildDir}/reports/jacoco/jacocoUnitTestReport/jacocoUnitTestReport.xml" property 'sonar.junit.reportPaths', "${project.buildDir}/test-results/testProductionDebugUnitTest" property 'sonar.androidLint.reportPaths', "${project.buildDir}/reports/lint-results-productionDebug.xml" } From 356a79e53e9cafaa25b9494027a05bc6abfe29be Mon Sep 17 00:00:00 2001 From: Anas Naouchi <113893333+AnasNaouchi@users.noreply.github.com> Date: Tue, 7 Nov 2023 10:48:34 +0700 Subject: [PATCH 4/5] Fix crash on special characters input in expiry date (#283) * Fix crash on special characters input * Extract logic to private function & format * Fixed failing unit tests * Ignore flaky test * Test adding delay to UI tests * Revert "Test adding delay to UI tests" This reverts commit 2e15d2d422657b736e9351a12db9f1aed7586431. * Add test to cover new cases * Remove nullability for sequence in onTextChanged * Restrict change to keyboard type --- .../java/co/omise/android/ui/ExpiryDateEditText.kt | 5 ++--- .../co/omise/android/ui/CreditCardActivityTest.kt | 12 ++++++------ .../co/omise/android/ui/ExpiryDateEditTextTest.kt | 9 +++++++++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/sdk/src/main/java/co/omise/android/ui/ExpiryDateEditText.kt b/sdk/src/main/java/co/omise/android/ui/ExpiryDateEditText.kt index 60585e501..c3b8f719a 100644 --- a/sdk/src/main/java/co/omise/android/ui/ExpiryDateEditText.kt +++ b/sdk/src/main/java/co/omise/android/ui/ExpiryDateEditText.kt @@ -37,7 +37,7 @@ class ExpiryDateEditText : OmiseEditText { addTextChangedListener(textWatcher) disableOptions() filters = arrayOf(InputFilter.LengthFilter(MAX_CHARS)) - inputType = InputType.TYPE_CLASS_PHONE + inputType = InputType.TYPE_CLASS_NUMBER } override fun onSelectionChanged(selStart: Int, selEnd: Int) { @@ -70,8 +70,7 @@ class ExpiryDateEditText : OmiseEditText { beforeChangedText = s.toString() } - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - if (s == null || s.length > MAX_CHARS) return + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { // On deleting if (s.length < beforeChangedText.length) { diff --git a/sdk/src/sharedTest/java/co/omise/android/ui/CreditCardActivityTest.kt b/sdk/src/sharedTest/java/co/omise/android/ui/CreditCardActivityTest.kt index 21f40a3af..e0d610116 100644 --- a/sdk/src/sharedTest/java/co/omise/android/ui/CreditCardActivityTest.kt +++ b/sdk/src/sharedTest/java/co/omise/android/ui/CreditCardActivityTest.kt @@ -102,7 +102,7 @@ class CreditCardActivityTest { fun form_validForm() { onView(withId(R.id.edit_card_number)).perform(typeText("4242424242424242")) onView(withId(R.id.edit_card_name)).perform(typeText("John Doe")) - onView(withId(R.id.edit_expiry_date)).perform(typeText("1234")) + onView(withId(R.id.edit_expiry_date)).perform(typeNumberText("1234")) onView(withId(R.id.edit_security_code)).perform(typeNumberText("123"), pressImeActionButton()) onView(withId(R.id.edit_card_number)).check(matches(withText("4242 4242 4242 4242"))) @@ -118,7 +118,7 @@ class CreditCardActivityTest { fun form_validBillingAddressForm() { onView(withId(R.id.edit_card_number)).perform(typeText("4242424242424242")) onView(withId(R.id.edit_card_name)).perform(typeText("John Doe")) - onView(withId(R.id.edit_expiry_date)).perform(typeText("1234")) + onView(withId(R.id.edit_expiry_date)).perform(typeNumberText("1234")) onView(withId(R.id.edit_security_code)).perform(typeNumberText("123")) onView(withId(R.id.edit_country)).perform(scrollTo(), click()) @@ -148,7 +148,7 @@ class CreditCardActivityTest { fun form_invalidForm() { onView(withId(R.id.edit_card_number)).perform(typeText("1234567890")) onView(withId(R.id.edit_card_name)).perform(typeText("John Doe")) - onView(withId(R.id.edit_expiry_date)).perform(typeText("1234")) + onView(withId(R.id.edit_expiry_date)).perform(typeNumberText("1234")) onView(withId(R.id.edit_security_code)).perform(typeNumberText("123"), pressImeActionButton()) onView(withId(R.id.edit_card_number)).check(matches(withText("1234 5678 90"))) @@ -298,7 +298,7 @@ class CreditCardActivityTest { whenever(mockClient.send(any(), any())).doAnswer {} onView(withId(R.id.edit_card_number)).perform(typeText("4242424242424242")) onView(withId(R.id.edit_card_name)).perform(typeText("John Doe")) - onView(withId(R.id.edit_expiry_date)).perform(typeText("1234")) + onView(withId(R.id.edit_expiry_date)).perform(typeNumberText("1234")) onView(withId(R.id.edit_security_code)).perform(typeNumberText("123"), closeSoftKeyboard()) onView(withId(R.id.button_submit)).perform(scrollTo(), click()) @@ -319,7 +319,7 @@ class CreditCardActivityTest { onView(withId(R.id.edit_card_number)).perform(typeText("4242424242424242")) onView(withId(R.id.edit_card_name)).perform(typeText("John Doe")) - onView(withId(R.id.edit_expiry_date)).perform(typeText("1234")) + onView(withId(R.id.edit_expiry_date)).perform(typeNumberText("1234")) onView(withId(R.id.edit_security_code)).perform(typeNumberText("123"), closeSoftKeyboard()) onView(withId(R.id.button_submit)).perform(scrollTo(), click()) @@ -346,7 +346,7 @@ class CreditCardActivityTest { onView(withId(R.id.edit_card_number)).perform(typeText("4242424242424242")) onView(withId(R.id.edit_card_name)).perform(typeText("John Doe")) - onView(withId(R.id.edit_expiry_date)).perform(typeText("1234")) + onView(withId(R.id.edit_expiry_date)).perform(typeNumberText("1234")) onView(withId(R.id.edit_security_code)).perform(typeNumberText("123"), closeSoftKeyboard()) onView(withId(R.id.edit_country)).perform(scrollTo(), click()) onView(withId(R.id.country_list)) diff --git a/sdk/src/test/java/co/omise/android/ui/ExpiryDateEditTextTest.kt b/sdk/src/test/java/co/omise/android/ui/ExpiryDateEditTextTest.kt index 8c91df2ba..56696ce88 100644 --- a/sdk/src/test/java/co/omise/android/ui/ExpiryDateEditTextTest.kt +++ b/sdk/src/test/java/co/omise/android/ui/ExpiryDateEditTextTest.kt @@ -48,6 +48,15 @@ class ExpiryDateEditTextTest { assertEquals(Unit, editText.validate()) } + @Test + fun validate_ignoreSpecialCharactersInput() { + "*,.".forEach { editText.append(it.toString()) } + + assertEquals("", editText.text.toString()) + "1234".forEach { editText.append(it.toString()) } // 12/34 + assertEquals("12/34", editText.text.toString()) + } + @Test(expected = InputValidationException.EmptyInputException::class) fun validate_emptyValue() { "".forEach { editText.append(it.toString()) } From ee96e520449ea72eabc7e8e34af01e735f1c97c2 Mon Sep 17 00:00:00 2001 From: Anas Naouchi <113893333+AnasNaouchi@users.noreply.github.com> Date: Wed, 8 Nov 2023 14:45:25 +0700 Subject: [PATCH 5/5] Add new OCBC logo (#285) --- .../drawable-hdpi/payment_ocbc_digital.png | Bin 3349 -> 2924 bytes .../drawable-mdpi/payment_ocbc_digital.png | Bin 2128 -> 1866 bytes .../drawable-xhdpi/payment_ocbc_digital.png | Bin 4583 -> 3834 bytes .../drawable-xxhdpi/payment_ocbc_digital.png | Bin 7866 -> 5902 bytes .../drawable-xxxhdpi/payment_ocbc_digital.png | Bin 10598 -> 7939 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/sdk/src/main/res/drawable-hdpi/payment_ocbc_digital.png b/sdk/src/main/res/drawable-hdpi/payment_ocbc_digital.png index edde059496b15173cff82f753ecf158032feab32..dbc5f2b792906cb8c3e77a522278412f7ed91514 100644 GIT binary patch delta 2912 zcmV-m3!n6r8tfL3DSrXk$t-^W000SaNLh0L01FcU01FcV0GgZ_000XSNklw=&h#gRpX z6)a%|i*=V(kySyiQV4-S2)Sl5lQ}bY=KQw5e)HydnWHE3hJW3z`d2b<-h17@{=WYD z>wa(I;=+{LHi=0@YluE4Dj`x5f!L~P)19iibplmY>uzp!g-W*`x9Z|nXQ-g+1dlk` z$!9tF?r>$RF8#tWg@Vl`DkpNq60)XELNh@-LU~JG9pE8Vd%VU;?!TX?N2I`~o-}Bs z2UG~znYX47XnzMk&<9lQpsYnZ`n|h86VVD5*p5V%u>$tDW+>230YM+S^*{hx)}k%k zr)oomb@|l2H5{}w7Qnn1$7evdp;DE z8|e1#2jgdF!2I>MU_A2$=nfZy=A9iNTe<=&`}7a$wtom23KVshswF)pXamNj0wM5V zu|ZXZ&x85wd2ks`5J|CD*MR2z-B6yK78JBJ-WRkbRY`mckM6|9Q;!j}Nk^363w>8C zSb6?baMaXB1RAVp_8owSY3Y8}3

7jsr#Aj*FwhV}h}b+QkEBifZMyNnpKpQxs?{ zPNNazn}1)2(zfjdpz%#Wg+lKL(8g{ET^ucF{e&4{Jah~s9aHFMX=)bOO7DvXj>TG0 z0guM!hlC6ypDB#jGK8*)23WK9ci^(wXv553eg(DddT`*qv%slPiVE(sTA`}2Fd}Hw zWB+J=0->$V1=jw4Qmj%Lg;3tnX2bjk*;?I zY?Sfv#~yIiqUA9GuPi8t*y1=Hk~Y=FHP_0J_vy zSEChlZ|~$RN!(cb%4_tPu(%9lp3R-ec7M__`duGI1Z}ISi7j-)*0-Bhdan!=vlnp| z%NYZfi{E&@v;K4g>N1B$K5XVc3oetH8GpRGs3>;O_QzF0D{lPmdytQuz%_bd5tu$X z1(L44z0rR03NW2GO}&MsuKMxQL&gi=Vz_iEHqiA?&kkQZnZoEi?M_hWb>p4^>#bir zr`T@ag}U^?Om*VQjJ1>|C5aC$8<-1*gT>&GNJDdztO>I*{<^xp!@%;-fBT(duYanh z$64wP6(oI2?V2^Qi{2Qt6f+ip`R`u_tehq$+skx%@{P(Eu+(c_0?UPeaakCVLnilY6fQp0 zE7o!yxby|`Sp^Ph9aG(gJ^PugEh;p+YM^4fdk^$Gchg|ycRm@@GlblA*SJPf#se1GZmEn4#6n*D}>#p43a^K*qSM ziPjCYxj;LG1T+1g_dxxmS%2KrH*+CdAlrK&Y`vJ{<3=FvqlpI1y60seV;Hx8!xg8t z0ZgBM&Qv$c64ZM2Iz1O3B!~uWBuhk=;xZaRLFQ_v!rLa_kW}JCHKb;m3HXa1YWyw3llIFP3yrX(V&1a8u-I5^ zxPJ2+VH;rm<}#Q_gr<6L59y3Ov_q(92kizmu`<^IzVOMkv$4}zqP1_qM@fM^1s?)N zW@$G#T8dgOsV^vcXMN`uVlR6L3X<$l58$ouw!Hee7k|J}9ocg*)2TB-<3TWM zhmYQ*6QrF|XakjGrKDfC0c=;U5nTt{RR&$*MXve&DyZ|OfP%z_`X2pAM`nV8EIoA_ zHq)hMa|*vf8BF+?H|q5?T143^3)FoEgE}2Ggj0r(_+$7zp4TS_RK&U}l8dXyOa>iE zNzu)1jFCB5(SL2<8MeLn#zXX*2t>H}cT`qK)!=B0Im^ev%_SF9`piww) znm;*3^WkU;UT^%%M_ekK(1&J+m=;rO2g%j{XVAQGezv~y8aU4V16(Hd*Q%^vTmb#5 z^)weyBoCmOAZBl}ZfTsgNv0UBwPOa2G7K39mb_=cGG+=iE`JfM@9qNI!D6r-I11MN zN5H!8Fn?Iyd>2fs)`5On0ceSrXflS<4#Px7nKF>e-l(^ph!DB&}Jq8*Ejsk7}k)Y{2oMv;Xv>Z^Tx~<(qXzl36 zHbU3M61-tRE?A$MLBOYjWvmcb6Y!GU~TuBv&Ey z%~(RCRcsSxgLV8&YV{`I!wC40e9&j-fi7z_=_I$cGX&PAV>_KLb?H(RcUGI{;f(D5x=XB~QLaao;5>AUxcV$Q#vMnFgR`O%Ors`)aRdgnr?{XtXf%xjd?at;;t5_C zZzgmm|5EJUSVJ3@tOQrtLvTGP2ivcTz<+V(JUB_}YFV(1OGczJ0-f)Ti{i~6 z*LQqheNG@U#uB$ZKY;W6S70aewM(M~^Q5_8o=T3_$>_X}F5bYqxF?GH zjO*?`;~LXac4&MvGDrBLdtof0?Zm<^vPuG_8XSMx1=bmh!1>b+PbTJk^w>RpaeqKo)_5oi>WDy%0 zt@i|>aSRtT0sDX_6CEV7iv_JA09kfs=&0Nqf0HsiB47c0ccSNOMfj~H&bRi-^T0N7 zwr5P^3e1%IwEvjKbX1t2&>UP4$hfQwe`bikwyUh=vdipnO#cNy?xIp4yDr550000< KMNUMnLSTYo0E|Ka literal 3349 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 literal 2128 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 literal 4583 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 diff --git a/sdk/src/main/res/drawable-xxhdpi/payment_ocbc_digital.png b/sdk/src/main/res/drawable-xxhdpi/payment_ocbc_digital.png index a81a58701400c66688a3b0b86996c3ccafcc4b00..d1b4d9549f4d2950d6845124dabebbdccda93075 100644 GIT binary patch literal 5902 zcmV+p7xCzcP)715C^NBt+8WL-udO|&3zcCd78d4*P8e7TxJ|y?q+(@tsi4GD4jD}^rmuAU; zHdO%HLB$|p(f*{>QIkH=v*qi7fD;T6J4M; z+#N;(OB`r((}Z3nFO@_6?My)p=x4~D>3Ir3n`sKYY@nVigW4cM zM~FgUT1hVxHDU#KS`nWM42P!`Fk3HWlTWomJ#ywbq7D;bJ5<_wfG9gvxH#kq8VxN` zz-G*+($E_Tv_S+=UT2FsepZuM?LA4 z$vpHPQ2@$At*-#}tSI4YHMD=dR=DpP4Jr{;O}+{o>t6-mk)y!M+&}Gw>VG^6rv?sV znonbSsZY@niksaH=F z)(jf@=@~V+(~JhFq~fd`sJ(qL_)E(S11qh%H7&f7@rGP&%8e%-zRzG7j$GBE@*Il|nl?tyFV+vEyZBoE4qX8B*XbE+X ztc2P-?ooY>=j-o{W*FD(+qc7^7A=`Wl?&5BoP7L^0Jpo*a7$Wt0QY~ki=9-v@LuKD zUC^0MQkdPm1qJY1UY>4n1z`mrqhE2Fxd{SZZY12&&V3XoDXOo#N%8e%c>^FohAU>b zfZYx!#*fz>GLFMz^zUQ@yvr!K6(c98!wLkTdd}Yz-&a255(wHI=_Qt=70sQiJ7k=I zQR(A{5pbmuaH}q#0YSGXZUP)bp*9*r-j?^9Uq3FVE9|BJ6ns51lQ4n;w6YeB6$c|Uu)By>5 z3)pOMG(TT=$aG=i>}Ep$l+;Tn+>-20;IF7mx|8Z2d`$7Wt`9z8h(bT}78b(6rcHH+ zjKbwk(l?H@1NQ#7H>FOhdw6Bkb&H$jf^YvpMi}(9+Kn620vLs}YH0;bVKi+6wx^y= zsgtbt{UhqSW!?Hipw7k!gT8ip_UyDlPCH;hl@q6f|8!B(omBI;g;Ce6nquCC38@7f z4mi@W;~xhw4v7@KIV%V19(o*t9&b_|RWpA<^k-dh4gEiVaS3?7`UV^?y#dw-9;Iu^ zmcPuQTY4;4&!+Fi!p-@wcY!;0f+asqR`>D8e++_?CPNQ=>`A(07!@i9i~`?*UlremGguhb^@b~`diE^|O|K?m zC&rG=Fu)|oCOQNP)YK~G(&F>lg5%{kNLlO`Uu%E*xwL_ms%p}eNmu-_%(`VuW&y^{ z=*gPr^RJ-1&+uq(?mY~+69r&@ZiDLQmbB_fhh3OAxs!!ZdSOrIZNt2*Wfd$@JDih~ zIe;Cny_H&PNhNK6{w2DDL+fYa%94DI7>hMMAlELsKehHLSm~lg83MRrHVwBd41r-K z9xK;kk>=U?o$58HCwJ_{wv8kOdB6XGNgD;T@7R$UfJ<__0asASwDC9_rS^I5ZJ)<| zKi&^($YtFJLeS@9*2W;IF%C9vY<|F0=eB~HTkiz-*3X#aE6a>oF(XFx^*6^|W7Y$* z?VqVFn#iqS>eLJYED=7^X-o#dp{(a%X6>@CThD?mHODvKWG>(c4;kIqf3%Gpnm~aPvv6B`*n!g;) za_K&3>2jTX^E)o*#WXU0#s|Zt#Z*a~uHgCd>uA8_{#dm;)g3BQJ2`Qpc>!a!8`b9y zC-p?jwR54kd3(**vafxf&P+-F&GYRpO`U{K{^NdX{L;|(k|GVCF_xYPz!I?Ks_U5n zF23a!^8&72uq18+(3>!fm*o#h=-L?mrSs8R48wEHyzs6Mtf1YWZsYHRAz&O6v$C=! zOUw%x=YbP3@9sVH+pp!?C9S)FYs<$;bsECN^dWj2smz`4ZeiAL6f3J-vBJE7Z7bF! z)hHUqNr&Up{PDs#7E<;XUP)>?6jMcMhkT}B1^G)#lWM!%s@JbKF% z$NoGre8QC-Tsrg@6>GmZ$MFjpr1)alcHDfx!mOJ%nHcb@wM-g`hGSLfwXveEobYGm z&px*mSg!mlKlatQrwiAsnF3bXn2@NkUK?hcgCAMRq>+M2ukHRX|A08IFyX;xmE)%< z-a~1p-gNU`yi<8_10c(``^zhsw7Kpbwog7V1&sTQSsKEU1C|=jZ#!Opn|il=NQGXF z&mZHLPU(3l95S(NJ1etqc-6dsaY(|_FzgD~)>1*cKK_(1)Ut9S?hEIu@pL@bM_a`u zK9(L_+BT0_oAC>Y=VF=?Fg}jL(y(X~uAS&E5uA#|$&T{=7f0M9t^m<(Jtqs9wHt?B ztPGb~@tXMHnCSrHIh!fWcy2VTBq5&&o0oe84hs^E=O*$-;3V(kheSoE4Lr_Z^UBHWs zH&NWl0;UZ|3pyOYG0g8l1g!8Ft5&f-O#qKt64$R4bR@}wOFLjSu#LlDF^r;WX5Iky z<*UK|xKwL+tx%SO>M0fR|F8Jo$EA{d-f}^!z=nEE(34a+>n22>L_y<&a)HV!rhsv# zF4kw_>9V>3jtMpdwX|studG}sLp5v5>pWCDs`B$u?Zax0x-fA`uR$bSpGx>#;thB& zrCB-Qdn5NLd>Q$u!?!0<(5yoN0y;FYqShz)s4Hv3^>BQiIv(I;hE2+VW#Kp?$3}pacx!~G zwg(?6`BOF9qWhqtRVRr$@@grS6nZppxo5`$Ekr0aZ^o@gdYI$Ye`!t+_!~~C0agsh zvB1hbmG9u1t7kK5yzP#ApfbC&Or4-o4n7LF06P)T7lc6z-W=)|$A;ILv`5JjnLDa=w4MoBV+-z1ajr@OTl;W zux|H;r`RV_;GvEs;KlKaEGcw^O(2V4g}f@}(NG9&#e0}_*3G(gj8?;H*D`Q)q*u#9 zOLKGxa%t->;CSH`-S4kr)Wn1VCz6Ykp6Ed6*b&%46s`bP2%{Htq2d^AWl7d*#!uzL zxDdi+prxT(fLup%e>zT;cW?h9slMkcYHe%A%;uue!_m931FOilzp8atsA|&{Z1J8( z(9vwSE#|*yp}ZXi9m>;^99_}1FC9O#dU#!nDIHazRa(czx;%V_u**mYtR#>vITu0Q zjO%$_g_=!eyE;XMu0thUf5t4RyK)v-iE1zXGyQ_$7p<~&7gf-;*USO;#`n?&RWRJS zk!nBVQT~PimI^~&6)AA4+jS@E!K*#0XIK~Zgc_=R74ff$cu)J#b%blS?G6^IJh0@V zE`q8YR5$wh2)df&Vyp|CtDgby_AepmX7_AwB8HE@vglj%CJ3&K2y03twk5Y#IH zMZyn*55uk!J{+aUYWvmVJE4Z8Gc}j7)v1v}!h5@7d(v*M&;fZ3#cA7N!-~f-!MS8{+PszGXk(6OTUr8z54+v zQ`!G!LR{lG@Vx&K)Qp=#*OmD-Ss|!U@-^UE@(?)ZEd=|tzk;<(KN2DOLT#tsP}8v&zo7s(6SV8W?-t1o?GO>YxHG@m zu!S^Ko6Czc5-`fzwLgJ8J_@vJpa6L^@$^yD+npr5I{J(NNAKZa?==kU`9s0pa|qbF z4+dM_AgH@&Ak=jmK(5)3K<-P^0EOH^fZT&_Iig#Es26vm8w+E4aYRLJ+_*Sn0i#zS zSkKV$GSCulM#%kDAom;!wjMm>x)5Y*xBfh2>cxGC7x$)Hgy^;)(Tnrqc(JUamX3=J z0_-vxRcx?{II^OS92c9Q*xHYP|7|p+ zRE5BNBPNQVqeHeNCu8BInvX-Fx|IUF$y$gKv z?*z~2$>15xLzd=a*O)(n_pw#LeeenRckcmi$5-Hf@G-Ct9VhYPP%f5M)NzX~l`*j~ z)$|Gk+`(u#EQFrLjk^MZ`wm1-O$CGC|Ic=C4;?4wW8V{N0ldl^3*3)9p^%HC$Hj^h zABtgGl7}0oJrC#BLnD7EFd9&*!cOx)^_&PQ@X3F{zxhK5*3?2^=eJ@$_Re1vW?y9i z&)v(wJ?B<%-~R~sfB6;cadfezEleAdhaL3G2 ztEQDEJ(Om(l*YsQ0zOtxZsZ@k5(SNhl&YMhk`8_?`N?+@ihZP_MlbgMWv=+ozGcg4 zMeSYsXhf04eYiC;OeG4pV|@o-uSt%*^9Ly%uK8c?dj4)>Fs>nFui@KjOAUA^F& zG84R$!e>tVH*TST-Q%wS&rP?3cl9$0fCtIur(@;eBnmBk`e2_T+z{YaL^h)#r6TM= zKgqQmfy_+~9~jEr|0oU9!IOoeXM1MO5hp%8H}gLz!hN(gb{>wJ)rwtcDNlIlR!Ikn zgrQLk$Tt}cDOurRWsaBL0FH#+z=C@rux>**B+pw&!?cfhc6clmguoAb!S~{8G!I7t zmgZsEtd?q_r8MD@R=}%b!j(YYY&5K7dN{ur!9BY{@pU5Ld^uoQ zp`|q8Aym>zGg?aP!|Vb7rU`Bca3i9hj0Tpd@E1xgGj9O*)6YY2=XVfzYcu%oS_;0g zQ~1RQVUBvpIPhLS54iQO5xojNBG01x>4b-q{C@=Cyx5g=^o*9qp%PobHqCG)kXsTJ z8VxNOp*M+nc+_NZj#^wd4o!MQEI}1sK{twi3&p^0>)~Z8yy4iAe{lX&>gUcsAp1eC6LU3 zjf?be|T#g-6_oN7sdinPH;i zG|>~-w}ydLGIB!=iKY;(A^MicVH992h$!HTFPE$fM{dLux6?{C;e~fuhWAQGZNOtY kRml;R5PeQ`2Wuw(1A6wqo{``5%K!iX07*qoM6N<$f_d{g$N&HU literal 7866 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 literal 10598 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