Skip to content

Commit 45a4537

Browse files
committed
⏺ Add callback-based start function with PayPalWebCheckoutCallback interface
- Create PayPalWebCheckoutCallback fun interface following CardApproveOrderCallback pattern - Add overloaded start() function accepting PayPalWebCheckoutCallback
1 parent 387de57 commit 45a4537

File tree

4 files changed

+81
-23
lines changed

4 files changed

+81
-23
lines changed

CorePayments/src/main/java/com/paypal/android/corepayments/PayPalSDKError.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,15 @@ class PayPalSDKError @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor(
1010
val errorDescription: String,
1111
val correlationId: String? = null,
1212
reason: Throwable? = null
13-
) : Exception("Error: $code - Description: $errorDescription", reason)
13+
) : Exception("Error: $code - Description: $errorDescription", reason) {
14+
companion object {
15+
fun error(throwable: Throwable) = PayPalSDKError(
16+
code = UNKNOWN_ERROR_CODE,
17+
errorDescription = UNKNOWN_ERROR,
18+
reason = throwable
19+
)
20+
21+
const val UNKNOWN_ERROR = "Unknown Error"
22+
const val UNKNOWN_ERROR_CODE = -1
23+
}
24+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.paypal.android.paypalwebpayments
2+
3+
import androidx.annotation.MainThread
4+
5+
fun interface PayPalWebCheckoutCallback {
6+
/**
7+
* Called when the PayPal web checkout operation completes.
8+
*
9+
* @param result [PayPalPresentAuthChallengeResult] result with details
10+
*/
11+
@MainThread
12+
fun onPayPalWebCheckoutResult(result: PayPalPresentAuthChallengeResult)
13+
}

PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClient.kt

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@ import android.content.Context
44
import android.content.Intent
55
import androidx.activity.ComponentActivity
66
import com.paypal.android.corepayments.CoreConfig
7+
import com.paypal.android.corepayments.PayPalSDKError
78
import com.paypal.android.corepayments.RestClient
89
import com.paypal.android.corepayments.analytics.AnalyticsService
910
import com.paypal.android.corepayments.api.FetchClientToken
1011
import com.paypal.android.paypalwebpayments.analytics.CheckoutEvent
1112
import com.paypal.android.paypalwebpayments.analytics.PayPalWebAnalytics
1213
import com.paypal.android.paypalwebpayments.analytics.VaultEvent
14+
import kotlinx.coroutines.CoroutineDispatcher
15+
import kotlinx.coroutines.CoroutineScope
16+
import kotlinx.coroutines.Dispatchers
17+
import kotlinx.coroutines.launch
1318

1419
// NEXT MAJOR VERSION: consider renaming this module to PayPalWebClient since
1520
// it now offers both checkout and vaulting
@@ -20,7 +25,8 @@ import com.paypal.android.paypalwebpayments.analytics.VaultEvent
2025
class PayPalWebCheckoutClient internal constructor(
2126
private val analytics: PayPalWebAnalytics,
2227
private val payPalWebLauncher: PayPalWebLauncher,
23-
private val fetchClientToken: FetchClientToken
28+
private val fetchClientToken: FetchClientToken,
29+
private val mainDispatcher: CoroutineDispatcher = Dispatchers.Main
2430
) {
2531

2632
// for analytics tracking
@@ -52,21 +58,50 @@ class PayPalWebCheckoutClient internal constructor(
5258
checkoutOrderId = request.orderId
5359
analytics.notify(CheckoutEvent.STARTED, checkoutOrderId)
5460

55-
// Fetch client token for authentication
56-
fetchClientToken()
57-
// todo: token will be used while fetching app switch eligibility
58-
59-
val result = payPalWebLauncher.launchPayPalWebCheckout(activity, request)
60-
when (result) {
61-
is PayPalPresentAuthChallengeResult.Success -> analytics.notify(
62-
CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_SUCCEEDED,
63-
checkoutOrderId
61+
return runCatching {
62+
fetchClientToken()
63+
// todo: token will be used while fetching app switch eligibility
64+
65+
val result = payPalWebLauncher.launchPayPalWebCheckout(activity, request)
66+
when (result) {
67+
is PayPalPresentAuthChallengeResult.Success -> analytics.notify(
68+
CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_SUCCEEDED,
69+
checkoutOrderId
70+
)
71+
72+
is PayPalPresentAuthChallengeResult.Failure ->
73+
analytics.notify(
74+
CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_FAILED,
75+
checkoutOrderId
76+
)
77+
}
78+
result
79+
}.getOrElse { throwable ->
80+
analytics.notify(CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_FAILED, checkoutOrderId)
81+
PayPalPresentAuthChallengeResult.Failure(
82+
throwable as? PayPalSDKError ?: PayPalSDKError.error(throwable)
6483
)
84+
}
85+
}
6586

66-
is PayPalPresentAuthChallengeResult.Failure ->
67-
analytics.notify(CheckoutEvent.AUTH_CHALLENGE_PRESENTATION_FAILED, checkoutOrderId)
87+
/**
88+
* Confirm PayPal payment source for an order with callback.
89+
* Network operations are handled automatically by the Http layer.
90+
*
91+
* @param activity the ComponentActivity to launch the auth challenge from
92+
* @param request [PayPalWebCheckoutRequest] for requesting an order approval
93+
* @param callback callback to receive the result
94+
*/
95+
fun start(
96+
activity: ComponentActivity,
97+
request: PayPalWebCheckoutRequest,
98+
callback: PayPalWebCheckoutCallback
99+
) {
100+
CoroutineScope(mainDispatcher).launch {
101+
callback.onPayPalWebCheckoutResult(
102+
start(activity, request)
103+
)
68104
}
69-
return result
70105
}
71106

72107
/**

PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutClientUnitTest.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.paypal.android.paypalwebpayments
22

33
import android.content.Intent
44
import androidx.fragment.app.FragmentActivity
5-
import com.paypal.android.corepayments.APIClientError
65
import com.paypal.android.corepayments.PayPalSDKError
76
import com.paypal.android.corepayments.api.FetchClientToken
87
import com.paypal.android.paypalwebpayments.analytics.PayPalWebAnalytics
@@ -67,26 +66,26 @@ class PayPalWebCheckoutClientUnitTest {
6766
}
6867

6968
@Test
70-
fun `start() propagates FetchClientToken failure`() = runTest {
71-
val tokenError = APIClientError.payPalCheckoutError("Token fetch failed")
69+
fun `start() returns failure when FetchClientToken fails`() = runTest {
70+
val tokenError = PayPalSDKError(401, "Token fetch failed")
7271
coEvery { fetchClientToken() } throws tokenError
7372

7473
val request = PayPalWebCheckoutRequest("fake-order-id")
7574

76-
val result = runCatching { sut.start(activity, request) }
77-
assertTrue(result.isFailure)
78-
assertTrue(result.exceptionOrNull() is PayPalSDKError)
75+
val result = sut.start(activity, request)
76+
assertTrue(result is PayPalPresentAuthChallengeResult.Failure)
77+
assertSame(tokenError, (result as PayPalPresentAuthChallengeResult.Failure).error)
7978
}
8079

8180
@Test
8281
fun `start() does not call launcher when token fetch fails`() = runTest {
83-
val tokenError = APIClientError.payPalCheckoutError("Token fetch failed")
82+
val tokenError = PayPalSDKError(401, "Token fetch failed")
8483
coEvery { fetchClientToken() } throws tokenError
8584

8685
val request = PayPalWebCheckoutRequest("fake-order-id")
8786

88-
val result = runCatching { sut.start(activity, request) }
89-
assertTrue(result.isFailure)
87+
val result = sut.start(activity, request)
88+
assertTrue(result is PayPalPresentAuthChallengeResult.Failure)
9089

9190
verify(exactly = 0) { payPalWebLauncher.launchPayPalWebCheckout(any(), any()) }
9291
}

0 commit comments

Comments
 (0)