-
Notifications
You must be signed in to change notification settings - Fork 238
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
Google pay single result #842
Conversation
BraintreeCore/schemas/com.braintreepayments.api.AnalyticsDatabase/1.json
Outdated
Show resolved
Hide resolved
GooglePay/src/main/java/com/braintreepayments/api/GooglePayReadinessResult.kt
Show resolved
Hide resolved
GooglePay/src/main/java/com/braintreepayments/api/GooglePayReadinessResult.kt
Show resolved
Hide resolved
…ent.java Co-authored-by: sshropshire <[email protected]>
* The Google Pay API is supported or not set up on this device, or there was an [error] | ||
* determining readiness. | ||
*/ | ||
class NotReadyToPay(val error: Exception?) : GooglePayReadinessResult() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class NotReadyToPay(val error: Exception?) : GooglePayReadinessResult() | |
class NotReadyToPay(val cause: Exception?) : GooglePayReadinessResult() |
nit: There is an Exception constructor public Exception(Throwable cause)
. We could borrow the name cause
to signal to merchants that the exception was produced by an underlying cause.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ The cause
property also allows us to chain stack traces. It'll be super useful for GitHub inbounds if we leverage that constructor internally when creating BraintreeException
instances.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated - do you think we should change to cause for all Failure results or just this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think wherever it feels right. That may be a separate PR, but sometimes the OG stack trace can provide a lot of context to get to the root issue, especially with everything being asynchronous.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I've created another ticket to look into where else in these result objects it would make sense to return throwable. For the GooglePayResult
object in this PR, we only ever return Exception
objects (stack trace included where possible), so I'm not sure there is a benefit to making it the more generic Throwable
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should keep it as Exception
yeah, Exception extends Throwable
so we shouldn't have to change any types. There may not always be a cause
too, and that's fine cause == null
in that case.
If we do have an exception that signifies the root cause, then a merchant can inspect braintreeException?.cause
to give us more info. Also, the toString()
implementation for Exception
prints out the combined stack trace of the exception chain.
new BraintreeException("Error with Config", ioExceptionCausedByConfigFetching)
as an example would allow us to chain the exception returned to the merchant by the SDK to the original root cause exception, like in this example the user's WiFi could be disabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok actually I think the cause
wording here is confusing - we aren't returning the reason why the user isn't ready to pay (we are just returning NotReadyToPay in those cases). We are only returning an error
if there was an error determining if the user was ready to pay (in which case it's not really the cause of why they aren't ready to pay, it's that we can't determine if they are ready to pay or not). I'm going to put it back to error
but I still have a ticket to investigate using cause/throwables
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also document this design decision to include cause
where we think it's applicable, and some loose guidelines around what "applicable" means somewhere internally.
I can see one of us in the future taking a look at the code and thinking "now why did we do this here but that there?", or even an external dev wondering why we did it 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put this back to error
, which aligns with all other Failure
results because I think it is more descriptive of what we are actually returning - so no additional design decision doc needed 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah cause
is mostly useful for acquiring stack traces from merchants when possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work on some meaningful cleanup. Got a few Qs and things I'd like to confirm before we merge
if (paymentAuthRequest instanceof GooglePayPaymentAuthRequest.ReadyToLaunch) { | ||
googlePayLauncher.launch( | ||
((GooglePayPaymentAuthRequest.ReadyToLaunch) paymentAuthRequest).getRequestParams()); | ||
} else if (paymentAuthRequest instanceof GooglePayPaymentAuthRequest.Failure) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are there any other possible values for GooglePayPaymentAuthRequest
? Curious if we need a fallback for scenarios we don't catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are the only options - the benefit of defining GooglePayPaymentAuthRequest
as a sealed class is that it is exhaustive, and when we migrate this file to Kotlin, we can use the when
switch statement without a default/fallback scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious if we need a fallback for scenarios we don't catch.
This is one thing that keeps me up at night lol. Adding a new case to an existing enum can totally break a merchant app. The tradeoff is reduced compiler enforcement in exchange for not breaking a merchant integration.
I do think it's worth calling out in our docs to encourage merchants to catch "UNKNOWN" results so that their integrations are forward compatible.
handleError(((GooglePayResult.Failure) googlePayResult).getError()); | ||
} else if (googlePayResult instanceof GooglePayResult.Success){ | ||
handleGooglePayActivityResult(((GooglePayResult.Success) googlePayResult).getNonce()); | ||
} else if (googlePayResult instanceof GooglePayResult.Cancel) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we add an else
block for other values, or are these the only options from google?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We define the GooglePayResult
object as a sealed class so these are the only options.
GooglePay/src/main/java/com/braintreepayments/api/GooglePayClient.java
Outdated
Show resolved
Hide resolved
GooglePay/src/main/java/com/braintreepayments/api/GooglePayClient.java
Outdated
Show resolved
Hide resolved
"manifest, or did not have a theme of R.style.bt_transparent_activity")); | ||
callback.onGooglePayPaymentAuthRequest(new GooglePayPaymentAuthRequest.Failure(new BraintreeException( | ||
"GooglePayActivity was not found in the Android " + | ||
"manifest, or did not have a theme of R.style.bt_transparent_activity"))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is oddly specific heh. No action, just commenting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... it is getting pretty verbose open to alternative callback naming! The previous pattern was always naming it onResult
which is a little confusing when the result we are returning is actually a request object
GooglePay/src/main/java/com/braintreepayments/api/GooglePayClient.java
Outdated
Show resolved
Hide resolved
callback.onResult(false, e); | ||
paymentsClient.isReadyToPay(isReadyToPayRequest).addOnCompleteListener(task -> { | ||
try { | ||
Boolean isReady = task.getResult(ApiException.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is some android syntax nuance that I'm missing, but this currently reads like:
"isReady = is there an API Exception?"
if yes, provide readiness result (which can be a failure?) <-- can I get a confirmation of this expectation?
otherwise not ready
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is saying isReady = the result of the task to fetch readiness status (which will be true or false if successfully fetched), but if there is an error executing this task, then throw an APIException.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
randomtwocents: The Google Pay API is legit weird IMO. Merchant wins when we wrap! 🌯
* The Google Pay API is supported or not set up on this device, or there was an [error] | ||
* determining readiness. | ||
*/ | ||
class NotReadyToPay(val error: Exception?) : GooglePayReadinessResult() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also document this design decision to include cause
where we think it's applicable, and some loose guidelines around what "applicable" means somewhere internally.
I can see one of us in the future taking a look at the code and thinking "now why did we do this here but that there?", or even an external dev wondering why we did it 😄
…ent.java Co-authored-by: Holly Richko <[email protected]>
…ent.java Co-authored-by: Holly Richko <[email protected]>
…ee/braintree_android into google_pay_single_result
It's mostly objective, the standard Also |
Co-authored-by: sshropshire <[email protected]>
Co-authored-by: sshropshire <[email protected]>
Summary of changes
Checklist
Authors