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

Instantiate BraintreeClient Within Clients #830

Merged
merged 16 commits into from
Nov 30, 2023
Merged

Conversation

sarahkoop
Copy link
Contributor

Summary of changes

  • Remove BraintreeClient public constructors and instead instantiate BraintreeClient under the hood within each payment method client
  • Add ClientParams object to be configurable with parameters required to instantiate clients (and BraintreeClient)
  • Update client public constructors to take ClientParams

Checklist

  • Added a changelog entry

Authors

@sarahkoop sarahkoop requested a review from a team as a code owner November 16, 2023 20:50
@sarahkoop
Copy link
Contributor Author

Open question for this PR: Is this cleaner/preferred over having merchants instantiate BraintreeClient? BraintreeClient feels a little bit like exposing unnecessary internals to the merchant (and that was feedback we received), but constructing a ClientParams object is essentially the same in code as instantiating a BraintreeClient, so this could be unnecessary migration lift for merchants.

@sarahkoop sarahkoop mentioned this pull request Nov 17, 2023
1 task
Comment on lines +16 to 17
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
open class BraintreeClient @VisibleForTesting internal constructor(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this change impact DropIn at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't - drop-in uses the BraintreeClient constructor with BraintreeOptions param, and has the same "com.braintreepayments.api" group ID as the core libraries, so they all can share these "package-private"/library group methods.


import android.content.Context

data class ClientParams(val context: Context,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it would be more clear to require the <Feature>Client constructors to require each param individually, versus encapsulating them all into this additional ClientParams class. Also that lets the feature clients that actually need the returnURL to enforce it's requirement

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah ClientParams is more tech-debt than API. It's internal in v4, it can be removed in v5.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, the parameter object pattern here can be useful. We created too many convenience initializers in v4 to support multiple integration patterns. With v5 we do get to wipe the slate clean and have a single constructor, but theoretically a param object like ClientParams can help to keep the constructor count low as we add new features.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that just plain auth and context as direct constructor parameters is definitely the cleanest - but yeah in v4 we ended up with too many overloads I wanted to avoid that with ClientParams. Return URL isn't a required param for any clients, it's optional for some. But hopefully we won't add a bunch of params to client constructors in this version, so maybe we are safe to just go with auth and context as the main constructor and one overload for the clients that can accept return URL. What do you all think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah there's definitely a middle ground. Thinking on it more, our request objects can also be considered parameter objects. It may make sense to move properties like returnUrl from ClientParams to <FEATURE>Request. That will cause some redundancy, but it also allows us to make returnUrl required (or optional to allow merchant overrides) on a per-feature basis.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea I think that's a good compromise here - I'll go with that

v5_MIGRATION_GUIDE.md Outdated Show resolved Hide resolved
Comment on lines 338 to 351
braintreClient = BraintreeClient(context, "TOKENIZATION_KEY_OR_CLIENT_TOKEN")
- braintreClient = BraintreeClient(context, "TOKENIZATION_KEY_OR_CLIENT_TOKEN")
- localPaymentClient = LocalPaymentClient(this, braintreeClient)
+ localPaymentClient = LocalPaymentClient(braintreeClient)
+ localPaymentClient = LocalPaymentClient(ClientParams(context, "TOKENIZATION_KEY_OR_CLIENT_TOKEN")
- localPaymentClient.setListener(this)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this! It's a change we can make on iOS too (in a major version), dropping the merchant step to instantiate a BTAPIClient. It always felt redundant to me to have merchants instantiate that class when there was no public functionality on it they would ever use.

Copy link
Contributor

@jaxdesmarais jaxdesmarais Nov 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great to coordinate this major version alongside the package access modifier in Swift 5.9+ (proposal here) as a whole. But totally agree having merchant just access the client like this would be dope:

// iOS v6
let apiClient = BTAPIClient(authorization: "AUTHORIZATION")
let cardClient = BTCardClient(apiClient: apiClient)

// iOS v7
let cardClient = BTCardClient(authorization: "AUTHORIZATION")

Added this to our v7 doc!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OMG if package is approved that will benefit us in so many ways! 🤞

@sarahkoop
Copy link
Contributor Author

Updated this PR to remove the ClientParams object and just construct each payment method client with a context and authorization string directly (which will hopefully align with an iOS v7 approach).

For the overload parameters (currently just browser switching params like returnUrlScheme), the plan will be to add those parameters to the payment method request objects themselves (ex: PayPalRequest.setReturnUrlScheme()). This is a simpler integration path for merchants as those request objects are already configurable, and it removes the need to make payment method specific parameters available to all clients. There is another ticket to handle moving those browser switch BraintreeClient params to the payment method specific request objects.

@@ -46,16 +50,11 @@ protected void onCreate(Bundle savedInstanceState) {

public BraintreeClient getBraintreeClient() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to remove the getBrainreeClient() eventually. If it can be removed in this PR that would be a nice win. Generally the less DemoActivity does the better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call - removed

Comment on lines -86 to +97
+ venmoClient = VenmoClient(braintreeClient)
+ venmoClient = VenmoClient(context, "TOKENIZATION_KEY_OR_CLIENT_TOKEN")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 So clean!

Comment on lines +38 to +39
@VisibleForTesting
SEPADirectDebitClient(@NonNull BraintreeClient braintreeClient) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we planning to remove these in a follow-up? Or are we keeping for ease of unit testing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now they are nice to keep for unit testing since we pass in a MockBraintreeClient with responses to various API calls

@@ -57,27 +53,13 @@ public View onCreateView(@NonNull LayoutInflater inflater,
public void onResume() {
super.onResume();

braintreeClient.getConfiguration((configuration, error) -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious - why were we doing config fetches in the demo app?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we probably could've deleted this a while back. This has roots in v3. I remember removing it from the Core SDK since it would require a runtime dependency on the Google Pay SDK, but we probably kept it in when we went to GA because we needed to get a release out quickly.

Copy link
Contributor

@scannillo scannillo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few small questions, but LGTM!

@sarahkoop sarahkoop mentioned this pull request Nov 29, 2023
1 task
Comment on lines +52 to +57
protected void fetchAuthorization(BraintreeAuthorizationCallback callback) {
DemoActivity demoActivity = getDemoActivity();
if (demoActivity != null) {
return demoActivity.getBraintreeClient();
demoActivity.fetchAuthorization(callback);
}
return null;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2cents: I always felt like phasing out BaseFragment would be the best call. We should be able to fetch client tokens within each feature fragment. Do you feel like it makes sense to remove eventually?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed - will look into that in another demo refactor PR

@sarahkoop sarahkoop merged commit 347905c into v5 Nov 30, 2023
2 checks passed
@sarahkoop sarahkoop deleted the instantiate_braintree_client branch November 30, 2023 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants