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

Rename 3DS APIs #827

Merged
merged 5 commits into from
Nov 14, 2023
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
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@
* Add `GooglePayClient#tokenize`
* ThreeDSecure
* Remove `ThreeDSecureListener`
* Add `ThreeDSecureLauncher`, `CardinalResult`, and `CardinalResultCallback`
* Add `ThreeDSecureLauncher`, `ThreeDSecurePaymentAuthResult`, and `ThreeDSecureLancherCallback`
* Remove overload constructors, `setListener`, `continuePerformVerification`, `onBrowserSwitchResult` and `onActivityResult` from `ThreeDSecureClient`
* Change `ThreeDSecureClient#performVerification` and `ThreeDSecureClient#initializeChallengeWithLookupResponse` parameters
* Add `ThreeDSecureClient#onCardinalResult`
* Change `ThreeDSecureClient#initializeChallengeWithLookupResponse` parameters
* Add `ThreeDSecureClient#tokenize`
* Rename `ThreeDSecureClient#performVerification` to
`ThreeDSecureClient#createPaymentAuthRequest` and change parameters
* Remove `versionRequested` from `ThreeDSecureRequest`
* Add `ThreeDSecureNonce` class
* Rename `ThreeDSecureResult#tokenizedCard` to `ThreeDSecureResult#threeDSecureNonce`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
View view = inflater.inflate(R.layout.fragment_card, container, false);

threeDSecureLauncher = new ThreeDSecureLauncher(this,
cardinalResult -> threeDSecureClient.onCardinalResult(cardinalResult,
paymentAuthResult -> threeDSecureClient.tokenize(paymentAuthResult,
this::handleThreeDSecureResult));

cardForm = view.findViewById(R.id.card_form);
Expand Down Expand Up @@ -228,7 +228,7 @@ private void handlePaymentMethodNonceCreated(PaymentMethodNonce paymentMethodNon
getString(R.string.loading), true, false);

ThreeDSecureRequest threeDSecureRequest = threeDSecureRequest(paymentMethodNonce);
threeDSecureClient.performVerification(requireContext(), threeDSecureRequest,
threeDSecureClient.createPaymentAuthRequest(requireContext(), threeDSecureRequest,
(threeDSecureResult, error) -> {
if (threeDSecureResult != null &&
threeDSecureResult.getLookup().requiresUserAuthentication()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import com.cardinalcommerce.cardinalmobilesdk.models.ValidateResponse;

class ThreeDSecureActivityResultContract
extends ActivityResultContract<ThreeDSecureResult, CardinalResult> {
extends ActivityResultContract<ThreeDSecureResult, ThreeDSecurePaymentAuthResult> {

@NonNull
@Override
Expand All @@ -31,25 +31,25 @@ public Intent createIntent(@NonNull Context context, ThreeDSecureResult input) {
}

@Override
public CardinalResult parseResult(int resultCode, @Nullable Intent intent) {
CardinalResult result;
public ThreeDSecurePaymentAuthResult parseResult(int resultCode, @Nullable Intent intent) {
ThreeDSecurePaymentAuthResult result;

if (resultCode == Activity.RESULT_CANCELED) {
result = new CardinalResult(new UserCanceledException("User canceled 3DS."));
result = new ThreeDSecurePaymentAuthResult(new UserCanceledException("User canceled 3DS."));
} else if (intent == null) {
String unknownErrorMessage =
"An unknown Android error occurred with the activity result API.";
result = new CardinalResult(new BraintreeException(unknownErrorMessage));
result = new ThreeDSecurePaymentAuthResult(new BraintreeException(unknownErrorMessage));
} else if (resultCode == ThreeDSecureActivity.RESULT_COULD_NOT_START_CARDINAL) {
String errorMessage = intent.getStringExtra(EXTRA_ERROR_MESSAGE);
result = new CardinalResult(new BraintreeException(errorMessage));
result = new ThreeDSecurePaymentAuthResult(new BraintreeException(errorMessage));
} else {
ThreeDSecureResult threeDSecureResult =
intent.getParcelableExtra(EXTRA_THREE_D_SECURE_RESULT);
ValidateResponse validateResponse =
(ValidateResponse) intent.getSerializableExtra(EXTRA_VALIDATION_RESPONSE);
String jwt = intent.getStringExtra(EXTRA_JWT);
result = new CardinalResult(threeDSecureResult, jwt, validateResponse);
result = new ThreeDSecurePaymentAuthResult(threeDSecureResult, jwt, validateResponse);
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ public ThreeDSecureClient(@NonNull BraintreeClient braintreeClient) {
* @param request the {@link ThreeDSecureRequest} with information used for authentication.
* @param callback {@link ThreeDSecureResultCallback}
*/
public void performVerification(@NonNull final Context context,
@NonNull final ThreeDSecureRequest request,
@NonNull final ThreeDSecureResultCallback callback) {
public void createPaymentAuthRequest(@NonNull final Context context,
@NonNull final ThreeDSecureRequest request,
@NonNull final ThreeDSecureResultCallback callback) {
if (request.getAmount() == null || request.getNonce() == null) {
callback.onResult(null, new InvalidArgumentException(
"The ThreeDSecureRequest nonce and amount cannot be null"));
Expand Down Expand Up @@ -94,7 +94,7 @@ public void performVerification(@NonNull final Context context,
ThreeDSecureResultCallback internalResultCallback =
(threeDSecureResult, performLookupError) -> {
if (threeDSecureResult != null) {
continuePerformVerification(threeDSecureResult, callback);
sendAnalyticsAndCallbackResult(threeDSecureResult, callback);
} else {
callback.onResult(null, performLookupError);
}
Expand Down Expand Up @@ -193,13 +193,6 @@ public void prepareLookup(@NonNull final Context context,
});
}

void continuePerformVerification(@NonNull final ThreeDSecureResult result,
@NonNull final ThreeDSecureResultCallback callback) {
braintreeClient.getConfiguration(
(configuration, error) -> startVerificationFlow(
result, callback));
}

/**
* Initialize a challenge from a server side lookup call.
*
Expand All @@ -213,15 +206,16 @@ public void initializeChallengeWithLookupResponse(@NonNull final String lookupRe
ThreeDSecureResult result;
try {
result = ThreeDSecureResult.fromJson(lookupResponse);
startVerificationFlow(result, callback);
sendAnalyticsAndCallbackResult(result, callback);
} catch (JSONException e) {
callback.onResult(null, e);
}
});
}

private void startVerificationFlow(ThreeDSecureResult result,
ThreeDSecureResultCallback callback) {
// TODO: Consolidate this method with createPaymentAuthRequest when analytics refactor is complete
void sendAnalyticsAndCallbackResult(ThreeDSecureResult result,
ThreeDSecureResultCallback callback) {
ThreeDSecureLookup lookup = result.getLookup();

boolean showChallenge = lookup.getAcsUrl() != null;
Expand Down Expand Up @@ -258,27 +252,26 @@ private void startVerificationFlow(ThreeDSecureResult result,
return;
}

// perform cardinal authentication
braintreeClient.sendAnalyticsEvent("three-d-secure.verification-flow.started");
callback.onResult(result, null);
}

/**
* Call this method from the {@link CardinalResultCallback} passed to the
* Call this method from the {@link ThreeDSecureLauncherCallback} passed to the
* {@link ThreeDSecureLauncher} used to launch the 3DS authentication challenge.
*
* @param cardinalResult a {@link CardinalResult} received in {@link CardinalResultCallback}
* @param paymentAuthResult a {@link ThreeDSecurePaymentAuthResult} received in {@link ThreeDSecureLauncherCallback}
* @param callback a {@link ThreeDSecureResultCallback}
*/
public void onCardinalResult(CardinalResult cardinalResult,
ThreeDSecureResultCallback callback) {
Exception threeDSecureError = cardinalResult.getError();
public void tokenize(ThreeDSecurePaymentAuthResult paymentAuthResult,
ThreeDSecureResultCallback callback) {
Exception threeDSecureError = paymentAuthResult.getError();
if (threeDSecureError != null) {
callback.onResult(null, threeDSecureError);
} else {
ThreeDSecureResult threeDSecureResult = cardinalResult.getThreeSecureResult();
ValidateResponse validateResponse = cardinalResult.getValidateResponse();
String jwt = cardinalResult.getJWT();
ThreeDSecureResult threeDSecureResult = paymentAuthResult.getThreeSecureResult();
ValidateResponse validateResponse = paymentAuthResult.getValidateResponse();
String jwt = paymentAuthResult.getJWT();

braintreeClient.sendAnalyticsEvent(
String.format("three-d-secure.verification-flow.cardinal-sdk.action-code.%s",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ public class ThreeDSecureLauncher {
"com.braintreepayments.api.ThreeDSecure.RESULT";
@VisibleForTesting
ActivityResultLauncher<ThreeDSecureResult> activityLauncher;
private final CardinalResultCallback callback;
private final ThreeDSecureLauncherCallback callback;

/**
* Used to launch the 3DS authentication flow to tokenize a 3DS card. This class must be
* instantiated in the onCreateView method of your Fragment
*
* @param fragment an Android Fragment from which you will launch the 3DS flow
* @param callback a {@link CardinalResultCallback} to received the result of the 3DS
* @param callback a {@link ThreeDSecureLauncherCallback} to received the result of the 3DS
* authentication flow
*/
public ThreeDSecureLauncher(@NonNull Fragment fragment,
@NonNull CardinalResultCallback callback) {
@NonNull ThreeDSecureLauncherCallback callback) {
this(fragment.getActivity().getActivityResultRegistry(), fragment.getViewLifecycleOwner(),
callback);
}
Expand All @@ -41,28 +41,28 @@ public ThreeDSecureLauncher(@NonNull Fragment fragment,
* instantiated in the onCreate method of your FragmentActivity
*
* @param activity an Android Activity from which you will launch the 3DS flow
* @param callback a {@link CardinalResultCallback} to received the result of the 3DS
* @param callback a {@link ThreeDSecureLauncherCallback} to received the result of the 3DS
* authentication flow
*/
public ThreeDSecureLauncher(@NonNull FragmentActivity activity,
@NonNull CardinalResultCallback callback) {
@NonNull ThreeDSecureLauncherCallback callback) {
this(activity.getActivityResultRegistry(), activity, callback);
}

@VisibleForTesting
ThreeDSecureLauncher(ActivityResultRegistry registry, LifecycleOwner lifecycleOwner,
CardinalResultCallback callback) {
ThreeDSecureLauncherCallback callback) {
this.callback = callback;
activityLauncher =
registry.register(THREE_D_SECURE_RESULT, lifecycleOwner,
new ThreeDSecureActivityResultContract(),
callback::onCardinalResult);
callback::onThreeDSecurePaymentAuthResult);
}

/**
* Launches the 3DS flow by switching to an authentication Activity. Call this method in the
* callback of
* {@link ThreeDSecureClient#performVerification(Context, ThreeDSecureRequest,
* {@link ThreeDSecureClient#createPaymentAuthRequest(Context, ThreeDSecureRequest,
* ThreeDSecureResultCallback)} if user authentication is required
* {@link ThreeDSecureLookup#requiresUserAuthentication()}
*
Expand All @@ -81,7 +81,7 @@ public void launch(ThreeDSecureResult threeDSecureResult) {
+ "Please contact Braintree Support for assistance.";
BraintreeException threeDSecureResponseTooLargeError =
new BraintreeException(errorMessage, runtimeException);
callback.onCardinalResult(new CardinalResult(threeDSecureResponseTooLargeError));
callback.onThreeDSecurePaymentAuthResult(new ThreeDSecurePaymentAuthResult(threeDSecureResponseTooLargeError));
} else {
throw runtimeException;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* Callback for receiving the results via {@link ThreeDSecureLauncher}
*/
public interface CardinalResultCallback {
public interface ThreeDSecureLauncherCallback {

void onCardinalResult(@NonNull CardinalResult cardinalResult);
void onThreeDSecurePaymentAuthResult(@NonNull ThreeDSecurePaymentAuthResult paymentAuthResult);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@

import com.cardinalcommerce.cardinalmobilesdk.models.ValidateResponse;

public class CardinalResult {
public class ThreeDSecurePaymentAuthResult {

private final String jwt;
private final ValidateResponse validateResponse;
private final ThreeDSecureResult threeDSecureResult;

private final Exception error;

CardinalResult(ThreeDSecureResult threeDSecureResult, String jwt,
ValidateResponse validateResponse) {
ThreeDSecurePaymentAuthResult(ThreeDSecureResult threeDSecureResult, String jwt,
ValidateResponse validateResponse) {
this.jwt = jwt;
this.validateResponse = validateResponse;
this.threeDSecureResult = threeDSecureResult;
this.error = null;
}

CardinalResult(Exception error) {
ThreeDSecurePaymentAuthResult(Exception error) {
this.error = error;
this.jwt = null;
this.validateResponse = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
/**
* Class to parse and contain 3D Secure authentication responses
*/
// TODO: Split into separate result objects for createPaymentAuthRequest and tokenize methods
public class ThreeDSecureResult implements Parcelable {

private static final String ERRORS_KEY = "errors";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package com.braintreepayments.api;

import android.content.Intent;

import androidx.annotation.Nullable;

/**
* Callback for receiving result of
* {@link ThreeDSecureClient#performVerification(android.content.Context, ThreeDSecureRequest, ThreeDSecureResultCallback)},
* {@link ThreeDSecureClient#continuePerformVerification(ThreeDSecureResult, ThreeDSecureResultCallback)},
* {@link ThreeDSecureClient#onBrowserSwitchResult(BrowserSwitchResult,
* ThreeDSecureResultCallback)}, and
* {@link ThreeDSecureClient#onActivityResult(int, Intent, ThreeDSecureResultCallback)}.
* {@link ThreeDSecureClient#createPaymentAuthRequest(android.content.Context, ThreeDSecureRequest, ThreeDSecureResultCallback)},

*/
// TODO: Split into separate callbacks for internal and public methods and for
// createPaymentAuthRequest and tokenize methods
public interface ThreeDSecureResultCallback {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,23 @@ public void parseResult_whenResultIsOK_returnsCardinalResultWithSuccessData() {
String jwt = "sample-jwt";
successIntent.putExtra(EXTRA_JWT, jwt);

CardinalResult cardinalResult = sut.parseResult(Activity.RESULT_OK, successIntent);
assertNotNull(cardinalResult);
ThreeDSecurePaymentAuthResult
paymentAuthResult = sut.parseResult(Activity.RESULT_OK, successIntent);
assertNotNull(paymentAuthResult);

assertSame(threeDSecureResult, cardinalResult.getThreeSecureResult());
assertSame(validateResponse, cardinalResult.getValidateResponse());
assertSame(jwt, cardinalResult.getJWT());
assertSame(threeDSecureResult, paymentAuthResult.getThreeSecureResult());
assertSame(validateResponse, paymentAuthResult.getValidateResponse());
assertSame(jwt, paymentAuthResult.getJWT());
}

@Test
public void parseResult_whenResultIsOKAndIntentIsNull_returnsCardinalResultWithError() {
sut = new ThreeDSecureActivityResultContract();

CardinalResult cardinalResult = sut.parseResult(Activity.RESULT_OK, null);
assertNotNull(cardinalResult);
ThreeDSecurePaymentAuthResult paymentAuthResult = sut.parseResult(Activity.RESULT_OK, null);
assertNotNull(paymentAuthResult);

BraintreeException error = (BraintreeException) cardinalResult.getError();
BraintreeException error = (BraintreeException) paymentAuthResult.getError();
String expectedMessage = "An unknown Android error occurred with the activity result API.";
assertNotNull(expectedMessage, error.getMessage());
}
Expand All @@ -83,10 +84,11 @@ public void parseResult_whenResultIsOKAndIntentIsNull_returnsCardinalResultWithE
public void parseResult_whenResultIsCANCELED_returnsCardinalResultWithError() {
sut = new ThreeDSecureActivityResultContract();

CardinalResult cardinalResult = sut.parseResult(Activity.RESULT_CANCELED, new Intent());
assertNotNull(cardinalResult);
ThreeDSecurePaymentAuthResult
paymentAuthResult = sut.parseResult(Activity.RESULT_CANCELED, new Intent());
assertNotNull(paymentAuthResult);

UserCanceledException error = (UserCanceledException) cardinalResult.getError();
UserCanceledException error = (UserCanceledException) paymentAuthResult.getError();
assertEquals("User canceled 3DS.", error.getMessage());
}

Expand All @@ -97,11 +99,11 @@ public void parseResult_whenResultIsCANCELEDAndHasErrorMessage_returnsCardinalRe
Intent intent = new Intent();
intent.putExtra(ThreeDSecureActivity.EXTRA_ERROR_MESSAGE, "sample error message");

CardinalResult cardinalResult =
ThreeDSecurePaymentAuthResult paymentAuthResult =
sut.parseResult(ThreeDSecureActivity.RESULT_COULD_NOT_START_CARDINAL, intent);
assertNotNull(cardinalResult);
assertNotNull(paymentAuthResult);

BraintreeException error = (BraintreeException) cardinalResult.getError();
BraintreeException error = (BraintreeException) paymentAuthResult.getError();
assertEquals("sample error message", error.getMessage());
}
}
Loading