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 1/2] 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 2/2] 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" }