Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge V2 Beta Branch into Main #293

Merged
merged 8 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,6 @@ jobs:
uses: ./.github/actions/unit_test_module
with:
module: PayPalWebPayments
unit_test_paypal_native:
name: PayPal Native Payments Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'microsoft'
- name: Run Unit Tests
uses: ./.github/actions/unit_test_module
with:
module: PayPalNativePayments
unit_test_fraud_protection:
name: Fraud Protection Unit Tests
runs-on: ubuntu-latest
Expand All @@ -112,7 +97,7 @@ jobs:
module: FraudProtection

unit_test_finished:
needs: [unit_test_card, unit_test_core, unit_test_paypal_web, unit_test_paypal_native, unit_test_fraud_protection]
needs: [unit_test_card, unit_test_core, unit_test_paypal_web, unit_test_fraud_protection]
name: All Unit Test finished
runs-on: ubuntu-latest
steps:
Expand Down
17 changes: 1 addition & 16 deletions .github/workflows/release_snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,21 +83,6 @@ jobs:
uses: ./.github/actions/unit_test_module
with:
module: PayPalWebPayments
unit_test_paypal_native:
name: PayPal Native Payments Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'microsoft'
- name: Run Unit Tests
uses: ./.github/actions/unit_test_module
with:
module: PayPalNativePayments
unit_test_fraud_protection:
name: Fraud Protection Unit Tests
runs-on: ubuntu-latest
Expand All @@ -115,7 +100,7 @@ jobs:
module: FraudProtection

unit_test_finished:
needs: [unit_test_card, unit_test_core, unit_test_paypal_web, unit_test_paypal_native, unit_test_fraud_protection]
needs: [unit_test_card, unit_test_core, unit_test_paypal_web, unit_test_fraud_protection]
name: All Unit Test finished
runs-on: ubuntu-latest
steps:
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/static_analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ jobs:
CorePayments/build/outputs/aar/CorePayments-release.aar
CardPayments/build/outputs/aar/CardPayments-release.aar
PayPalWebPayments/build/outputs/aar/PayPalWebPayments-release.aar
PayPalNativePayments/build/outputs/aar/PayPalNativePayments-release.aar
PaymentButtons/build/outputs/aar/PaymentButtons-release.aar
FraudProtection/build/outputs/aar/FraudProtection-release.aar

Expand All @@ -75,7 +74,6 @@ jobs:
CorePayments/build/outputs/aar/CorePayments-release.aar
CardPayments/build/outputs/aar/CardPayments-release.aar
PayPalWebPayments/build/outputs/aar/PayPalWebPayments-release.aar
PayPalNativePayments/build/outputs/aar/PayPalNativePayments-release.aar
PaymentButtons/build/outputs/aar/PaymentButtons-release.aar
FraudProtection/build/outputs/aar/FraudProtection-release.aar

Expand All @@ -97,8 +95,6 @@ jobs:
run: diffuse diff --aar main-aar/CorePayments/build/outputs/aar/CorePayments-release.aar current-aar/CorePayments/build/outputs/aar/CorePayments-release.aar
- name: Run Diffuse Analysis - PayPalWebPayments
run: diffuse diff --aar main-aar/PayPalWebPayments/build/outputs/aar/PayPalWebPayments-release.aar current-aar/PayPalWebPayments/build/outputs/aar/PayPalWebPayments-release.aar
- name: Run Diffuse Analysis - PayPalNativePayments
run: diffuse diff --aar main-aar/PayPalNativePayments/build/outputs/aar/PayPalNativePayments-release.aar current-aar/PayPalNativePayments/build/outputs/aar/PayPalNativePayments-release.aar
- name: Run Diffuse Analysis - PaymentButtons
run: diffuse diff --aar main-aar/PaymentButtons/build/outputs/aar/PaymentButtons-release.aar current-aar/PaymentButtons/build/outputs/aar/PaymentButtons-release.aar
- name: Run Diffuse Analysis - FraudProtection
Expand Down
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
# PayPal Android SDK Release Notes

# beta - unreleased
* Breaking Changes
* PayPalNativePayments
* Remove entire PayPalNativePayments module
* CardPayments
* Remove `CardClient(FragmentActivity, Config)` constructor
* Add `CardClient(Context, Config)` constructor
* Remove `CardClient.approveOrder(FragmentActivity, CardRequest)` method
* Add `CardClient.approveOrder(CardRequest)` method
* Remove `CardClient.presentAuthChallenge(FragmentActivity, CardAuthChallenge)` method
* Add `CardClient.presentAuthChallenge(ComponentActivity, CardAuthChallenge)` method
* Add `CardPresentAuthChallengeResult` type
* Add `CardClient.completeAuthChallenge(Intent, String)` method
* Add `CardStatus` type
* Add `ApproveOrderListener.onApproveOrderAuthorizationRequired(CardAuthChallenge)` method
* Add `CardVaultListener.onVaultAuthorizationRequired(CardAuthChallenge)` method
* Remove `authChallenge` property from `CardVaultResult`
* PayPalWebPayments
* Remove `PayPalWebCheckoutClient(FragmentActivity, CoreConfig, String)` constructor
* Add `PayPalWebCheckoutClient(Context, CoreConfig, String)` constructor
* Remove `PayPalWebCheckoutClient.start(PayPalWebCheckoutRequest)` method
* Add `PayPalWebCheckoutClient.start(ComponentActivity, PayPalWebCheckoutRequest)` method
* Remove `PayPalWebCheckoutClient.vault(PayPalWebVaultRequest)` method
* Add `PayPalWebCheckoutClient.vault(ComponentActivity, PayPalWebVaultRequest)` method
* Add `CardClient.completeAuthChallenge(Intent, String)` method
* Add `PayPalPresentAuthChallengeResult` type
* Add `PayPalWebStatus` type
* Gradle
* Update `browser-switch` version to `3.0.0-beta`

# unreleased
* Gradle
* Update Kotlin version to `1.9.24`
Expand Down
2 changes: 2 additions & 0 deletions CardPayments/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility modules.sourceCompatibility
targetCompatibility modules.targetCompatibility
}

kotlinOptions {
jvmTarget = modules.kotlinJvmTarget
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ interface ApproveOrderListener {
@MainThread
fun onApproveOrderSuccess(result: CardResult)

/**
* Called when authorization is required to continue.
*/
@MainThread
fun onApproveOrderAuthorizationRequired(authChallenge: CardAuthChallenge)

/**
* Called when the approval fails.
* @param error [PayPalSDKError] explaining the reason for failure.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,110 +1,84 @@
package com.paypal.android.cardpayments

import androidx.fragment.app.FragmentActivity
import android.content.Intent
import androidx.activity.ComponentActivity
import com.braintreepayments.api.BrowserSwitchClient
import com.braintreepayments.api.BrowserSwitchException
import com.braintreepayments.api.BrowserSwitchFinalResult
import com.braintreepayments.api.BrowserSwitchOptions
import com.braintreepayments.api.BrowserSwitchResult
import com.braintreepayments.api.BrowserSwitchStatus
import com.paypal.android.corepayments.PayPalSDKError
import com.braintreepayments.api.BrowserSwitchStartResult
import com.paypal.android.corepayments.BrowserSwitchRequestCodes
import org.json.JSONObject

internal class CardAuthLauncher(
private val browserSwitchClient: BrowserSwitchClient = BrowserSwitchClient(),
) {

companion object {
private const val METADATA_KEY_REQUEST_TYPE = "request_type"
private const val REQUEST_TYPE_APPROVE_ORDER = "approve_order"
private const val REQUEST_TYPE_VAULT = "vault"

private const val METADATA_KEY_ORDER_ID = "order_id"
private const val METADATA_KEY_SETUP_TOKEN_ID = "setup_token_id"
}

fun presentAuthChallenge(
activity: FragmentActivity,
activity: ComponentActivity,
authChallenge: CardAuthChallenge
): PayPalSDKError? {
): CardPresentAuthChallengeResult {
val metadata = when (authChallenge) {
is CardAuthChallenge.ApproveOrder -> {
val request = authChallenge.request
JSONObject()
.put(METADATA_KEY_REQUEST_TYPE, REQUEST_TYPE_APPROVE_ORDER)
.put(METADATA_KEY_ORDER_ID, request.orderId)
}

is CardAuthChallenge.Vault -> {
val request = authChallenge.request
JSONObject()
.put(METADATA_KEY_REQUEST_TYPE, REQUEST_TYPE_VAULT)
.put(METADATA_KEY_SETUP_TOKEN_ID, request.setupTokenId)
}
}

val requestCode = when (authChallenge) {
is CardAuthChallenge.ApproveOrder -> BrowserSwitchRequestCodes.CARD_APPROVE_ORDER
is CardAuthChallenge.Vault -> BrowserSwitchRequestCodes.CARD_VAULT
}

// launch the 3DS flow
val browserSwitchOptions = BrowserSwitchOptions()
.url(authChallenge.url)
.requestCode(requestCode)
.returnUrlScheme(authChallenge.returnUrlScheme)
.metadata(metadata)
return launchBrowserSwitch(activity, browserSwitchOptions)
}

private fun launchBrowserSwitch(
activity: FragmentActivity,
options: BrowserSwitchOptions
): PayPalSDKError? {
var error: PayPalSDKError? = null
try {
browserSwitchClient.start(activity, options)
} catch (e: BrowserSwitchException) {
error = CardError.browserSwitchError(e)
}
return error
}

fun deliverBrowserSwitchResult(activity: FragmentActivity) =
browserSwitchClient.deliverResult(activity)?.let { browserSwitchResult ->
val requestType =
browserSwitchResult.requestMetadata?.optString(METADATA_KEY_REQUEST_TYPE)
if (requestType == REQUEST_TYPE_VAULT) {
parseVaultResult(browserSwitchResult)
} else {
// Assume REQUEST_TYPE_APPROVE_ORDER
parseApproveOrderResult(browserSwitchResult)
return when (val startResult = browserSwitchClient.start(activity, browserSwitchOptions)) {
is BrowserSwitchStartResult.Started -> {
CardPresentAuthChallengeResult.Success(startResult.pendingRequest)
}
}

private fun parseVaultResult(browserSwitchResult: BrowserSwitchResult): CardStatus? {
val setupTokenId =
browserSwitchResult.requestMetadata?.optString(METADATA_KEY_SETUP_TOKEN_ID)
return when (browserSwitchResult.status) {
BrowserSwitchStatus.SUCCESS -> parseVaultSuccessResult(browserSwitchResult)
BrowserSwitchStatus.CANCELED -> CardStatus.VaultCanceled(setupTokenId)
else -> null
is BrowserSwitchStartResult.Failure -> {
val error = CardError.browserSwitchError(startResult.error)
CardPresentAuthChallengeResult.Failure(error)
}
}
}

private fun parseApproveOrderResult(browserSwitchResult: BrowserSwitchResult): CardStatus? {
val orderId = browserSwitchResult.requestMetadata?.optString(METADATA_KEY_ORDER_ID)
return if (orderId == null) {
CardStatus.ApproveOrderError(CardError.unknownError, orderId)
} else {
when (browserSwitchResult.status) {
BrowserSwitchStatus.SUCCESS ->
parseApproveOrderSuccessResult(browserSwitchResult, orderId)
fun completeAuthRequest(intent: Intent, authState: String): CardStatus =
when (val finalResult = browserSwitchClient.completeRequest(intent, authState)) {
is BrowserSwitchFinalResult.Success -> parseBrowserSwitchSuccessResult(finalResult)
is BrowserSwitchFinalResult.Failure -> CardStatus.UnknownError(finalResult.error)
BrowserSwitchFinalResult.NoResult -> CardStatus.NoResult
}

BrowserSwitchStatus.CANCELED -> CardStatus.ApproveOrderCanceled(orderId)
else -> null
}
private fun parseBrowserSwitchSuccessResult(result: BrowserSwitchFinalResult.Success): CardStatus =
when (result.requestCode) {
BrowserSwitchRequestCodes.CARD_APPROVE_ORDER -> parseApproveOrderSuccessResult(result)
BrowserSwitchRequestCodes.CARD_VAULT -> parseVaultSuccessResult(result)
else -> CardStatus.NoResult
}
}

private fun parseVaultSuccessResult(browserSwitchResult: BrowserSwitchResult): CardStatus {
val deepLinkUrl = browserSwitchResult.deepLinkUrl
val requestMetadata = browserSwitchResult.requestMetadata
private fun parseVaultSuccessResult(finalResult: BrowserSwitchFinalResult.Success): CardStatus {
val deepLinkUrl = finalResult.returnUrl
val requestMetadata = finalResult.requestMetadata

return if (deepLinkUrl == null || requestMetadata == null) {
return if (requestMetadata == null) {
CardStatus.VaultError(CardError.unknownError)
} else {
// TODO: see if there's a way that we can require the merchant to make their
Expand All @@ -113,7 +87,7 @@ internal class CardAuthLauncher(
// NOTE: this assumes that when the merchant created a setup token, they used a
// return_url with word "success" in it (or a cancel_url with the word "cancel" in it)
val setupTokenId =
browserSwitchResult.requestMetadata?.optString(METADATA_KEY_SETUP_TOKEN_ID)
finalResult.requestMetadata?.optString(METADATA_KEY_SETUP_TOKEN_ID)
val deepLinkUrlString = deepLinkUrl.toString()
val didSucceed = deepLinkUrlString.contains("success")
if (didSucceed) {
Expand All @@ -131,12 +105,14 @@ internal class CardAuthLauncher(
}

private fun parseApproveOrderSuccessResult(
browserSwitchResult: BrowserSwitchResult,
orderId: String
finalResult: BrowserSwitchFinalResult.Success,
): CardStatus {
val deepLinkUrl = browserSwitchResult.deepLinkUrl
val deepLinkUrl = finalResult.returnUrl
val orderId = finalResult.requestMetadata?.optString(METADATA_KEY_ORDER_ID)

return if (deepLinkUrl == null || deepLinkUrl.getQueryParameter("error") != null) {
return if (orderId == null) {
CardStatus.ApproveOrderError(CardError.unknownError, null)
} else if (deepLinkUrl.getQueryParameter("error") != null) {
CardStatus.ApproveOrderError(CardError.threeDSVerificationError, orderId)
} else {
val state = deepLinkUrl.getQueryParameter("state")
Expand Down
Loading