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
Show file tree
Hide file tree
Changes from 14 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
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.braintreepayments.api;

import android.content.Context;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import org.json.JSONException;

Expand All @@ -16,7 +18,17 @@ public class AmericanExpressClient {

private final BraintreeClient braintreeClient;

public AmericanExpressClient(@NonNull BraintreeClient braintreeClient) {
/**
* Initializes a new {@link AmericanExpressClient} instance
*
* @param context an Android Context
* @param authorization a Tokenization Key or Client Token used to authenticate
*/
public AmericanExpressClient(@NonNull Context context, @NonNull String authorization) {
this.braintreeClient = new BraintreeClient(context, authorization);
}

@VisibleForTesting AmericanExpressClient(@NonNull BraintreeClient braintreeClient) {
this.braintreeClient = braintreeClient;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.braintreepayments.api.IntegrationType.Integration
* Core Braintree class that handles network requests.
*/
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
open class BraintreeClient @VisibleForTesting internal constructor(
Comment on lines +16 to 17
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.


/**
Expand Down Expand Up @@ -76,6 +77,7 @@ open class BraintreeClient @VisibleForTesting internal constructor(
* @param authorization The tokenization key or client token to use. If an invalid authorization
* is provided, a [BraintreeException] will be returned via callback.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
constructor(context: Context, authorization: String) :
this(BraintreeOptions(context = context, initialAuthString = authorization))

Expand All @@ -86,6 +88,7 @@ open class BraintreeClient @VisibleForTesting internal constructor(
* @param clientTokenProvider An implementation of [ClientTokenProvider] that [BraintreeClient]
* will use to fetch a client token on demand.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
constructor(context: Context, clientTokenProvider: ClientTokenProvider) :
this(BraintreeOptions(context = context, clientTokenProvider = clientTokenProvider))

Expand All @@ -104,7 +107,8 @@ open class BraintreeClient @VisibleForTesting internal constructor(
* authorization is provided, a [BraintreeException] will be returned via callback.
* @param returnUrlScheme A custom return url to use for browser and app switching
*/
constructor (context: Context, authorization: String, returnUrlScheme: String) : this(
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
constructor (context: Context, authorization: String, returnUrlScheme: String?) : this(
BraintreeOptions(
context = context,
initialAuthString = authorization,
Expand All @@ -127,6 +131,7 @@ open class BraintreeClient @VisibleForTesting internal constructor(
* will use to fetch a client token on demand.
* @param returnUrlScheme A custom return url to use for browser and app switching
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
constructor(
context: Context,
clientTokenProvider: ClientTokenProvider,
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
* All Modules
* Bump `minSdkVersion` to API 23
* Bump target Java version to Java 11
* Remove `BraintreeClient` public constructors
* Update payment method constructor parameters from `braintreeClient` to `context` and
`authorization`
* UnionPay
* Remove `union-pay` module
* UnionPay cards can now be processed as regular cards (through the `card` module) due to their partnership with Discover
Expand Down
15 changes: 14 additions & 1 deletion Card/src/main/java/com/braintreepayments/api/CardClient.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.braintreepayments.api;

import android.content.Context;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
Expand All @@ -17,7 +19,18 @@ public class CardClient {
private final BraintreeClient braintreeClient;
private final ApiClient apiClient;

public CardClient(@NonNull BraintreeClient braintreeClient) {
/**
* Initializes a new {@link CardClient} instance
*
* @param context an Android Context
* @param authorization a Tokenization Key or Client Token used to authenticate
*/
public CardClient(@NonNull Context context, @NonNull String authorization) {
this(new BraintreeClient(context, authorization));
}

@VisibleForTesting
CardClient(@NonNull BraintreeClient braintreeClient) {
this(braintreeClient, new ApiClient(braintreeClient));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,18 @@ public class DataCollector {
private final UUIDHelper uuidHelper;
private final BraintreeClient braintreeClient;

public DataCollector(@NonNull BraintreeClient braintreeClient) {
/**
* Initializes a new {@link DataCollector} instance
*
* @param context an Android Context
* @param authorization a Tokenization Key or Client Token used to authenticate
*/
public DataCollector(@NonNull Context context, @NonNull String authorization) {
this(new BraintreeClient(context, authorization));
}

@VisibleForTesting
DataCollector(@NonNull BraintreeClient braintreeClient) {
this(braintreeClient, new MagnesInternalClient(), new UUIDHelper());
}

Expand Down
13 changes: 9 additions & 4 deletions Demo/src/main/java/com/braintreepayments/demo/BaseFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;

import com.braintreepayments.api.BraintreeClient;
import com.braintreepayments.api.PaymentMethodNonce;

import java.util.Objects;

public abstract class BaseFragment extends Fragment {

@CallSuper
Expand Down Expand Up @@ -47,11 +48,15 @@ protected void showDialog(String message) {
}
}

protected BraintreeClient getBraintreeClient() {

protected void fetchAuthorization(BraintreeAuthorizationCallback callback) {
DemoActivity demoActivity = getDemoActivity();
if (demoActivity != null) {
return demoActivity.getBraintreeClient();
demoActivity.fetchAuthorization(callback);
}
return null;
}
Comment on lines +52 to +57
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


String getAuthStringArg() {
return Objects.requireNonNull(requireArguments().getString("authString"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.braintreepayments.demo;

import androidx.annotation.NonNull;

import com.braintreepayments.api.Authorization;
import com.braintreepayments.api.BraintreeClient;

public interface BraintreeAuthorizationCallback {
void onResult(@NonNull String authString);
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,5 @@
// TODO: remove when AuthorizationProvider is released
public class BraintreeClientFactory {

static public BraintreeClient createBraintreeClientWithAuthorizationProvider(Context context) {
return new BraintreeClient(context, new DemoClientTokenProvider(context));
}

}
44 changes: 18 additions & 26 deletions Demo/src/main/java/com/braintreepayments/demo/CardFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import com.braintreepayments.api.AmericanExpressClient;
import com.braintreepayments.api.AmericanExpressRewardsBalance;
import com.braintreepayments.api.BraintreeClient;
import com.braintreepayments.api.Card;
import com.braintreepayments.api.CardClient;
import com.braintreepayments.api.CardNonce;
Expand Down Expand Up @@ -71,12 +70,11 @@ public class CardFragment extends BaseFragment implements OnCardFormSubmitListen
public void onCreate(Bundle onSaveInstanceState) {
super.onCreate(onSaveInstanceState);

BraintreeClient braintreeClient = getBraintreeClient();
americanExpressClient = new AmericanExpressClient(braintreeClient);
cardClient = new CardClient(braintreeClient);
threeDSecureClient = new ThreeDSecureClient(braintreeClient);
americanExpressClient = new AmericanExpressClient(requireContext(), super.getAuthStringArg());
cardClient = new CardClient(requireContext(), super.getAuthStringArg());
threeDSecureClient = new ThreeDSecureClient(requireContext(), super.getAuthStringArg());

dataCollector = new DataCollector(braintreeClient);
dataCollector = new DataCollector(requireContext(), super.getAuthStringArg());

if (onSaveInstanceState != null) {
threeDSecureRequested = onSaveInstanceState.getBoolean(EXTRA_THREE_D_SECURE_REQUESTED);
Expand Down Expand Up @@ -126,26 +124,20 @@ public void onSaveInstanceState(Bundle outState) {

private void configureCardForm() {
final AppCompatActivity activity = (AppCompatActivity) getActivity();
BraintreeClient braintreeClient = getBraintreeClient();

braintreeClient.getConfiguration((configuration, configError) -> {
if (configuration != null) {
cardForm.cardRequired(true)
.expirationRequired(true)
.cvvRequired(configuration.isCvvChallengePresent())
.postalCodeRequired(configuration.isPostalCodeChallengePresent())
.mobileNumberRequired(false)
.actionLabel(cardFormActionLabel)
.setup(activity);

if (getArguments().getBoolean(MainFragment.EXTRA_COLLECT_DEVICE_DATA, false)) {
dataCollector.collectDeviceData(activity,
(deviceData, e) -> this.deviceData = deviceData);
}
} else {
handleError(configError);
}
});

// TODO: Configure card form via settings
cardForm.cardRequired(true)
.expirationRequired(true)
.cvvRequired(true)
.postalCodeRequired(true)
.mobileNumberRequired(false)
.actionLabel(cardFormActionLabel)
.setup(activity);

if (getArguments().getBoolean(MainFragment.EXTRA_COLLECT_DEVICE_DATA, false)) {
dataCollector.collectDeviceData(activity,
(deviceData, e) -> this.deviceData = deviceData);
}
}

@Override
Expand Down
20 changes: 5 additions & 15 deletions Demo/src/main/java/com/braintreepayments/demo/DemoActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,33 @@
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;

import com.braintreepayments.api.BraintreeClient;

import java.util.Arrays;
import java.util.List;

public class DemoActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback, ActionBar.OnNavigationListener {

private BraintreeClient braintreeClient;
private AppBarConfiguration appBarConfiguration;

private DemoClientTokenProvider clientTokenProvider;

private SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.activity_demo);
clientTokenProvider = new DemoClientTokenProvider(this);

setupActionBar();
setProgressBarIndeterminateVisibility(true);

registerSharedPreferencesListener();
}

public BraintreeClient getBraintreeClient() {
// lazily instantiate braintree client in case the demo has been reset
if (braintreeClient == null) {
if (Settings.useTokenizationKey(this)) {
String tokenizationKey = Settings.getTokenizationKey(this);
braintreeClient = new BraintreeClient(this, tokenizationKey);
} else {
braintreeClient =
BraintreeClientFactory.createBraintreeClientWithAuthorizationProvider(this);
}
}
return braintreeClient;
public void fetchAuthorization(BraintreeAuthorizationCallback callback) {
clientTokenProvider.getClientToken(callback);
}

@Override
Expand Down Expand Up @@ -104,7 +95,6 @@ public boolean onSupportNavigateUp() {

private void performReset() {
setProgressBarIndeterminateVisibility(true);
braintreeClient = null;
}

public void showDialog(String message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import com.braintreepayments.demo.R;
import com.braintreepayments.demo.Settings;

public class DemoClientTokenProvider implements ClientTokenProvider {
public class DemoClientTokenProvider {

private final Merchant merchant;
private final Context appContext;
Expand All @@ -21,15 +21,14 @@ public DemoClientTokenProvider(Context context) {
appContext = context.getApplicationContext();
}

@Override
public void getClientToken(@NonNull ClientTokenCallback callback) {
public void getClientToken(@NonNull BraintreeAuthorizationCallback callback) {
String authType = Settings.getAuthorizationType(appContext);
if (authType.equals(getString(appContext, R.string.client_token))) {
merchant.fetchClientToken(appContext, (clientToken, error) -> {
if (clientToken != null) {
callback.onSuccess(clientToken);
callback.onResult(clientToken);
} else if (error != null) {
callback.onFailure(error);
callback.onResult(null);
}
});
}
Expand Down
Loading
Loading