From dbd7a3a2ae81fd989dad40a0645d6609a1a67f3c Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Mon, 30 Oct 2023 15:49:59 -0500 Subject: [PATCH 01/29] Add SEPADirectDebitBrowserSwitchResult class --- .../SEPADirectDebitBrowserSwitchResult.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResult.java diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResult.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResult.java new file mode 100644 index 0000000000..51dc0c689c --- /dev/null +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResult.java @@ -0,0 +1,24 @@ +package com.braintreepayments.api; + +// TODO: - Docstring +public class SEPADirectDebitBrowserSwitchResult { + + private BrowserSwitchResult browserSwitchResult; + private Exception error; + + SEPADirectDebitBrowserSwitchResult(BrowserSwitchResult browserSwitchResult) { + this.browserSwitchResult = browserSwitchResult; + } + + SEPADirectDebitBrowserSwitchResult(Exception error) { + this.error = error; + } + + BrowserSwitchResult getBrowserSwitchResult() { + return browserSwitchResult; + } + + Exception getError() { + return error; + } +} \ No newline at end of file From 0cf76e1c04294b180c18cb52506bd29ac0e4125e Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Mon, 30 Oct 2023 16:00:12 -0500 Subject: [PATCH 02/29] Add SEPADirectDebitLauncher.java and SEPADirectDebitLauncherCallback.java --- .../api/SEPADirectDebitLauncher.java | 30 +++++++++++++++++++ .../api/SEPADirectDebitLauncherCallback.java | 8 +++++ 2 files changed, 38 insertions(+) create mode 100644 SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java create mode 100644 SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncherCallback.java diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java new file mode 100644 index 0000000000..e4ba1679bf --- /dev/null +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java @@ -0,0 +1,30 @@ +package com.braintreepayments.api; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.fragment.app.FragmentActivity; + +public class SEPADirectDebitLauncher { + + private final BrowserSwitchClient browserSwitchClient; + private final SEPADirectDebitLauncherCallback callback; + + public SEPADirectDebitLauncher(@NonNull SEPADirectDebitLauncherCallback callback) { + this(new BrowserSwitchClient(), callback); + } + + @VisibleForTesting + SEPADirectDebitLauncher(@NonNull BrowserSwitchClient browserSwitchClient, + SEPADirectDebitLauncherCallback callback) { + this.browserSwitchClient = browserSwitchClient; + this.callback = callback; + } + +// public void launch(@NonNull FragmentActivity activity, @NonNull PayPalResponse payPalResponse) { +// try { +// browserSwitchClient.start(activity, payPalResponse.getBrowserSwitchOptions()); +// } catch (BrowserSwitchException e) { +// callback.onResult(new SEPADirectDebitBrowserSwitchResult(e)); +// } +// } +} diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncherCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncherCallback.java new file mode 100644 index 0000000000..48a24c39fa --- /dev/null +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncherCallback.java @@ -0,0 +1,8 @@ +package com.braintreepayments.api; + +import androidx.annotation.NonNull; + +public interface SEPADirectDebitLauncherCallback { + + void onResult(@NonNull SEPADirectDebitBrowserSwitchResult sepaDirectDebitBrowserSwitchResult); +} From 149432ad0b16571f84c39e7c55cefe247501faaf Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Mon, 30 Oct 2023 16:02:26 -0500 Subject: [PATCH 03/29] Add SEPADirectDebitResponse.java --- .../api/SEPADirectDebitLauncher.java | 14 +++++++------- .../api/SEPADirectDebitResponse.java | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java index e4ba1679bf..e10f04acf1 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java @@ -20,11 +20,11 @@ public SEPADirectDebitLauncher(@NonNull SEPADirectDebitLauncherCallback callback this.callback = callback; } -// public void launch(@NonNull FragmentActivity activity, @NonNull PayPalResponse payPalResponse) { -// try { -// browserSwitchClient.start(activity, payPalResponse.getBrowserSwitchOptions()); -// } catch (BrowserSwitchException e) { -// callback.onResult(new SEPADirectDebitBrowserSwitchResult(e)); -// } -// } + public void launch(@NonNull FragmentActivity activity, @NonNull SEPADirectDebitResponse sepaDirectDebitResponse) { + try { + browserSwitchClient.start(activity, sepaDirectDebitResponse.getBrowserSwitchOptions()); + } catch (BrowserSwitchException e) { + callback.onResult(new SEPADirectDebitBrowserSwitchResult(e)); + } + } } diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java new file mode 100644 index 0000000000..10d5ce8e90 --- /dev/null +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java @@ -0,0 +1,14 @@ +package com.braintreepayments.api; + +public class SEPADirectDebitResponse { + + private BrowserSwitchOptions browserSwitchOptions; + + BrowserSwitchOptions getBrowserSwitchOptions() { + return browserSwitchOptions; + } + + void setBrowserSwitchOptions(BrowserSwitchOptions browserSwitchOptions) { + this.browserSwitchOptions = browserSwitchOptions; + } +} From e883cdbc48e29f6f0b45dc80bb3968e8d9124c40 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Mon, 30 Oct 2023 16:06:06 -0500 Subject: [PATCH 04/29] Add launch() and handleReturnToAppFromBrowser() methods to SEPADirectDebitLauncher.java --- .../api/SEPADirectDebitLauncher.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java index e10f04acf1..efbbb2bf86 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java @@ -1,5 +1,10 @@ package com.braintreepayments.api; +import static com.braintreepayments.api.BraintreeRequestCodes.SEPA_DEBIT; + +import android.content.Context; +import android.content.Intent; + import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.fragment.app.FragmentActivity; @@ -20,6 +25,7 @@ public SEPADirectDebitLauncher(@NonNull SEPADirectDebitLauncherCallback callback this.callback = callback; } + // TODO: - Launch SEPA flow in web browser & deliver results to SEPADirectDebitLauncherCallback passed into SEPADirectDebitLauncher init public void launch(@NonNull FragmentActivity activity, @NonNull SEPADirectDebitResponse sepaDirectDebitResponse) { try { browserSwitchClient.start(activity, sepaDirectDebitResponse.getBrowserSwitchOptions()); @@ -27,4 +33,13 @@ public void launch(@NonNull FragmentActivity activity, @NonNull SEPADirectDebitR callback.onResult(new SEPADirectDebitBrowserSwitchResult(e)); } } + + // TODO: - Capture & deliver result of browser-based SEPA flow. Invoke in onResume. + public void handleReturnToAppFromBrowser(@NonNull Context context, @NonNull Intent intent) { + BrowserSwitchResult result = browserSwitchClient.parseResult(context, SEPA_DEBIT, intent); + if (result != null) { + callback.onResult(new SEPADirectDebitBrowserSwitchResult(result)); + browserSwitchClient.clearActiveRequests(context); + } + } } From a60eb2d15a9b58eff77cfcdb8ca411fdcea512b2 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Mon, 30 Oct 2023 16:55:17 -0500 Subject: [PATCH 05/29] WIP - add SEPADirectDebitFlowStartedCallback and begin migration of SEPADirectDebitClient away from Listener --- ...irectDebitBrowserSwitchResultCallback.java | 16 ++++ .../api/SEPADirectDebitClient.java | 92 +++++++------------ .../SEPADirectDebitFlowStartedCallback.java | 8 ++ 3 files changed, 57 insertions(+), 59 deletions(-) create mode 100644 SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResultCallback.java create mode 100644 SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResultCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResultCallback.java new file mode 100644 index 0000000000..71bbbde72f --- /dev/null +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResultCallback.java @@ -0,0 +1,16 @@ +package com.braintreepayments.api; + +import androidx.annotation.Nullable; + +/** + * Callback for receiving result of + * {@link SEPADirectDebitClient#onBrowserSwitchResult(SEPADirectDebitBrowserSwitchResult, SEPADirectDebitBrowserSwitchResultCallback)}. + */ +public interface SEPADirectDebitBrowserSwitchResultCallback { + + /** + * @param sepaDirectDebitNonce {@link SEPADirectDebitNonce} + * @param error an exception that occurred while processing a PayPal result + */ + void onResult(@Nullable SEPADirectDebitNonce sepaDirectDebitNonce, @Nullable Exception error); +} diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index 5438ce3a06..7033328bb1 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -25,53 +25,15 @@ public class SEPADirectDebitClient { private final SEPADirectDebitApi sepaDirectDebitApi; private final BraintreeClient braintreeClient; - private SEPADirectDebitListener listener; - /** - * Create a new instance of {@link SEPADirectDebitClient} from within an Activity using a - * {@link BraintreeClient}. - * - * @param activity an Android FragmentActivity - * @param braintreeClient a {@link BraintreeClient} - */ - public SEPADirectDebitClient(@NonNull FragmentActivity activity, - @NonNull BraintreeClient braintreeClient) { - this(activity, activity.getLifecycle(), braintreeClient, - new SEPADirectDebitApi(braintreeClient)); - } - - /** - * Create a new instance of {@link SEPADirectDebitClient} from within a Fragment using a - * {@link BraintreeClient}. - * - * @param fragment an Android Fragment - * @param braintreeClient a {@link BraintreeClient} - */ - public SEPADirectDebitClient(@NonNull Fragment fragment, - @NonNull BraintreeClient braintreeClient) { - this(fragment.getActivity(), fragment.getLifecycle(), braintreeClient, - new SEPADirectDebitApi(braintreeClient)); + public SEPADirectDebitClient(@NonNull BraintreeClient braintreeClient) { + this(braintreeClient, new SEPADirectDebitApi(braintreeClient)); } @VisibleForTesting - SEPADirectDebitClient(FragmentActivity activity, Lifecycle lifecycle, - BraintreeClient braintreeClient, SEPADirectDebitApi sepaDirectDebitApi) { - this.sepaDirectDebitApi = sepaDirectDebitApi; + SEPADirectDebitClient(BraintreeClient braintreeClient, SEPADirectDebitApi sepaDirectDebitApi) { this.braintreeClient = braintreeClient; - if (activity != null && lifecycle != null) { - SEPADirectDebitLifecycleObserver observer = new SEPADirectDebitLifecycleObserver(this); - lifecycle.addObserver(observer); - } - } - - /** - * Add a {@link SEPADirectDebitListener} to your client to receive results or errors from the - * SEPA Direct Debit flow. - * - * @param listener a {@link SEPADirectDebitListener} - */ - public void setListener(SEPADirectDebitListener listener) { - this.listener = listener; + this.sepaDirectDebitApi = sepaDirectDebitApi; } /** @@ -82,8 +44,9 @@ public void setListener(SEPADirectDebitListener listener) { * @param activity an Android FragmentActivity * @param sepaDirectDebitRequest the {@link SEPADirectDebitRequest}. */ - public void tokenize(final FragmentActivity activity, - final SEPADirectDebitRequest sepaDirectDebitRequest) { + public void tokenize(@NonNull final FragmentActivity activity, + @NonNull final SEPADirectDebitRequest sepaDirectDebitRequest, + @NonNull final SEPADirectDebitFlowStartedCallback callback) { braintreeClient.sendAnalyticsEvent("sepa-direct-debit.selected.started"); braintreeClient.sendAnalyticsEvent("sepa-direct-debit.create-mandate.requested"); sepaDirectDebitApi.createMandate(sepaDirectDebitRequest, @@ -95,11 +58,15 @@ public void tokenize(final FragmentActivity activity, "sepa-direct-debit.create-mandate.success"); try { startBrowserSwitch(activity, result); + // TODO: - replace this startBrowserSwitch call w/ notification of tokenize() method w/ SEPAResult to continue to call sepaLauncher.launch() + SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(); // Does this need any data on it? + callback.onResult(sepaDirectDebitResponse, null); } catch (JSONException | BrowserSwitchException exception) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.browser-switch.failure"); - listener.onSEPADirectDebitFailure(exception); + callback.onResult(null, exception); } + // TODO: - For the cases where we don't need a web-flow mandate, when do we want to notify the merchant of success } else if (result.getApprovalUrl().equals("null")) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.success"); @@ -113,37 +80,45 @@ public void tokenize(final FragmentActivity activity, if (sepaDirectDebitNonce != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.tokenize.success"); - listener.onSEPADirectDebitSuccess(sepaDirectDebitNonce); + // listener.onSEPADirectDebitSuccess(sepaDirectDebitNonce); } else if (tokenizeError != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.tokenize.failure"); - listener.onSEPADirectDebitFailure(tokenizeError); + // listener.onSEPADirectDebitFailure(tokenizeError); } }); } else { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.failure"); - listener.onSEPADirectDebitFailure( - new BraintreeException("An unexpected error occurred.")); + // listener.onSEPADirectDebitFailure( + new BraintreeException("An unexpected error occurred."); } } else if (createMandateError != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.failure"); - listener.onSEPADirectDebitFailure(createMandateError); + // listener.onSEPADirectDebitFailure(createMandateError); } }); } - void onBrowserSwitchResult(FragmentActivity activity) { + void onBrowserSwitchResult(@NonNull SEPADirectDebitBrowserSwitchResult sepaDirectDebitBrowserSwitchResult, + @NonNull final SEPADirectDebitBrowserSwitchResultCallback callback) { BrowserSwitchResult browserSwitchResult = - braintreeClient.deliverBrowserSwitchResult(activity); + sepaDirectDebitBrowserSwitchResult.getBrowserSwitchResult(); + if (browserSwitchResult == null && sepaDirectDebitBrowserSwitchResult.getError() != null) { + callback.onResult(null, sepaDirectDebitBrowserSwitchResult.getError()); + return; + } + + if (browserSwitchResult == null) { + callback.onResult(null, new BraintreeException("An unexpected error occurred.")); + } int result = browserSwitchResult.getStatus(); switch (result) { case BrowserSwitchStatus.CANCELED: braintreeClient.sendAnalyticsEvent("sepa-direct-debit.browser-switch.canceled"); - listener.onSEPADirectDebitFailure( - new UserCanceledException("User canceled SEPA Debit.")); + callback.onResult(null, new UserCanceledException("User canceled SEPA Debit.")); break; case BrowserSwitchStatus.SUCCESS: Uri deepLinkUri = browserSwitchResult.getDeepLinkUrl(); @@ -165,22 +140,21 @@ void onBrowserSwitchResult(FragmentActivity activity) { if (sepaDirectDebitNonce != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.tokenize.success"); - listener.onSEPADirectDebitSuccess(sepaDirectDebitNonce); + callback.onResult(sepaDirectDebitNonce, null); } else if (error != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.tokenize.failure"); - listener.onSEPADirectDebitFailure(error); + callback.onResult(null, error); } }); } else if (deepLinkUri.getPath().contains("cancel")) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.browser-switch.failure"); - listener.onSEPADirectDebitFailure( - new BraintreeException("An unexpected error occurred.")); + callback.onResult(null, new BraintreeException("An unexpected error occurred.")); } } else { braintreeClient.sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); - listener.onSEPADirectDebitFailure(new BraintreeException("Unknown error")); + callback.onResult(null, new BraintreeException("Unknown error")); } break; } diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java new file mode 100644 index 0000000000..222cc1f0d8 --- /dev/null +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java @@ -0,0 +1,8 @@ +package com.braintreepayments.api; + +import androidx.annotation.Nullable; + +public interface SEPADirectDebitFlowStartedCallback { + + void onResult(SEPADirectDebitResponse sepaDirectDebitResponse, @Nullable Exception error); +} From 0c987a91cd416213c50e89f7c8cb9b35fc3381fc Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Mon, 30 Oct 2023 16:56:56 -0500 Subject: [PATCH 06/29] Comment out SEPADirectDebitLifecycleObserver --- .../api/SEPADirectDebitLifecycleObserver.java | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserver.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserver.java index ec75ab7574..ff3c14b74c 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserver.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserver.java @@ -12,31 +12,31 @@ import androidx.lifecycle.LifecycleOwner; // NEXT_MAJOR_VERSION: Update to implement DefaultLifeCycleObserver when Java 7 support is explicitly dropped. -class SEPADirectDebitLifecycleObserver implements LifecycleEventObserver { - - @VisibleForTesting - SEPADirectDebitClient sepaDirectDebitClient; - - SEPADirectDebitLifecycleObserver(@NonNull SEPADirectDebitClient sepaDirectDebitClient) { - this.sepaDirectDebitClient = sepaDirectDebitClient; - } - - @Override - public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner, @NonNull Lifecycle.Event event) { - if (event == ON_RESUME) { - FragmentActivity activity = null; - if (lifecycleOwner instanceof FragmentActivity) { - activity = (FragmentActivity) lifecycleOwner; - } else if (lifecycleOwner instanceof Fragment) { - activity = ((Fragment) lifecycleOwner).getActivity(); - } - - if (activity != null) { - BrowserSwitchResult pendingResult = sepaDirectDebitClient.getBrowserSwitchResult(activity); - if (pendingResult != null && pendingResult.getRequestCode() == BraintreeRequestCodes.SEPA_DEBIT) { - sepaDirectDebitClient.onBrowserSwitchResult(activity); - } - } - } - } -} +//class SEPADirectDebitLifecycleObserver implements LifecycleEventObserver { +// +// @VisibleForTesting +// SEPADirectDebitClient sepaDirectDebitClient; +// +// SEPADirectDebitLifecycleObserver(@NonNull SEPADirectDebitClient sepaDirectDebitClient) { +// this.sepaDirectDebitClient = sepaDirectDebitClient; +// } +// +// @Override +// public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner, @NonNull Lifecycle.Event event) { +// if (event == ON_RESUME) { +// FragmentActivity activity = null; +// if (lifecycleOwner instanceof FragmentActivity) { +// activity = (FragmentActivity) lifecycleOwner; +// } else if (lifecycleOwner instanceof Fragment) { +// activity = ((Fragment) lifecycleOwner).getActivity(); +// } +// +// if (activity != null) { +// BrowserSwitchResult pendingResult = sepaDirectDebitClient.getBrowserSwitchResult(activity); +// if (pendingResult != null && pendingResult.getRequestCode() == BraintreeRequestCodes.SEPA_DEBIT) { +// sepaDirectDebitClient.onBrowserSwitchResult(activity); +// } +// } +// } +// } +//} From fa7aedfa85f396660ea70275722e8f615a4fd420 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Mon, 30 Oct 2023 17:03:23 -0500 Subject: [PATCH 07/29] WIP - refactor Demo SEPADirectDebitFragment to use new launcher API --- .../demo/SEPADirectDebitFragment.java | 49 ++++++++++++++----- .../api/SEPADirectDebitClient.java | 17 ++----- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java index b257bf1966..a1dff70f36 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java +++ b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java @@ -7,22 +7,29 @@ import android.widget.Button; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.navigation.fragment.NavHostFragment; import com.braintreepayments.api.BraintreeClient; import com.braintreepayments.api.PostalAddress; +import com.braintreepayments.api.SEPADirectDebitBrowserSwitchResult; +import com.braintreepayments.api.SEPADirectDebitBrowserSwitchResultCallback; import com.braintreepayments.api.SEPADirectDebitClient; +import com.braintreepayments.api.SEPADirectDebitFlowStartedCallback; +import com.braintreepayments.api.SEPADirectDebitLauncher; +import com.braintreepayments.api.SEPADirectDebitLauncherCallback; import com.braintreepayments.api.SEPADirectDebitListener; import com.braintreepayments.api.SEPADirectDebitMandateType; import com.braintreepayments.api.SEPADirectDebitNonce; import com.braintreepayments.api.SEPADirectDebitRequest; +import com.braintreepayments.api.SEPADirectDebitResponse; import java.util.UUID; -public class SEPADirectDebitFragment extends BaseFragment implements SEPADirectDebitListener { +public class SEPADirectDebitFragment extends BaseFragment { private SEPADirectDebitClient sepaDirectDebitClient; - + private SEPADirectDebitLauncher sepaDirectDebitLauncher; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -31,8 +38,23 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, button.setOnClickListener(this::launchSEPADirectDebit); BraintreeClient braintreeClient = getBraintreeClient(); - sepaDirectDebitClient = new SEPADirectDebitClient(this, braintreeClient); - sepaDirectDebitClient.setListener(this); + sepaDirectDebitClient = new SEPADirectDebitClient(braintreeClient); + + sepaDirectDebitLauncher = new SEPADirectDebitLauncher(new SEPADirectDebitLauncherCallback() { + @Override + public void onResult(@NonNull SEPADirectDebitBrowserSwitchResult sepaDirectDebitBrowserSwitchResult) { + sepaDirectDebitClient.onBrowserSwitchResult(sepaDirectDebitBrowserSwitchResult, new SEPADirectDebitBrowserSwitchResultCallback() { + @Override + public void onResult(@Nullable SEPADirectDebitNonce sepaDirectDebitNonce, @Nullable Exception error) { + if (error != null) { + handleError(error); + } else { + handleSEPANonce(sepaDirectDebitNonce); + } + } + }); + } + }); return view; } @@ -54,24 +76,27 @@ public void launchSEPADirectDebit(View view) { request.setBillingAddress(billingAddress); request.setMerchantAccountId("EUR-sepa-direct-debit"); - sepaDirectDebitClient.tokenize(requireActivity(), request); + sepaDirectDebitClient.tokenize(requireActivity(), request, new SEPADirectDebitFlowStartedCallback() { + @Override + public void onResult(SEPADirectDebitResponse sepaDirectDebitResponse, @Nullable Exception error) { + if (error != null) { + handleError(error); + } else { + sepaDirectDebitLauncher.launch(requireActivity(), sepaDirectDebitResponse); + } + } + }); } private String generateRandomCustomerId() { return UUID.randomUUID().toString().substring(0,20); } - @Override - public void onSEPADirectDebitSuccess(@NonNull SEPADirectDebitNonce sepaDirectDebitNonce) { + private void handleSEPANonce(@NonNull SEPADirectDebitNonce sepaDirectDebitNonce) { super.onPaymentMethodNonceCreated(sepaDirectDebitNonce); SEPADirectDebitFragmentDirections.ActionSepaDirectDebitFragmentToDisplayNonceFragment action = SEPADirectDebitFragmentDirections.actionSepaDirectDebitFragmentToDisplayNonceFragment(sepaDirectDebitNonce); NavHostFragment.findNavController(this).navigate(action); } - - @Override - public void onSEPADirectDebitFailure(@NonNull Exception error) { - handleError(error); - } } diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index 7033328bb1..87962c792c 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -56,16 +56,9 @@ public void tokenize(@NonNull final FragmentActivity activity, if (URLUtil.isValidUrl(result.getApprovalUrl())) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.success"); - try { - startBrowserSwitch(activity, result); - // TODO: - replace this startBrowserSwitch call w/ notification of tokenize() method w/ SEPAResult to continue to call sepaLauncher.launch() - SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(); // Does this need any data on it? - callback.onResult(sepaDirectDebitResponse, null); - } catch (JSONException | BrowserSwitchException exception) { - braintreeClient.sendAnalyticsEvent( - "sepa-direct-debit.browser-switch.failure"); - callback.onResult(null, exception); - } + SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(); // Does this need any data on it? + // TODO: - Need to provide browser switch options to avoid crash + callback.onResult(sepaDirectDebitResponse, null); // TODO: - For the cases where we don't need a web-flow mandate, when do we want to notify the merchant of success } else if (result.getApprovalUrl().equals("null")) { braintreeClient.sendAnalyticsEvent( @@ -101,8 +94,8 @@ public void tokenize(@NonNull final FragmentActivity activity, }); } - void onBrowserSwitchResult(@NonNull SEPADirectDebitBrowserSwitchResult sepaDirectDebitBrowserSwitchResult, - @NonNull final SEPADirectDebitBrowserSwitchResultCallback callback) { + public void onBrowserSwitchResult(@NonNull SEPADirectDebitBrowserSwitchResult sepaDirectDebitBrowserSwitchResult, + @NonNull final SEPADirectDebitBrowserSwitchResultCallback callback) { BrowserSwitchResult browserSwitchResult = sepaDirectDebitBrowserSwitchResult.getBrowserSwitchResult(); if (browserSwitchResult == null && sepaDirectDebitBrowserSwitchResult.getError() != null) { From 85da429cf7c1ec3f32830425b835d6dabbf50064 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 10:35:43 -0500 Subject: [PATCH 08/29] Add browserSwitchOptions to SEPADirectDebitResult & implement onResume in SEPADemo Fragment --- .../demo/SEPADirectDebitFragment.java | 6 ++++ .../api/SEPADirectDebitClient.java | 28 +++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java index a1dff70f36..896bfd7c4f 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java +++ b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java @@ -59,6 +59,12 @@ public void onResult(@Nullable SEPADirectDebitNonce sepaDirectDebitNonce, @Nulla return view; } + @Override + public void onResume() { + super.onResume(); + sepaDirectDebitLauncher.handleReturnToAppFromBrowser(requireContext(), requireActivity().getIntent()); + } + public void launchSEPADirectDebit(View view) { PostalAddress billingAddress = new PostalAddress(); billingAddress.setStreetAddress("Kantstraße 70"); diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index 87962c792c..5e9c44d53b 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -56,9 +56,15 @@ public void tokenize(@NonNull final FragmentActivity activity, if (URLUtil.isValidUrl(result.getApprovalUrl())) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.success"); - SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(); // Does this need any data on it? - // TODO: - Need to provide browser switch options to avoid crash - callback.onResult(sepaDirectDebitResponse, null); + try { + SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(); + sepaDirectDebitResponse.setBrowserSwitchOptions(buildBrowserSwitchOptions(result)); + callback.onResult(sepaDirectDebitResponse, null); + } catch (JSONException exception) { + braintreeClient.sendAnalyticsEvent( + "sepa-direct-debit.browser-switch.failure"); + callback.onResult(null, exception); + } // TODO: - For the cases where we don't need a web-flow mandate, when do we want to notify the merchant of success } else if (result.getApprovalUrl().equals("null")) { braintreeClient.sendAnalyticsEvent( @@ -175,4 +181,20 @@ private void startBrowserSwitch(FragmentActivity activity, braintreeClient.startBrowserSwitch(activity, browserSwitchOptions); braintreeClient.sendAnalyticsEvent("sepa-direct-debit.browser-switch.started"); } + + private BrowserSwitchOptions buildBrowserSwitchOptions(CreateMandateResult createMandateResult) throws JSONException { + JSONObject metadata = new JSONObject() + .put(IBAN_LAST_FOUR_KEY, createMandateResult.getIbanLastFour()) + .put(CUSTOMER_ID_KEY, createMandateResult.getCustomerId()) + .put(BANK_REFERENCE_TOKEN_KEY, createMandateResult.getBankReferenceToken()) + .put(MANDATE_TYPE_KEY, createMandateResult.getMandateType().toString()); + + BrowserSwitchOptions browserSwitchOptions = new BrowserSwitchOptions() + .requestCode(BraintreeRequestCodes.SEPA_DEBIT) + .url(Uri.parse(createMandateResult.getApprovalUrl())) + .metadata(metadata) + .returnUrlScheme(braintreeClient.getReturnUrlScheme()); + + return browserSwitchOptions; + } } From 6da395233a07664115dd7b69262eb5f13d8a1671 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 10:47:40 -0500 Subject: [PATCH 09/29] Remove unused getBrowserSwitchResult() and startBrowserSwitch() methods from SEPAClient --- .../api/SEPADirectDebitClient.java | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index 5e9c44d53b..0e0a98b027 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -159,29 +159,6 @@ public void onBrowserSwitchResult(@NonNull SEPADirectDebitBrowserSwitchResult se } } - BrowserSwitchResult getBrowserSwitchResult(FragmentActivity activity) { - return braintreeClient.getBrowserSwitchResult(activity); - } - - private void startBrowserSwitch(FragmentActivity activity, - CreateMandateResult createMandateResult) - throws JSONException, BrowserSwitchException { - JSONObject metadata = new JSONObject() - .put(IBAN_LAST_FOUR_KEY, createMandateResult.getIbanLastFour()) - .put(CUSTOMER_ID_KEY, createMandateResult.getCustomerId()) - .put(BANK_REFERENCE_TOKEN_KEY, createMandateResult.getBankReferenceToken()) - .put(MANDATE_TYPE_KEY, createMandateResult.getMandateType().toString()); - - BrowserSwitchOptions browserSwitchOptions = new BrowserSwitchOptions() - .requestCode(BraintreeRequestCodes.SEPA_DEBIT) - .url(Uri.parse(createMandateResult.getApprovalUrl())) - .metadata(metadata) - .returnUrlScheme(braintreeClient.getReturnUrlScheme()); - - braintreeClient.startBrowserSwitch(activity, browserSwitchOptions); - braintreeClient.sendAnalyticsEvent("sepa-direct-debit.browser-switch.started"); - } - private BrowserSwitchOptions buildBrowserSwitchOptions(CreateMandateResult createMandateResult) throws JSONException { JSONObject metadata = new JSONObject() .put(IBAN_LAST_FOUR_KEY, createMandateResult.getIbanLastFour()) From 849b03b084c30e2cc623cc90be4f4d9c1919f081 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 11:00:51 -0500 Subject: [PATCH 10/29] Add SEPADirectDebitLauncher.java docstrings --- .../api/SEPADirectDebitLauncher.java | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java index efbbb2bf86..e3e0ee404b 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java @@ -9,11 +9,20 @@ import androidx.annotation.VisibleForTesting; import androidx.fragment.app.FragmentActivity; +/** + * Responsible for launching a SEPA mandate in a web browser + */ public class SEPADirectDebitLauncher { private final BrowserSwitchClient browserSwitchClient; private final SEPADirectDebitLauncherCallback callback; + /** + * Used to launch the SEPA mandate in a web browser and deliver results to your Activity + * + * @param callback a {@link SEPADirectDebitLauncherCallback} to handle the result of + * {@link SEPADirectDebitLauncher#launch(FragmentActivity, SEPADirectDebitResponse)} + */ public SEPADirectDebitLauncher(@NonNull SEPADirectDebitLauncherCallback callback) { this(new BrowserSwitchClient(), callback); } @@ -25,7 +34,15 @@ public SEPADirectDebitLauncher(@NonNull SEPADirectDebitLauncherCallback callback this.callback = callback; } - // TODO: - Launch SEPA flow in web browser & deliver results to SEPADirectDebitLauncherCallback passed into SEPADirectDebitLauncher init + /** + * Launches the SEPA mandate by switching to a web browser for user authentication and + * delivers results to the {@link SEPADirectDebitLauncherCallback} passed into + * {@link SEPADirectDebitLauncher#SEPADirectDebitLauncher(SEPADirectDebitLauncherCallback)} + * + * @param activity an Android {@link FragmentActivity} + * @param sepaDirectDebitResponse the result of the SEPA mandate received from invoking + * {@link SEPADirectDebitClient#tokenize(FragmentActivity, SEPADirectDebitRequest, SEPADirectDebitFlowStartedCallback)} + */ public void launch(@NonNull FragmentActivity activity, @NonNull SEPADirectDebitResponse sepaDirectDebitResponse) { try { browserSwitchClient.start(activity, sepaDirectDebitResponse.getBrowserSwitchOptions()); @@ -34,8 +51,25 @@ public void launch(@NonNull FragmentActivity activity, @NonNull SEPADirectDebitR } } - // TODO: - Capture & deliver result of browser-based SEPA flow. Invoke in onResume. - public void handleReturnToAppFromBrowser(@NonNull Context context, @NonNull Intent intent) { + /** + * Captures and delivers the result of the browser-based SEPA mandate flow. + *

+ * For most integrations, this method should be invoked in the onResume method of the Activity + * used to invoke {@link SEPADirectDebitLauncher#launch(FragmentActivity, SEPADirectDebitResponse)}. + *

+ * If the Activity used to launch the SEPA mandate is configured with + * android:launchMode="singleTop", this method should be invoked in the onNewIntent method of + * the Activity, after invoking setIntent(intent). + *

+ * This method will deliver a {@link SEPADirectDebitBrowserSwitchResult} to the + * {@link SEPADirectDebitLauncherCallback} used to instantiate this class. The + * {@link SEPADirectDebitBrowserSwitchResult} should be passed to + * {@link SEPADirectDebitClient#onBrowserSwitchResult(SEPADirectDebitBrowserSwitchResult, SEPADirectDebitBrowserSwitchResultCallback)} + * + * @param context the context used to check for pending results + * @param intent the intent to return to your application containing a deep link result from + * the SEPA mandate flow + */ public void handleReturnToAppFromBrowser(@NonNull Context context, @NonNull Intent intent) { BrowserSwitchResult result = browserSwitchClient.parseResult(context, SEPA_DEBIT, intent); if (result != null) { callback.onResult(new SEPADirectDebitBrowserSwitchResult(result)); From 1c69f8071a55ceaaf8294e527a36e22cc4c2d236 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 11:16:18 -0500 Subject: [PATCH 11/29] Add docstrings for SEPADirectDebitClient.java --- .../api/SEPADirectDebitClient.java | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index 0e0a98b027..eecb28fcb6 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -1,5 +1,7 @@ package com.braintreepayments.api; +import android.content.Context; +import android.content.Intent; import android.net.Uri; import android.webkit.URLUtil; @@ -26,6 +28,11 @@ public class SEPADirectDebitClient { private final SEPADirectDebitApi sepaDirectDebitApi; private final BraintreeClient braintreeClient; + /** + * Create a new instance of {@link SEPADirectDebitClient} using a {@link BraintreeClient}. + * + * @param braintreeClient a {@link BraintreeClient} + */ public SEPADirectDebitClient(@NonNull BraintreeClient braintreeClient) { this(braintreeClient, new SEPADirectDebitApi(braintreeClient)); } @@ -37,12 +44,13 @@ public SEPADirectDebitClient(@NonNull BraintreeClient braintreeClient) { } /** - * Initiates a browser switch to display a mandate to the user. Upon successful mandate - * creation, tokenizes the payment method and returns a result to the - * {@link SEPADirectDebitListener}. + * Starts the SEPA tokenization process by creating a {@link SEPADirectDebitResponse} to be used + * to launch the SEPA mandate flow in + * {@link SEPADirectDebitLauncher#launch(FragmentActivity, SEPADirectDebitResponse)} * - * @param activity an Android FragmentActivity - * @param sepaDirectDebitRequest the {@link SEPADirectDebitRequest}. + * @param activity Android FragmentActivity + * @param sepaDirectDebitRequest {@link SEPADirectDebitRequest} + * @param callback {@link SEPADirectDebitFlowStartedCallback} */ public void tokenize(@NonNull final FragmentActivity activity, @NonNull final SEPADirectDebitRequest sepaDirectDebitRequest, @@ -100,6 +108,17 @@ public void tokenize(@NonNull final FragmentActivity activity, }); } + // TODO: - The wording in this docstring is confusing to me. Let's improve & align across all clients. + /** + * After receiving a result from the SEPA mandate web flow via + * {@link SEPADirectDebitLauncher#handleReturnToAppFromBrowser(Context, Intent)}, pass the + * {@link SEPADirectDebitBrowserSwitchResult} returned to this method to tokenize the SEPA + * account and receive a {@link SEPADirectDebitNonce} on success. + * + * @param sepaDirectDebitBrowserSwitchResult a {@link SEPADirectDebitBrowserSwitchResult} received + * in the callback of {@link SEPADirectDebitLauncher} + * @param callback {@link SEPADirectDebitBrowserSwitchResultCallback} + */ public void onBrowserSwitchResult(@NonNull SEPADirectDebitBrowserSwitchResult sepaDirectDebitBrowserSwitchResult, @NonNull final SEPADirectDebitBrowserSwitchResultCallback callback) { BrowserSwitchResult browserSwitchResult = From ce270e74972fae76cfe3b57e5f6bfdd0c0ccde80 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 11:42:42 -0500 Subject: [PATCH 12/29] Add nonce to SEPADirectDebitResponse for use when mandate not required; update demo fragment to use lambdas --- .../demo/SEPADirectDebitFragment.java | 28 ++++++++----------- .../api/SEPADirectDebitClient.java | 12 ++++---- .../api/SEPADirectDebitResponse.java | 9 ++++++ 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java index 896bfd7c4f..cae2a469f8 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java +++ b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java @@ -43,14 +43,11 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, sepaDirectDebitLauncher = new SEPADirectDebitLauncher(new SEPADirectDebitLauncherCallback() { @Override public void onResult(@NonNull SEPADirectDebitBrowserSwitchResult sepaDirectDebitBrowserSwitchResult) { - sepaDirectDebitClient.onBrowserSwitchResult(sepaDirectDebitBrowserSwitchResult, new SEPADirectDebitBrowserSwitchResultCallback() { - @Override - public void onResult(@Nullable SEPADirectDebitNonce sepaDirectDebitNonce, @Nullable Exception error) { - if (error != null) { - handleError(error); - } else { - handleSEPANonce(sepaDirectDebitNonce); - } + sepaDirectDebitClient.onBrowserSwitchResult(sepaDirectDebitBrowserSwitchResult, (sepaDirectDebitNonce, error) -> { + if (error != null) { + handleError(error); + } else { + handleSEPANonce(sepaDirectDebitNonce); } }); } @@ -82,14 +79,13 @@ public void launchSEPADirectDebit(View view) { request.setBillingAddress(billingAddress); request.setMerchantAccountId("EUR-sepa-direct-debit"); - sepaDirectDebitClient.tokenize(requireActivity(), request, new SEPADirectDebitFlowStartedCallback() { - @Override - public void onResult(SEPADirectDebitResponse sepaDirectDebitResponse, @Nullable Exception error) { - if (error != null) { - handleError(error); - } else { - sepaDirectDebitLauncher.launch(requireActivity(), sepaDirectDebitResponse); - } + sepaDirectDebitClient.tokenize(requireActivity(), request, (sepaDirectDebitResponse, error) -> { + if (error != null) { + handleError(error); + } else if (sepaDirectDebitResponse.getNonce() != null) { // web-flow mandate not required + handleSEPANonce(sepaDirectDebitResponse.getNonce()); + } else { + sepaDirectDebitLauncher.launch(requireActivity(), sepaDirectDebitResponse); } }); } diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index eecb28fcb6..a6a54a3692 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -73,7 +73,6 @@ public void tokenize(@NonNull final FragmentActivity activity, "sepa-direct-debit.browser-switch.failure"); callback.onResult(null, exception); } - // TODO: - For the cases where we don't need a web-flow mandate, when do we want to notify the merchant of success } else if (result.getApprovalUrl().equals("null")) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.success"); @@ -87,23 +86,24 @@ public void tokenize(@NonNull final FragmentActivity activity, if (sepaDirectDebitNonce != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.tokenize.success"); - // listener.onSEPADirectDebitSuccess(sepaDirectDebitNonce); + SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(); + sepaDirectDebitResponse.setNonce(sepaDirectDebitNonce); + callback.onResult(sepaDirectDebitResponse, null); } else if (tokenizeError != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.tokenize.failure"); - // listener.onSEPADirectDebitFailure(tokenizeError); + callback.onResult(null, tokenizeError); } }); } else { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.failure"); - // listener.onSEPADirectDebitFailure( - new BraintreeException("An unexpected error occurred."); + callback.onResult(null, new BraintreeException("An unexpected error occurred.")); } } else if (createMandateError != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.failure"); - // listener.onSEPADirectDebitFailure(createMandateError); + callback.onResult(null, createMandateError); } }); } diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java index 10d5ce8e90..9f8497b9f4 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java @@ -3,6 +3,7 @@ public class SEPADirectDebitResponse { private BrowserSwitchOptions browserSwitchOptions; + private SEPADirectDebitNonce sepaDirectDebitNonce; BrowserSwitchOptions getBrowserSwitchOptions() { return browserSwitchOptions; @@ -11,4 +12,12 @@ BrowserSwitchOptions getBrowserSwitchOptions() { void setBrowserSwitchOptions(BrowserSwitchOptions browserSwitchOptions) { this.browserSwitchOptions = browserSwitchOptions; } + + void setNonce(SEPADirectDebitNonce sepaDirectDebitNonce) { + this.sepaDirectDebitNonce = sepaDirectDebitNonce; + } + + public SEPADirectDebitNonce getNonce() { + return this.sepaDirectDebitNonce; + } } From 746c82f0b2602f8a552395dce65feaed79242aa0 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 11:48:32 -0500 Subject: [PATCH 13/29] Add docstrings to SEPADirectDebitFlowStartedCallback.java & add comment in demo fragment for clarity --- .../demo/SEPADirectDebitFragment.java | 2 +- .../api/SEPADirectDebitFlowStartedCallback.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java index cae2a469f8..60b8096741 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java +++ b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java @@ -84,7 +84,7 @@ public void launchSEPADirectDebit(View view) { handleError(error); } else if (sepaDirectDebitResponse.getNonce() != null) { // web-flow mandate not required handleSEPANonce(sepaDirectDebitResponse.getNonce()); - } else { + } else { // web-flow mandate required sepaDirectDebitLauncher.launch(requireActivity(), sepaDirectDebitResponse); } }); diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java index 222cc1f0d8..23ff375f3e 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java @@ -1,8 +1,20 @@ package com.braintreepayments.api; import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; +/** + * Callback for receiving result of + * {@link SEPADirectDebitClient#tokenize(FragmentActivity, SEPADirectDebitRequest, SEPADirectDebitFlowStartedCallback)}. + */ public interface SEPADirectDebitFlowStartedCallback { + /** + * @param sepaDirectDebitResponse the result of the SEPA create mandate call. If a nonce is present, + * no web-based mandate is required. If a nonce is not present, + * you must trigger the web-based mandate flow via + * {@link SEPADirectDebitLauncher#launch(FragmentActivity, SEPADirectDebitResponse)} + * @param error an exception that occurred while initiating the SEPA transaction + */ void onResult(SEPADirectDebitResponse sepaDirectDebitResponse, @Nullable Exception error); } From 9da0a3f427cecb95fabc66e4aff1d6068ebc5501 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 12:05:21 -0500 Subject: [PATCH 14/29] Start SEPA migration guide entry --- v5_MIGRATION_GUIDE.md | 72 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/v5_MIGRATION_GUIDE.md b/v5_MIGRATION_GUIDE.md index 828109ad1c..0af26aef22 100644 --- a/v5_MIGRATION_GUIDE.md +++ b/v5_MIGRATION_GUIDE.md @@ -13,6 +13,7 @@ basics for updating your Braintree integration from v4 to v5. 1. [3DS](#3ds) 1. [PayPal](#paypal) 1. [Local Payment](#local-payment) +1. [SEPA Direct Debit](#sepa-direct-debit) ## Android API @@ -354,3 +355,74 @@ class MyActivity : FragmentActivity() { - } } +## SEPA Direct Debit + +The SEPA Direct Debit integration has been updated to allow for more flexibility with browser +switching and result handling. + +`SEPADirectDebitLauncher` has been added to handle the browser switching portion of the SEPA +flow for payment authorization via bank mandate. This class is used to handle browser switch results +upon returning to your app from the web flow, therefore must be instantiated in or before +the `OnResume` method of your Activity or Fragment. + +`BraintreeClient` and `SEPADirectDebitClient` no longer require references to Fragment or Activity and +do not need to be instantiated in `OnCreate`. + +```diff +class MyActivity : FragmentActivity() { + ++ private lateinit var sepaDirectDebitLauncher: SEPADirectDebitLAuncher + private lateinit var braintreeClient: BraintreeClient + private lateinit var sepaDirectDebitClient: SEPADirectDebitClient + + @override fun onCreate(savedInstanceState: Bundle?) { ++ // can initialize clients outside of onCreate if desired +- initializeClients() ++ sepaDirectDebitLauncher = SEPADirectDebitLauncher() { sepaDirectDebitBrowserSwitchResult -> ++ sepaDirectDebitClient.onBrowserSwitchResult(sepaDirectDebitBrowserSwitchResult) { ++ sepaDirectDebitNonce, error -> ++ // handle nonce or error ++ } ++ } + } + + // ONLY REQUIRED IF YOUR ACTIVITY LAUNCH MODE IS SINGLE_TOP + @override fun onNewIntent(intent: Intent) { ++ sepaDirectDebitLauncher.handleReturnToAppFromBrowser(requireContext(), intent) + } + + // ALL OTHER ACTIVITY LAUNCH MODES + @override fun onResume() { ++ sepaDirectDebitLauncher.handleReturnToAppFromBrowser(requireContext(), requireActivity(). ++ intent) + } + + + fun initializeClients() { + braintreClient = BraintreeClient(context, "TOKENIZATION_KEY_OR_CLIENT_TOKEN") +- sepaDirectDebitClient = SEPADirectDebitClient(this, braintreeClient) ++ sepaDirectDebitClient = SEPADirectDebitClient(braintreeClient) +- sepaDirectDebitClient.setListener(this) + } + + fun onPaymentButtonClick() { +- sepaDirectDebitClient.tokenize(activity, request) ++ sepaDirectDebitClient.tokenize(activity, request) { sepaDirectDebitResponse, error -> ++ if (error != null) { ++ // handle error ++ } else if (sepaDirectDebitResponse.nonce != null) { // web-flow mandate not required ++ // handle nonce ++ } else { // web-flow mandate required ++ sepaDirectDebitLauncher.launch(activity, sepaDirectDebitResponse) ++ } ++ } + } + +- override fun onSEPADirectDebitSuccess(sepaDirectDebitNonce: SEPADirectDebitNonce) { +- // handle SEPA nonce +- } + +- override fun onSEPADirectDebitFailure(error: java.lang.Exception) { +- // handle error +- } +} \ No newline at end of file From 7ce4a971ffb76af2c5b0aef4f2bcc524a4064e60 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 13:28:51 -0500 Subject: [PATCH 15/29] Add initializer to BTSEPADirectDebitResponse --- .../api/SEPADirectDebitClient.java | 6 ++---- .../api/SEPADirectDebitResponse.java | 20 ++++++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index a6a54a3692..4b92eb3ae0 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -65,8 +65,7 @@ public void tokenize(@NonNull final FragmentActivity activity, braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.success"); try { - SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(); - sepaDirectDebitResponse.setBrowserSwitchOptions(buildBrowserSwitchOptions(result)); + SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(buildBrowserSwitchOptions(result), null); callback.onResult(sepaDirectDebitResponse, null); } catch (JSONException exception) { braintreeClient.sendAnalyticsEvent( @@ -86,8 +85,7 @@ public void tokenize(@NonNull final FragmentActivity activity, if (sepaDirectDebitNonce != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.tokenize.success"); - SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(); - sepaDirectDebitResponse.setNonce(sepaDirectDebitNonce); + SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(null, sepaDirectDebitNonce); callback.onResult(sepaDirectDebitResponse, null); } else if (tokenizeError != null) { braintreeClient.sendAnalyticsEvent( diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java index 9f8497b9f4..9938f648c1 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java @@ -1,22 +1,28 @@ package com.braintreepayments.api; +import androidx.fragment.app.FragmentActivity; + public class SEPADirectDebitResponse { private BrowserSwitchOptions browserSwitchOptions; private SEPADirectDebitNonce sepaDirectDebitNonce; - BrowserSwitchOptions getBrowserSwitchOptions() { - return browserSwitchOptions; - } - - void setBrowserSwitchOptions(BrowserSwitchOptions browserSwitchOptions) { + SEPADirectDebitResponse(BrowserSwitchOptions browserSwitchOptions, SEPADirectDebitNonce sepaDirectDebitNonce) { this.browserSwitchOptions = browserSwitchOptions; + this.sepaDirectDebitNonce = sepaDirectDebitNonce; } - void setNonce(SEPADirectDebitNonce sepaDirectDebitNonce) { - this.sepaDirectDebitNonce = sepaDirectDebitNonce; + BrowserSwitchOptions getBrowserSwitchOptions() { + return browserSwitchOptions; } + /** + * If this nonce is non-null, then the SEPA mandate is already approved. + * If this nonce is null, continue to present the SEPA mandate web flow via + * {@link SEPADirectDebitLauncher#launch(FragmentActivity, SEPADirectDebitResponse)} + * + * @return {@link SEPADirectDebitNonce} + */ public SEPADirectDebitNonce getNonce() { return this.sepaDirectDebitNonce; } From 039571a923b4d1f749344681e29fa594da495f5d Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 14:18:16 -0500 Subject: [PATCH 16/29] Cleanup --- .../demo/SEPADirectDebitFragment.java | 5 --- .../SEPADirectDebitBrowserSwitchResult.java | 9 +++- ...irectDebitBrowserSwitchResultCallback.java | 2 +- .../api/SEPADirectDebitClient.java | 6 ++- .../api/SEPADirectDebitLifecycleObserver.java | 42 ------------------- .../api/SEPADirectDebitListener.java | 21 ---------- .../api/SEPADirectDebitResponse.java | 8 ++++ .../api/SEPADirectDebitTokenizeCallback.java | 1 - v5_MIGRATION_GUIDE.md | 1 - 9 files changed, 20 insertions(+), 75 deletions(-) delete mode 100644 SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserver.java delete mode 100644 SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitListener.java diff --git a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java index 60b8096741..e4eebd6e45 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java +++ b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java @@ -7,22 +7,17 @@ import android.widget.Button; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.navigation.fragment.NavHostFragment; import com.braintreepayments.api.BraintreeClient; import com.braintreepayments.api.PostalAddress; import com.braintreepayments.api.SEPADirectDebitBrowserSwitchResult; -import com.braintreepayments.api.SEPADirectDebitBrowserSwitchResultCallback; import com.braintreepayments.api.SEPADirectDebitClient; -import com.braintreepayments.api.SEPADirectDebitFlowStartedCallback; import com.braintreepayments.api.SEPADirectDebitLauncher; import com.braintreepayments.api.SEPADirectDebitLauncherCallback; -import com.braintreepayments.api.SEPADirectDebitListener; import com.braintreepayments.api.SEPADirectDebitMandateType; import com.braintreepayments.api.SEPADirectDebitNonce; import com.braintreepayments.api.SEPADirectDebitRequest; -import com.braintreepayments.api.SEPADirectDebitResponse; import java.util.UUID; diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResult.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResult.java index 51dc0c689c..c7872ef69c 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResult.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResult.java @@ -1,6 +1,11 @@ package com.braintreepayments.api; -// TODO: - Docstring +/** + * Result received from the SEPA mandate web flow through {@link SEPADirectDebitBrowserSwitchResultCallback}. + * This result should be passed to + * {@link SEPADirectDebitClient#onBrowserSwitchResult(SEPADirectDebitBrowserSwitchResult, SEPADirectDebitBrowserSwitchResultCallback)} )} + * to complete the SEPA mandate flow. + */ public class SEPADirectDebitBrowserSwitchResult { private BrowserSwitchResult browserSwitchResult; @@ -21,4 +26,4 @@ BrowserSwitchResult getBrowserSwitchResult() { Exception getError() { return error; } -} \ No newline at end of file +} diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResultCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResultCallback.java index 71bbbde72f..e56a65a48d 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResultCallback.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitBrowserSwitchResultCallback.java @@ -10,7 +10,7 @@ public interface SEPADirectDebitBrowserSwitchResultCallback { /** * @param sepaDirectDebitNonce {@link SEPADirectDebitNonce} - * @param error an exception that occurred while processing a PayPal result + * @param error an exception that occurred while processing a PayPal result */ void onResult(@Nullable SEPADirectDebitNonce sepaDirectDebitNonce, @Nullable Exception error); } diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index 4b92eb3ae0..4abb1b6042 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -65,7 +65,8 @@ public void tokenize(@NonNull final FragmentActivity activity, braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.create-mandate.success"); try { - SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(buildBrowserSwitchOptions(result), null); + SEPADirectDebitResponse sepaDirectDebitResponse = + new SEPADirectDebitResponse(buildBrowserSwitchOptions(result), null); callback.onResult(sepaDirectDebitResponse, null); } catch (JSONException exception) { braintreeClient.sendAnalyticsEvent( @@ -85,7 +86,8 @@ public void tokenize(@NonNull final FragmentActivity activity, if (sepaDirectDebitNonce != null) { braintreeClient.sendAnalyticsEvent( "sepa-direct-debit.tokenize.success"); - SEPADirectDebitResponse sepaDirectDebitResponse = new SEPADirectDebitResponse(null, sepaDirectDebitNonce); + SEPADirectDebitResponse sepaDirectDebitResponse = + new SEPADirectDebitResponse(null, sepaDirectDebitNonce); callback.onResult(sepaDirectDebitResponse, null); } else if (tokenizeError != null) { braintreeClient.sendAnalyticsEvent( diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserver.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserver.java deleted file mode 100644 index ff3c14b74c..0000000000 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserver.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.braintreepayments.api; - - -import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleEventObserver; -import androidx.lifecycle.LifecycleOwner; - -// NEXT_MAJOR_VERSION: Update to implement DefaultLifeCycleObserver when Java 7 support is explicitly dropped. -//class SEPADirectDebitLifecycleObserver implements LifecycleEventObserver { -// -// @VisibleForTesting -// SEPADirectDebitClient sepaDirectDebitClient; -// -// SEPADirectDebitLifecycleObserver(@NonNull SEPADirectDebitClient sepaDirectDebitClient) { -// this.sepaDirectDebitClient = sepaDirectDebitClient; -// } -// -// @Override -// public void onStateChanged(@NonNull LifecycleOwner lifecycleOwner, @NonNull Lifecycle.Event event) { -// if (event == ON_RESUME) { -// FragmentActivity activity = null; -// if (lifecycleOwner instanceof FragmentActivity) { -// activity = (FragmentActivity) lifecycleOwner; -// } else if (lifecycleOwner instanceof Fragment) { -// activity = ((Fragment) lifecycleOwner).getActivity(); -// } -// -// if (activity != null) { -// BrowserSwitchResult pendingResult = sepaDirectDebitClient.getBrowserSwitchResult(activity); -// if (pendingResult != null && pendingResult.getRequestCode() == BraintreeRequestCodes.SEPA_DEBIT) { -// sepaDirectDebitClient.onBrowserSwitchResult(activity); -// } -// } -// } -// } -//} diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitListener.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitListener.java deleted file mode 100644 index c44180c6f8..0000000000 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.braintreepayments.api; - -import androidx.annotation.NonNull; - -/** - * Implement this interface to receive SEPA Direct Debit result notifications. - */ -public interface SEPADirectDebitListener { - - /** - * Called when SEPA Direct Debit tokenization is complete without error. - * @param sepaDirectDebitNonce SEPA Direct Debit tokenization result - */ - void onSEPADirectDebitSuccess(@NonNull SEPADirectDebitNonce sepaDirectDebitNonce); - - /** - * Called when SEPA Direct Debit tokenization has failed with an error. - * @param error explains reason for SEPA Direct Debit failure. - */ - void onSEPADirectDebitFailure(@NonNull Exception error); -} diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java index 9938f648c1..41d3ccfbf0 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java @@ -2,6 +2,14 @@ import androidx.fragment.app.FragmentActivity; +/** + * Returned via the {@link SEPADirectDebitFlowStartedCallback} after calling + * {@link SEPADirectDebitClient#tokenize(FragmentActivity, SEPADirectDebitRequest, SEPADirectDebitFlowStartedCallback)}. + * + * Inspect the {@link SEPADirectDebitNonce} property to determine if tokenization is complete, or + * if you must continue the SEPA mandate web flow via + * {@link SEPADirectDebitLauncher#launch(FragmentActivity, SEPADirectDebitResponse)} + */ public class SEPADirectDebitResponse { private BrowserSwitchOptions browserSwitchOptions; diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitTokenizeCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitTokenizeCallback.java index 79b285d492..7ce2072a16 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitTokenizeCallback.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitTokenizeCallback.java @@ -5,5 +5,4 @@ interface SEPADirectDebitTokenizeCallback { void onResult(@Nullable SEPADirectDebitNonce sepaDirectDebitNonce, @Nullable Exception error); - } diff --git a/v5_MIGRATION_GUIDE.md b/v5_MIGRATION_GUIDE.md index 0af26aef22..1c0b241e27 100644 --- a/v5_MIGRATION_GUIDE.md +++ b/v5_MIGRATION_GUIDE.md @@ -397,7 +397,6 @@ class MyActivity : FragmentActivity() { + intent) } - fun initializeClients() { braintreClient = BraintreeClient(context, "TOKENIZATION_KEY_OR_CLIENT_TOKEN") - sepaDirectDebitClient = SEPADirectDebitClient(this, braintreeClient) From df5d6fd8939666e716bce0c7d9ba1c5811ca8388 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Tue, 31 Oct 2023 14:32:31 -0500 Subject: [PATCH 17/29] Add CHANGELOG entry --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7964427d87..3899a33bf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,12 @@ * Remove overload constructors, `setListener`, `parseBrowserSwitchResult`, `clearActiveBrowserSwitchResult`, `approveLocalPayment`, and `approvePayment` from `LocalPaymentClient` + * SEPA Direct Debit + * Remove `SEPADirectDebitLifecycleObserver` and `SEPADirectDebitListener` + * Add `SEPADirectDebitLauncher`, `SEPADirectDebitLauncherCallback`, `SEPADirectDebitFlowStartedCallback`, + `SEPADirectDebitResponse`, `SEPADirectDebitBrowserSwitchResult`, and `SEPADirectDebitBrowserSwitchResultCallback` + * Remove Fragment or Activity requirement from `SEPADirectDebitClient` constructor + * Add additional callback param to `SEPADirectDebitClient#tokenize` ## unreleased From d542165b834b44a97fd66384bea8ead9d105c98b Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 09:51:03 -0500 Subject: [PATCH 18/29] Use lambda syntax in SEPA demo fragment --- .../demo/SEPADirectDebitFragment.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java index e4eebd6e45..0f60a03678 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java +++ b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java @@ -35,18 +35,15 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, BraintreeClient braintreeClient = getBraintreeClient(); sepaDirectDebitClient = new SEPADirectDebitClient(braintreeClient); - sepaDirectDebitLauncher = new SEPADirectDebitLauncher(new SEPADirectDebitLauncherCallback() { - @Override - public void onResult(@NonNull SEPADirectDebitBrowserSwitchResult sepaDirectDebitBrowserSwitchResult) { - sepaDirectDebitClient.onBrowserSwitchResult(sepaDirectDebitBrowserSwitchResult, (sepaDirectDebitNonce, error) -> { - if (error != null) { - handleError(error); - } else { - handleSEPANonce(sepaDirectDebitNonce); - } - }); - } - }); + sepaDirectDebitLauncher = new SEPADirectDebitLauncher(sepaDirectDebitBrowserSwitchResult -> + sepaDirectDebitClient.onBrowserSwitchResult(sepaDirectDebitBrowserSwitchResult, (sepaDirectDebitNonce, error) -> { + if (error != null) { + handleError(error); + } else { + handleSEPANonce(sepaDirectDebitNonce); + } + }) + ); return view; } From 16d1fcd0e3995d2d6804c4c4b525e85eda3e5973 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 10:31:55 -0500 Subject: [PATCH 19/29] PR Feedback - remove no-longer-needed Activity argument from SEPAClient.tokenize(), add docstring --- CHANGELOG.md | 2 +- .../com/braintreepayments/api/SEPADirectDebitClient.java | 4 +--- .../api/SEPADirectDebitFlowStartedCallback.java | 2 +- .../com/braintreepayments/api/SEPADirectDebitLauncher.java | 5 +++-- .../api/SEPADirectDebitLauncherCallback.java | 5 +++++ .../com/braintreepayments/api/SEPADirectDebitResponse.java | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3899a33bf1..0997d22781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ * Add `SEPADirectDebitLauncher`, `SEPADirectDebitLauncherCallback`, `SEPADirectDebitFlowStartedCallback`, `SEPADirectDebitResponse`, `SEPADirectDebitBrowserSwitchResult`, and `SEPADirectDebitBrowserSwitchResultCallback` * Remove Fragment or Activity requirement from `SEPADirectDebitClient` constructor - * Add additional callback param to `SEPADirectDebitClient#tokenize` + * Modify `SEPADirectDebitClient#tokenize` parameters ## unreleased diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index 4abb1b6042..35b332fb6b 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -48,12 +48,10 @@ public SEPADirectDebitClient(@NonNull BraintreeClient braintreeClient) { * to launch the SEPA mandate flow in * {@link SEPADirectDebitLauncher#launch(FragmentActivity, SEPADirectDebitResponse)} * - * @param activity Android FragmentActivity * @param sepaDirectDebitRequest {@link SEPADirectDebitRequest} * @param callback {@link SEPADirectDebitFlowStartedCallback} */ - public void tokenize(@NonNull final FragmentActivity activity, - @NonNull final SEPADirectDebitRequest sepaDirectDebitRequest, + public void tokenize(@NonNull final SEPADirectDebitRequest sepaDirectDebitRequest, @NonNull final SEPADirectDebitFlowStartedCallback callback) { braintreeClient.sendAnalyticsEvent("sepa-direct-debit.selected.started"); braintreeClient.sendAnalyticsEvent("sepa-direct-debit.create-mandate.requested"); diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java index 23ff375f3e..00094c44b5 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java @@ -5,7 +5,7 @@ /** * Callback for receiving result of - * {@link SEPADirectDebitClient#tokenize(FragmentActivity, SEPADirectDebitRequest, SEPADirectDebitFlowStartedCallback)}. + * {@link SEPADirectDebitClient#tokenize(SEPADirectDebitRequest, SEPADirectDebitFlowStartedCallback)}. */ public interface SEPADirectDebitFlowStartedCallback { diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java index e3e0ee404b..0d7fc60002 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncher.java @@ -41,7 +41,7 @@ public SEPADirectDebitLauncher(@NonNull SEPADirectDebitLauncherCallback callback * * @param activity an Android {@link FragmentActivity} * @param sepaDirectDebitResponse the result of the SEPA mandate received from invoking - * {@link SEPADirectDebitClient#tokenize(FragmentActivity, SEPADirectDebitRequest, SEPADirectDebitFlowStartedCallback)} + * {@link SEPADirectDebitClient#tokenize(SEPADirectDebitRequest, SEPADirectDebitFlowStartedCallback)} */ public void launch(@NonNull FragmentActivity activity, @NonNull SEPADirectDebitResponse sepaDirectDebitResponse) { try { @@ -69,7 +69,8 @@ public void launch(@NonNull FragmentActivity activity, @NonNull SEPADirectDebitR * @param context the context used to check for pending results * @param intent the intent to return to your application containing a deep link result from * the SEPA mandate flow - */ public void handleReturnToAppFromBrowser(@NonNull Context context, @NonNull Intent intent) { + */ + public void handleReturnToAppFromBrowser(@NonNull Context context, @NonNull Intent intent) { BrowserSwitchResult result = browserSwitchClient.parseResult(context, SEPA_DEBIT, intent); if (result != null) { callback.onResult(new SEPADirectDebitBrowserSwitchResult(result)); diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncherCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncherCallback.java index 48a24c39fa..aa2ed6f23c 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncherCallback.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitLauncherCallback.java @@ -2,6 +2,11 @@ import androidx.annotation.NonNull; +/** + * Used to receive notification that the SEPA mandate web browser flow completed. + * Once this is invoked, continue the flow by calling + * {@link SEPADirectDebitClient#onBrowserSwitchResult(SEPADirectDebitBrowserSwitchResult, SEPADirectDebitBrowserSwitchResultCallback)} + */ public interface SEPADirectDebitLauncherCallback { void onResult(@NonNull SEPADirectDebitBrowserSwitchResult sepaDirectDebitBrowserSwitchResult); diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java index 41d3ccfbf0..76184b8946 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java @@ -4,7 +4,7 @@ /** * Returned via the {@link SEPADirectDebitFlowStartedCallback} after calling - * {@link SEPADirectDebitClient#tokenize(FragmentActivity, SEPADirectDebitRequest, SEPADirectDebitFlowStartedCallback)}. + * {@link SEPADirectDebitClient#tokenize(SEPADirectDebitRequest, SEPADirectDebitFlowStartedCallback)}. * * Inspect the {@link SEPADirectDebitNonce} property to determine if tokenization is complete, or * if you must continue the SEPA mandate web flow via From 90e5dff966ce01747e5b8a16537b921335b24f8d Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 10:58:17 -0500 Subject: [PATCH 20/29] Fixup - fix SEPAClient.tokenize() callsite to remove Activity arg --- .../com/braintreepayments/demo/SEPADirectDebitFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java index 0f60a03678..af13214ff9 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java +++ b/Demo/src/main/java/com/braintreepayments/demo/SEPADirectDebitFragment.java @@ -71,7 +71,7 @@ public void launchSEPADirectDebit(View view) { request.setBillingAddress(billingAddress); request.setMerchantAccountId("EUR-sepa-direct-debit"); - sepaDirectDebitClient.tokenize(requireActivity(), request, (sepaDirectDebitResponse, error) -> { + sepaDirectDebitClient.tokenize(request, (sepaDirectDebitResponse, error) -> { if (error != null) { handleError(error); } else if (sepaDirectDebitResponse.getNonce() != null) { // web-flow mandate not required From 8a78c254e58c4e83022edf3bd8f69c9d085aa928 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 11:02:09 -0500 Subject: [PATCH 21/29] Delete SEPADirectDebitLifecycleObserverUnitTest.java --- ...ADirectDebitLifecycleObserverUnitTest.java | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserverUnitTest.java diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserverUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserverUnitTest.java deleted file mode 100644 index ca356bc0ab..0000000000 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLifecycleObserverUnitTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.braintreepayments.api; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.lifecycle.Lifecycle; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -@RunWith(RobolectricTestRunner.class) -public class SEPADirectDebitLifecycleObserverUnitTest { - - @Test - public void onResume_whenLifeCycleObserverIsFragment_payPalClientDeliversResultWithFragmentActivity() { - Fragment fragment = mock(Fragment.class); - FragmentActivity activity = mock(FragmentActivity.class); - when(fragment.getActivity()).thenReturn(activity); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getRequestCode()).thenReturn(BraintreeRequestCodes.SEPA_DEBIT); - - SEPADirectDebitClient sepaDirectDebitClient = mock(SEPADirectDebitClient.class); - when(sepaDirectDebitClient.getBrowserSwitchResult(activity)).thenReturn( - browserSwitchResult); - - SEPADirectDebitLifecycleObserver sut = - new SEPADirectDebitLifecycleObserver(sepaDirectDebitClient); - - sut.onStateChanged(fragment, Lifecycle.Event.ON_RESUME); - - verify(sepaDirectDebitClient).onBrowserSwitchResult(same(activity)); - } - - @Test - public void onResume_whenLifeCycleObserverIsActivity_payPalClientDeliversResultWithSameActivity() { - FragmentActivity activity = mock(FragmentActivity.class); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getRequestCode()).thenReturn(BraintreeRequestCodes.SEPA_DEBIT); - - SEPADirectDebitClient sepaDirectDebitClient = mock(SEPADirectDebitClient.class); - when(sepaDirectDebitClient.getBrowserSwitchResult(activity)).thenReturn( - browserSwitchResult); - - SEPADirectDebitLifecycleObserver sut = - new SEPADirectDebitLifecycleObserver(sepaDirectDebitClient); - - sut.onStateChanged(activity, Lifecycle.Event.ON_RESUME); - - verify(sepaDirectDebitClient).onBrowserSwitchResult(same(activity)); - } - - @Test - public void onResume_whenPendingBrowserSwitchResultExists_andRequestCodeNotPayPal_doesNothing() { - FragmentActivity activity = mock(FragmentActivity.class); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getRequestCode()).thenReturn(BraintreeRequestCodes.PAYPAL); - - SEPADirectDebitClient sepaDirectDebitClient = mock(SEPADirectDebitClient.class); - when(sepaDirectDebitClient.getBrowserSwitchResult(activity)).thenReturn( - browserSwitchResult); - - SEPADirectDebitLifecycleObserver sut = - new SEPADirectDebitLifecycleObserver(sepaDirectDebitClient); - - sut.onStateChanged(activity, Lifecycle.Event.ON_RESUME); - - verify(sepaDirectDebitClient, never()).onBrowserSwitchResult(any(FragmentActivity.class)); - } -} From 43f365f9df45303f85dc877e72158e19c20e8cb9 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 12:07:38 -0500 Subject: [PATCH 22/29] WIP - begin updating SEPAClient unit tests --- .../api/SEPADirectDebitClientUnitTest.java | 658 ++++++++---------- 1 file changed, 302 insertions(+), 356 deletions(-) diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java index 3639f7cee2..32b56e4272 100644 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java @@ -2,9 +2,11 @@ import static junit.framework.TestCase.assertSame; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -14,6 +16,7 @@ import android.net.Uri; +import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.Lifecycle; @@ -24,26 +27,25 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class SEPADirectDebitClientUnitTest { private FragmentActivity activity; - private Lifecycle lifecycle; private BraintreeClient braintreeClient; - private SEPADirectDebitListener listener; private CreateMandateResult createMandateResult; private SEPADirectDebitRequest sepaDirectDebitRequest; + private SEPADirectDebitBrowserSwitchResultCallback sepaBrowserSwitchResultCallback; + private SEPADirectDebitFlowStartedCallback sepaFlowStartedCallback; @Before public void beforeEach() { activity = mock(FragmentActivity.class); - lifecycle = mock(Lifecycle.class); braintreeClient = new MockBraintreeClientBuilder() .returnUrlScheme("com.example") .build(); - listener = mock(SEPADirectDebitListener.class); createMandateResult = new CreateMandateResult( "http://www.example.com", @@ -53,92 +55,34 @@ public void beforeEach() { "ONE_OFF" ); sepaDirectDebitRequest = new SEPADirectDebitRequest(); - } - - @Test - public void constructor_setsLifecycleObserver() { - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); - BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(SEPADirectDebitLifecycleObserver.class); - verify(lifecycle).addObserver(captor.capture()); - - SEPADirectDebitLifecycleObserver observer = captor.getValue(); - assertSame(sut, observer.sepaDirectDebitClient); - } - - @Test - public void constructor_withFragment_passesFragmentLifecycleAndActivityToObserver() { - BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - - Fragment fragment = mock(Fragment.class); - when(fragment.getActivity()).thenReturn(activity); - when(fragment.getLifecycle()).thenReturn(lifecycle); - - SEPADirectDebitClient sut = new SEPADirectDebitClient(fragment, braintreeClient); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(SEPADirectDebitLifecycleObserver.class); - verify(lifecycle).addObserver(captor.capture()); - - SEPADirectDebitLifecycleObserver observer = captor.getValue(); - assertSame(sut, observer.sepaDirectDebitClient); - } - - @Test - public void constructor_withFragmentActivity_passesActivityLifecycleAndActivityToObserver() { - BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - - FragmentActivity activity = mock(FragmentActivity.class); - when(activity.getLifecycle()).thenReturn(lifecycle); - - SEPADirectDebitClient sut = new SEPADirectDebitClient(activity, braintreeClient); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(SEPADirectDebitLifecycleObserver.class); - verify(lifecycle).addObserver(captor.capture()); - - SEPADirectDebitLifecycleObserver observer = captor.getValue(); - assertSame(sut, observer.sepaDirectDebitClient); - } - - @Test - public void constructor_withoutFragmentOrActivity_doesNotSetObserver() { - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); - BraintreeClient braintreeClient = new MockBraintreeClientBuilder().build(); - SEPADirectDebitClient sut = - new SEPADirectDebitClient(null, null, braintreeClient, sepaDirectDebitApi); - - ArgumentCaptor captor = - ArgumentCaptor.forClass(SEPADirectDebitLifecycleObserver.class); - verify(lifecycle, never()).addObserver(captor.capture()); + sepaBrowserSwitchResultCallback = mock(SEPADirectDebitBrowserSwitchResultCallback.class); + sepaFlowStartedCallback = mock(SEPADirectDebitFlowStartedCallback.class); } @Test - public void tokenize_onCreateMandateRequestSuccess_launchesBrowserSwitch_andSendsAnalytics() + public void tokenize_onCreateMandateRequestSuccess_callsBackSEPAResponse_andSendsAnalytics() throws BrowserSwitchException, JSONException { SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() .createMandateResultSuccess(createMandateResult) .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.selected.started"); - ArgumentCaptor captor = - ArgumentCaptor.forClass(BrowserSwitchOptions.class); - verify(braintreeClient).startBrowserSwitch(same(activity), captor.capture()); + ArgumentCaptor captor = + ArgumentCaptor.forClass(SEPADirectDebitResponse.class); + verify(sepaFlowStartedCallback).onResult(captor.capture(), isNull()); + + SEPADirectDebitResponse sepaResponseResult = captor.getValue(); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.requested"); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.success"); - BrowserSwitchOptions browserSwitchOptions = captor.getValue(); + BrowserSwitchOptions browserSwitchOptions = sepaResponseResult.getBrowserSwitchOptions(); assertEquals(Uri.parse("http://www.example.com"), browserSwitchOptions.getUrl()); assertEquals("com.example", browserSwitchOptions.getReturnUrlScheme()); assertEquals(BraintreeRequestCodes.SEPA_DEBIT, browserSwitchOptions.getRequestCode()); @@ -150,7 +94,7 @@ public void tokenize_onCreateMandateRequestSuccess_launchesBrowserSwitch_andSend } @Test - public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeSuccess_forwardsResultToListener_andSendsAnalytics() + public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeSuccess_callsBackResultWithNonce_andSendsAnalytics() throws JSONException { // null approval URL indicates mandate approved createMandateResult = new CreateMandateResult( @@ -169,42 +113,44 @@ public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_on .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); - verify(listener).onSEPADirectDebitSuccess(nonce); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.success"); + sut.tokenize(sepaDirectDebitRequest, (sepaDirectDebitResponse, error) -> { + assertEquals(sepaDirectDebitResponse.getNonce(), nonce); + assertNull(error); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.success"); + }); } - @Test - public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeFailure_forwardsErrorToListener_andSendsAnalytics() { - // null approval URL indicates mandate approved - createMandateResult = new CreateMandateResult( - "null", - "1234", - "fake-customer-id", - "fake-bank-reference-token", - "ONE_OFF" - ); - - Exception exception = new Exception("tokenize error"); - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() - .createMandateResultSuccess(createMandateResult) - .tokenizeError(exception) - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.tokenize(activity, sepaDirectDebitRequest); - verify(listener).onSEPADirectDebitFailure(exception); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); - } +// @Test +// public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeFailure_callsBackError_andSendsAnalytics() { +// // null approval URL indicates mandate approved +// createMandateResult = new CreateMandateResult( +// "null", +// "1234", +// "fake-customer-id", +// "fake-bank-reference-token", +// "ONE_OFF" +// ); +// +// Exception exception = new Exception("tokenize error"); +// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() +// .createMandateResultSuccess(createMandateResult) +// .tokenizeError(exception) +// .build(); +// +// SEPADirectDebitClient sut = +// new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); +// +// sut.tokenize(sepaDirectDebitRequest, (sepaDirectDebitResponse, error) -> { +// assertEquals(error, exception); +// assertNull(sepaDirectDebitResponse); +// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); +// }); +// } @Test - public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLInvalid_returnsErrorToListener() { + public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLInvalid_callsBackError() { createMandateResult = new CreateMandateResult( "", "1234", @@ -218,18 +164,14 @@ public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLInvalid_return .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.tokenize(activity, sepaDirectDebitRequest); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); - - Exception exception = captor.getValue(); - assertTrue(exception instanceof BraintreeException); - assertEquals("An unexpected error occurred.", exception.getMessage()); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + sut.tokenize(sepaDirectDebitRequest, (response, error) -> { + assertNull(response); + assertTrue(error instanceof BraintreeException); + assertEquals("An unexpected error occurred.", error.getMessage()); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); + }); } @Test @@ -248,10 +190,9 @@ public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLNull_callsSEPA .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(activity, sepaDirectDebitRequest); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); verify(braintreeClient, never()).startBrowserSwitch(any(FragmentActivity.class), any(BrowserSwitchOptions.class)); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.success"); @@ -261,29 +202,29 @@ public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLNull_callsSEPA any(SEPADirectDebitTokenizeCallback.class)); } - @Test - public void tokenize_onCreateMandateRequestSuccess_whenStartBrowserSwitchFails_returnsErrorToListener_andSendsAnalytics() - throws BrowserSwitchException { - doThrow(BrowserSwitchException.class).when(braintreeClient) - .startBrowserSwitch(any(FragmentActivity.class), any(BrowserSwitchOptions.class)); - - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() - .createMandateResultSuccess(createMandateResult) - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.tokenize(activity, sepaDirectDebitRequest); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); - - Exception exception = captor.getValue(); - assertTrue(exception instanceof BrowserSwitchException); - } +// @Test +// public void tokenize_onCreateMandateRequestSuccess_whenStartBrowserSwitchFails_returnsErrorToListener_andSendsAnalytics() +// throws BrowserSwitchException { +// doThrow(BrowserSwitchException.class).when(braintreeClient) +// .startBrowserSwitch(any(FragmentActivity.class), any(BrowserSwitchOptions.class)); +// +// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() +// .createMandateResultSuccess(createMandateResult) +// .build(); +// +// SEPADirectDebitClient sut = +// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); +// sut.setListener(listener); +// +// sut.tokenize(activity, sepaDirectDebitRequest); +// +// ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); +// verify(listener).onSEPADirectDebitFailure(captor.capture()); +// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); +// +// Exception exception = captor.getValue(); +// assertTrue(exception instanceof BrowserSwitchException); +// } @Test public void tokenize_onCreateMandateError_returnsErrorToListener_andSendsAnalytics() { @@ -293,40 +234,40 @@ public void tokenize_onCreateMandateError_returnsErrorToListener_andSendsAnalyti .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.tokenize(activity, sepaDirectDebitRequest); - - verify(listener).onSEPADirectDebitFailure(same(error)); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); - } - - @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusCanceled_returnsUserCanceledExceptionToListener_andSendsAnalytics() { - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.CANCELED); - braintreeClient = new MockBraintreeClientBuilder() - .deliverBrowserSwitchResult(browserSwitchResult) - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.onBrowserSwitchResult(activity); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.canceled"); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - Exception exception = captor.getValue(); - assertTrue(exception instanceof UserCanceledException); - assertEquals("User canceled SEPA Debit.", exception.getMessage()); + sut.tokenize(sepaDirectDebitRequest, (SEPADirectDebitFlowStartedCallback) (sepaDirectDebitResponse, actualError) -> { + assertNull(sepaDirectDebitResponse); + assertEquals(actualError, error); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); + }); } +// @Test +// public void onBrowserSwitchResult_whenBrowserSwitchStatusCanceled_returnsUserCanceledExceptionToListener_andSendsAnalytics() { +// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); +// +// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); +// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.CANCELED); +// braintreeClient = new MockBraintreeClientBuilder() +// .deliverBrowserSwitchResult(browserSwitchResult) +// .build(); +// +// SEPADirectDebitClient sut = +// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); +// sut.setListener(listener); +// +// sut.onBrowserSwitchResult(activity); +// +// ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); +// verify(listener).onSEPADirectDebitFailure(captor.capture()); +// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.canceled"); +// +// Exception exception = captor.getValue(); +// assertTrue(exception instanceof UserCanceledException); +// assertEquals("User canceled SEPA Debit.", exception.getMessage()); +// } +// @Test public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkContainsSuccess_callsTokenize_andSendsAnalytics() throws JSONException { @@ -349,91 +290,21 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkCon .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.onBrowserSwitchResult(activity); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.success"); - verify(sepaDirectDebitApi).tokenize(eq("1234"), eq("customer-id"), - eq("bank-reference-token"), eq("ONE_OFF"), - any(SEPADirectDebitTokenizeCallback.class)); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.requested"); - } - - @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_forwardsResultToListener_andSendsAnalytics() - throws JSONException { - SEPADirectDebitNonce nonce = SEPADirectDebitNonce.fromJSON( - new JSONObject(Fixtures.SEPA_DEBIT_TOKENIZE_RESPONSE)); - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() - .tokenizeSuccess(nonce) - .build(); - - JSONObject metadata = new JSONObject() - .put("ibanLastFour", "1234") - .put("customerId", "customer-id") - .put("bankReferenceToken", "bank-reference-token") - .put("mandateType", "ONE_OFF"); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); - when(browserSwitchResult.getDeepLinkUrl()).thenReturn( - Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); - when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); - - braintreeClient = new MockBraintreeClientBuilder() - .deliverBrowserSwitchResult(browserSwitchResult) - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.onBrowserSwitchResult(activity); - - verify(listener).onSEPADirectDebitSuccess(nonce); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.success"); - } - - @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_forwardsErrorToListener_andSendsAnalytics() - throws JSONException { - Exception exception = new Exception("tokenize error"); - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() - .tokenizeError(exception) - .build(); - - JSONObject metadata = new JSONObject() - .put("ibanLastFour", "1234") - .put("customerId", "customer-id") - .put("bankReferenceToken", "bank-reference-token") - .put("mandateType", "ONE_OFF"); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); - when(browserSwitchResult.getDeepLinkUrl()).thenReturn( - Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); - when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - braintreeClient = new MockBraintreeClientBuilder() - .deliverBrowserSwitchResult(browserSwitchResult) - .build(); + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); - sut.onBrowserSwitchResult(activity); - - verify(listener).onSEPADirectDebitFailure(exception); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.success"); verify(sepaDirectDebitApi).tokenize(eq("1234"), eq("customer-id"), eq("bank-reference-token"), eq("ONE_OFF"), any(SEPADirectDebitTokenizeCallback.class)); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.requested"); } @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_forwardsResultToListener() + public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_callsBackNonce_andSendsAnalytics() throws JSONException { SEPADirectDebitNonce nonce = SEPADirectDebitNonce.fromJSON( new JSONObject(Fixtures.SEPA_DEBIT_TOKENIZE_RESPONSE)); @@ -458,112 +329,187 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSucce .build(); SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.onBrowserSwitchResult(activity); - - verify(listener).onSEPADirectDebitSuccess(nonce); - } + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_forwardsErrorToListener() - throws JSONException { - Exception exception = new Exception("tokenize error"); - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() - .tokenizeError(exception) - .build(); + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); - JSONObject metadata = new JSONObject() - .put("ibanLastFour", "1234") - .put("customerId", "customer-id") - .put("bankReferenceToken", "bank-reference-token") - .put("mandateType", "ONE_OFF"); + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); - when(browserSwitchResult.getDeepLinkUrl()).thenReturn( - Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); - when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); - - braintreeClient = new MockBraintreeClientBuilder() - .deliverBrowserSwitchResult(browserSwitchResult) - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.onBrowserSwitchResult(activity); - - verify(listener).onSEPADirectDebitFailure(exception); - } - - @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkContainsCancel_returnsErrorToListener_andSendsAnalytics() { - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); - when(browserSwitchResult.getDeepLinkUrl()).thenReturn(Uri.parse( - "com.braintreepayments.demo.braintree://sepa/cancel?error_code=internal_error")); - - braintreeClient = new MockBraintreeClientBuilder() - .deliverBrowserSwitchResult(browserSwitchResult) - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.onBrowserSwitchResult(activity); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); - - Exception exception = captor.getValue(); - assertTrue(exception instanceof BraintreeException); - assertEquals("An unexpected error occurred.", exception.getMessage()); - } - - @Test - public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkURLIsNull_returnsErrorToListener() { - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); - - BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); - when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); - when(browserSwitchResult.getDeepLinkUrl()).thenReturn(null); - - braintreeClient = new MockBraintreeClientBuilder() - .deliverBrowserSwitchResult(browserSwitchResult) - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.setListener(listener); - - sut.onBrowserSwitchResult(activity); - - ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); - verify(listener).onSEPADirectDebitFailure(captor.capture()); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); - - Exception exception = captor.getValue(); - assertTrue(exception instanceof BraintreeException); - assertEquals("Unknown error", exception.getMessage()); - } - - @Test - public void getBrowserSwitchResult_getBrowserSwitchResultFromBraintreeClient() { - SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); - - braintreeClient = new MockBraintreeClientBuilder() - .build(); - - SEPADirectDebitClient sut = - new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); - sut.getBrowserSwitchResult(activity); - verify(braintreeClient).getBrowserSwitchResult(activity); + verify(sepaBrowserSwitchResultCallback).onResult(eq(nonce), isNull()); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.success"); } -} \ No newline at end of file +// +// @Test +// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_forwardsErrorToListener_andSendsAnalytics() +// throws JSONException { +// Exception exception = new Exception("tokenize error"); +// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() +// .tokenizeError(exception) +// .build(); +// +// JSONObject metadata = new JSONObject() +// .put("ibanLastFour", "1234") +// .put("customerId", "customer-id") +// .put("bankReferenceToken", "bank-reference-token") +// .put("mandateType", "ONE_OFF"); +// +// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); +// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); +// when(browserSwitchResult.getDeepLinkUrl()).thenReturn( +// Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); +// when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); +// +// braintreeClient = new MockBraintreeClientBuilder() +// .deliverBrowserSwitchResult(browserSwitchResult) +// .build(); +// +// SEPADirectDebitClient sut = +// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); +// sut.setListener(listener); +// +// sut.onBrowserSwitchResult(activity); +// +// verify(listener).onSEPADirectDebitFailure(exception); +// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); +// verify(sepaDirectDebitApi).tokenize(eq("1234"), eq("customer-id"), +// eq("bank-reference-token"), eq("ONE_OFF"), +// any(SEPADirectDebitTokenizeCallback.class)); +// } +// +// @Test +// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_forwardsResultToListener() +// throws JSONException { +// SEPADirectDebitNonce nonce = SEPADirectDebitNonce.fromJSON( +// new JSONObject(Fixtures.SEPA_DEBIT_TOKENIZE_RESPONSE)); +// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() +// .tokenizeSuccess(nonce) +// .build(); +// +// JSONObject metadata = new JSONObject() +// .put("ibanLastFour", "1234") +// .put("customerId", "customer-id") +// .put("bankReferenceToken", "bank-reference-token") +// .put("mandateType", "ONE_OFF"); +// +// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); +// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); +// when(browserSwitchResult.getDeepLinkUrl()).thenReturn( +// Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); +// when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); +// +// braintreeClient = new MockBraintreeClientBuilder() +// .deliverBrowserSwitchResult(browserSwitchResult) +// .build(); +// +// SEPADirectDebitClient sut = +// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); +// sut.setListener(listener); +// +// sut.onBrowserSwitchResult(activity); +// +// verify(listener).onSEPADirectDebitSuccess(nonce); +// } +// +// @Test +// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_forwardsErrorToListener() +// throws JSONException { +// Exception exception = new Exception("tokenize error"); +// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() +// .tokenizeError(exception) +// .build(); +// +// JSONObject metadata = new JSONObject() +// .put("ibanLastFour", "1234") +// .put("customerId", "customer-id") +// .put("bankReferenceToken", "bank-reference-token") +// .put("mandateType", "ONE_OFF"); +// +// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); +// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); +// when(browserSwitchResult.getDeepLinkUrl()).thenReturn( +// Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); +// when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); +// +// braintreeClient = new MockBraintreeClientBuilder() +// .deliverBrowserSwitchResult(browserSwitchResult) +// .build(); +// +// SEPADirectDebitClient sut = +// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); +// sut.setListener(listener); +// +// sut.onBrowserSwitchResult(activity); +// +// verify(listener).onSEPADirectDebitFailure(exception); +// } +// +// @Test +// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkContainsCancel_returnsErrorToListener_andSendsAnalytics() { +// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); +// +// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); +// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); +// when(browserSwitchResult.getDeepLinkUrl()).thenReturn(Uri.parse( +// "com.braintreepayments.demo.braintree://sepa/cancel?error_code=internal_error")); +// +// braintreeClient = new MockBraintreeClientBuilder() +// .deliverBrowserSwitchResult(browserSwitchResult) +// .build(); +// +// SEPADirectDebitClient sut = +// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); +// sut.setListener(listener); +// +// sut.onBrowserSwitchResult(activity); +// +// ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); +// verify(listener).onSEPADirectDebitFailure(captor.capture()); +// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); +// +// Exception exception = captor.getValue(); +// assertTrue(exception instanceof BraintreeException); +// assertEquals("An unexpected error occurred.", exception.getMessage()); +// } +// +// @Test +// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkURLIsNull_returnsErrorToListener() { +// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); +// +// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); +// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); +// when(browserSwitchResult.getDeepLinkUrl()).thenReturn(null); +// +// braintreeClient = new MockBraintreeClientBuilder() +// .deliverBrowserSwitchResult(browserSwitchResult) +// .build(); +// +// SEPADirectDebitClient sut = +// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); +// sut.setListener(listener); +// +// sut.onBrowserSwitchResult(activity); +// +// ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); +// verify(listener).onSEPADirectDebitFailure(captor.capture()); +// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); +// +// Exception exception = captor.getValue(); +// assertTrue(exception instanceof BraintreeException); +// assertEquals("Unknown error", exception.getMessage()); +// } +} + +// Save for SEPABrowserSwitchResult_Tests + +// @Test +// public void getBrowserSwitchResult_getBrowserSwitchResultFromBraintreeClient() { +// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); +// +// braintreeClient = new MockBraintreeClientBuilder() +// .build(); +// +// SEPADirectDebitClient sut = +// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); +// sut.getBrowserSwitchResult(activity); +// verify(braintreeClient).getBrowserSwitchResult(activity); +// } \ No newline at end of file From e6299b4b5d3b51f1fc99e2e9127329f59f71cdff Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 12:08:08 -0500 Subject: [PATCH 23/29] Add nullable annotations to SEPAResponse & nonce properties --- .../SEPADirectDebitFlowStartedCallback.java | 2 +- .../api/SEPADirectDebitResponse.java | 3 +- .../api/SEPADirectDebitClientUnitTest.java | 427 ++++++++---------- 3 files changed, 186 insertions(+), 246 deletions(-) diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java index 00094c44b5..442487e970 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitFlowStartedCallback.java @@ -16,5 +16,5 @@ public interface SEPADirectDebitFlowStartedCallback { * {@link SEPADirectDebitLauncher#launch(FragmentActivity, SEPADirectDebitResponse)} * @param error an exception that occurred while initiating the SEPA transaction */ - void onResult(SEPADirectDebitResponse sepaDirectDebitResponse, @Nullable Exception error); + void onResult(@Nullable SEPADirectDebitResponse sepaDirectDebitResponse, @Nullable Exception error); } diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java index 76184b8946..6e4ddf15c8 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitResponse.java @@ -1,5 +1,6 @@ package com.braintreepayments.api; +import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; /** @@ -31,7 +32,7 @@ BrowserSwitchOptions getBrowserSwitchOptions() { * * @return {@link SEPADirectDebitNonce} */ - public SEPADirectDebitNonce getNonce() { + public @Nullable SEPADirectDebitNonce getNonce() { return this.sepaDirectDebitNonce; } } diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java index 32b56e4272..cb8c171085 100644 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java @@ -1,14 +1,11 @@ package com.braintreepayments.api; -import static junit.framework.TestCase.assertSame; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -16,10 +13,7 @@ import android.net.Uri; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; -import androidx.lifecycle.Lifecycle; import org.json.JSONException; import org.json.JSONObject; @@ -27,13 +21,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class SEPADirectDebitClientUnitTest { - private FragmentActivity activity; private BraintreeClient braintreeClient; private CreateMandateResult createMandateResult; private SEPADirectDebitRequest sepaDirectDebitRequest; @@ -42,7 +34,6 @@ public class SEPADirectDebitClientUnitTest { @Before public void beforeEach() { - activity = mock(FragmentActivity.class); braintreeClient = new MockBraintreeClientBuilder() .returnUrlScheme("com.example") .build(); @@ -62,7 +53,7 @@ public void beforeEach() { @Test public void tokenize_onCreateMandateRequestSuccess_callsBackSEPAResponse_andSendsAnalytics() - throws BrowserSwitchException, JSONException { + throws JSONException { SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() .createMandateResultSuccess(createMandateResult) .build(); @@ -122,32 +113,32 @@ public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_on }); } -// @Test -// public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeFailure_callsBackError_andSendsAnalytics() { -// // null approval URL indicates mandate approved -// createMandateResult = new CreateMandateResult( -// "null", -// "1234", -// "fake-customer-id", -// "fake-bank-reference-token", -// "ONE_OFF" -// ); -// -// Exception exception = new Exception("tokenize error"); -// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() -// .createMandateResultSuccess(createMandateResult) -// .tokenizeError(exception) -// .build(); -// -// SEPADirectDebitClient sut = -// new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); -// -// sut.tokenize(sepaDirectDebitRequest, (sepaDirectDebitResponse, error) -> { -// assertEquals(error, exception); -// assertNull(sepaDirectDebitResponse); -// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); -// }); -// } + @Test + public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_onTokenizeFailure_callsBackError_andSendsAnalytics() { + // null approval URL indicates mandate approved + createMandateResult = new CreateMandateResult( + "null", + "1234", + "fake-customer-id", + "fake-bank-reference-token", + "ONE_OFF" + ); + + Exception exception = new Exception("tokenize error"); + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() + .createMandateResultSuccess(createMandateResult) + .tokenizeError(exception) + .build(); + + SEPADirectDebitClient sut = + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + sut.tokenize(sepaDirectDebitRequest, (sepaDirectDebitResponse, error) -> { + assertEquals(error, exception); + assertNull(sepaDirectDebitResponse); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); + }); + } @Test public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLInvalid_callsBackError() { @@ -202,30 +193,6 @@ public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLNull_callsSEPA any(SEPADirectDebitTokenizeCallback.class)); } -// @Test -// public void tokenize_onCreateMandateRequestSuccess_whenStartBrowserSwitchFails_returnsErrorToListener_andSendsAnalytics() -// throws BrowserSwitchException { -// doThrow(BrowserSwitchException.class).when(braintreeClient) -// .startBrowserSwitch(any(FragmentActivity.class), any(BrowserSwitchOptions.class)); -// -// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() -// .createMandateResultSuccess(createMandateResult) -// .build(); -// -// SEPADirectDebitClient sut = -// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); -// sut.setListener(listener); -// -// sut.tokenize(activity, sepaDirectDebitRequest); -// -// ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); -// verify(listener).onSEPADirectDebitFailure(captor.capture()); -// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); -// -// Exception exception = captor.getValue(); -// assertTrue(exception instanceof BrowserSwitchException); -// } - @Test public void tokenize_onCreateMandateError_returnsErrorToListener_andSendsAnalytics() { Exception error = new Exception("error"); @@ -243,31 +210,32 @@ public void tokenize_onCreateMandateError_returnsErrorToListener_andSendsAnalyti }); } -// @Test -// public void onBrowserSwitchResult_whenBrowserSwitchStatusCanceled_returnsUserCanceledExceptionToListener_andSendsAnalytics() { -// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); -// -// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); -// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.CANCELED); -// braintreeClient = new MockBraintreeClientBuilder() -// .deliverBrowserSwitchResult(browserSwitchResult) -// .build(); -// -// SEPADirectDebitClient sut = -// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); -// sut.setListener(listener); -// -// sut.onBrowserSwitchResult(activity); -// -// ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); -// verify(listener).onSEPADirectDebitFailure(captor.capture()); -// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.canceled"); -// -// Exception exception = captor.getValue(); -// assertTrue(exception instanceof UserCanceledException); -// assertEquals("User canceled SEPA Debit.", exception.getMessage()); -// } -// + @Test + public void onBrowserSwitchResult_whenBrowserSwitchStatusCanceled_callsBackUserCanceledException_andSendsAnalytics() { + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); + + BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); + when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.CANCELED); + braintreeClient = new MockBraintreeClientBuilder() + .deliverBrowserSwitchResult(browserSwitchResult) + .build(); + + SEPADirectDebitClient sut = + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); + + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.canceled"); + + Exception exception = captor.getValue(); + assertTrue(exception instanceof UserCanceledException); + assertEquals("User canceled SEPA Debit.", exception.getMessage()); + } + @Test public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkContainsSuccess_callsTokenize_andSendsAnalytics() throws JSONException { @@ -338,165 +306,136 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSucce verify(sepaBrowserSwitchResultCallback).onResult(eq(nonce), isNull()); verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.success"); } -// -// @Test -// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_forwardsErrorToListener_andSendsAnalytics() -// throws JSONException { -// Exception exception = new Exception("tokenize error"); -// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() -// .tokenizeError(exception) -// .build(); -// -// JSONObject metadata = new JSONObject() -// .put("ibanLastFour", "1234") -// .put("customerId", "customer-id") -// .put("bankReferenceToken", "bank-reference-token") -// .put("mandateType", "ONE_OFF"); -// -// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); -// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); -// when(browserSwitchResult.getDeepLinkUrl()).thenReturn( -// Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); -// when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); -// -// braintreeClient = new MockBraintreeClientBuilder() -// .deliverBrowserSwitchResult(browserSwitchResult) -// .build(); -// -// SEPADirectDebitClient sut = -// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); -// sut.setListener(listener); -// -// sut.onBrowserSwitchResult(activity); -// -// verify(listener).onSEPADirectDebitFailure(exception); -// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); -// verify(sepaDirectDebitApi).tokenize(eq("1234"), eq("customer-id"), -// eq("bank-reference-token"), eq("ONE_OFF"), -// any(SEPADirectDebitTokenizeCallback.class)); -// } -// -// @Test -// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_forwardsResultToListener() -// throws JSONException { -// SEPADirectDebitNonce nonce = SEPADirectDebitNonce.fromJSON( -// new JSONObject(Fixtures.SEPA_DEBIT_TOKENIZE_RESPONSE)); -// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() -// .tokenizeSuccess(nonce) -// .build(); -// -// JSONObject metadata = new JSONObject() -// .put("ibanLastFour", "1234") -// .put("customerId", "customer-id") -// .put("bankReferenceToken", "bank-reference-token") -// .put("mandateType", "ONE_OFF"); -// -// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); -// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); -// when(browserSwitchResult.getDeepLinkUrl()).thenReturn( -// Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); -// when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); -// -// braintreeClient = new MockBraintreeClientBuilder() -// .deliverBrowserSwitchResult(browserSwitchResult) -// .build(); -// -// SEPADirectDebitClient sut = -// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); -// sut.setListener(listener); -// -// sut.onBrowserSwitchResult(activity); -// -// verify(listener).onSEPADirectDebitSuccess(nonce); -// } -// -// @Test -// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_forwardsErrorToListener() -// throws JSONException { -// Exception exception = new Exception("tokenize error"); -// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() -// .tokenizeError(exception) -// .build(); -// -// JSONObject metadata = new JSONObject() -// .put("ibanLastFour", "1234") -// .put("customerId", "customer-id") -// .put("bankReferenceToken", "bank-reference-token") -// .put("mandateType", "ONE_OFF"); -// -// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); -// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); -// when(browserSwitchResult.getDeepLinkUrl()).thenReturn( -// Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); -// when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); -// -// braintreeClient = new MockBraintreeClientBuilder() -// .deliverBrowserSwitchResult(browserSwitchResult) -// .build(); -// -// SEPADirectDebitClient sut = -// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); -// sut.setListener(listener); -// -// sut.onBrowserSwitchResult(activity); -// -// verify(listener).onSEPADirectDebitFailure(exception); -// } -// -// @Test -// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkContainsCancel_returnsErrorToListener_andSendsAnalytics() { -// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); -// -// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); -// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); -// when(browserSwitchResult.getDeepLinkUrl()).thenReturn(Uri.parse( -// "com.braintreepayments.demo.braintree://sepa/cancel?error_code=internal_error")); -// -// braintreeClient = new MockBraintreeClientBuilder() -// .deliverBrowserSwitchResult(browserSwitchResult) -// .build(); -// -// SEPADirectDebitClient sut = -// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); -// sut.setListener(listener); -// -// sut.onBrowserSwitchResult(activity); -// -// ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); -// verify(listener).onSEPADirectDebitFailure(captor.capture()); -// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); -// -// Exception exception = captor.getValue(); -// assertTrue(exception instanceof BraintreeException); -// assertEquals("An unexpected error occurred.", exception.getMessage()); -// } -// -// @Test -// public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkURLIsNull_returnsErrorToListener() { -// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); -// -// BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); -// when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); -// when(browserSwitchResult.getDeepLinkUrl()).thenReturn(null); -// -// braintreeClient = new MockBraintreeClientBuilder() -// .deliverBrowserSwitchResult(browserSwitchResult) -// .build(); -// -// SEPADirectDebitClient sut = -// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); -// sut.setListener(listener); -// -// sut.onBrowserSwitchResult(activity); -// -// ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); -// verify(listener).onSEPADirectDebitFailure(captor.capture()); -// verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); -// -// Exception exception = captor.getValue(); -// assertTrue(exception instanceof BraintreeException); -// assertEquals("Unknown error", exception.getMessage()); -// } + + @Test + public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeFailure_callsBackError_andSendsAnalytics() + throws JSONException { + Exception exception = new Exception("tokenize error"); + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() + .tokenizeError(exception) + .build(); + + JSONObject metadata = new JSONObject() + .put("ibanLastFour", "1234") + .put("customerId", "customer-id") + .put("bankReferenceToken", "bank-reference-token") + .put("mandateType", "ONE_OFF"); + + BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); + when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); + when(browserSwitchResult.getDeepLinkUrl()).thenReturn( + Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); + when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); + + braintreeClient = new MockBraintreeClientBuilder() + .deliverBrowserSwitchResult(browserSwitchResult) + .build(); + + SEPADirectDebitClient sut = + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); + + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); + + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), eq(exception)); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); + verify(sepaDirectDebitApi).tokenize(eq("1234"), eq("customer-id"), + eq("bank-reference-token"), eq("ONE_OFF"), + any(SEPADirectDebitTokenizeCallback.class)); + } + + @Test + public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_onTokenizeSuccess_callsBackResult() + throws JSONException { + SEPADirectDebitNonce nonce = SEPADirectDebitNonce.fromJSON( + new JSONObject(Fixtures.SEPA_DEBIT_TOKENIZE_RESPONSE)); + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder() + .tokenizeSuccess(nonce) + .build(); + + JSONObject metadata = new JSONObject() + .put("ibanLastFour", "1234") + .put("customerId", "customer-id") + .put("bankReferenceToken", "bank-reference-token") + .put("mandateType", "ONE_OFF"); + + BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); + when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); + when(browserSwitchResult.getDeepLinkUrl()).thenReturn( + Uri.parse("com.braintreepayments.demo.braintree://sepa/success?success=true")); + when(browserSwitchResult.getRequestMetadata()).thenReturn(metadata); + + braintreeClient = new MockBraintreeClientBuilder() + .deliverBrowserSwitchResult(browserSwitchResult) + .build(); + + SEPADirectDebitClient sut = + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); + + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); + + verify(sepaBrowserSwitchResultCallback).onResult(eq(nonce), isNull()); + } + + @Test + public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkContainsCancel_returnsErrorToListener_andSendsAnalytics() { + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); + + BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); + when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); + when(browserSwitchResult.getDeepLinkUrl()).thenReturn(Uri.parse( + "com.braintreepayments.demo.braintree://sepa/cancel?error_code=internal_error")); + + braintreeClient = new MockBraintreeClientBuilder() + .deliverBrowserSwitchResult(browserSwitchResult) + .build(); + + SEPADirectDebitClient sut = + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); + + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); + + Exception exception = captor.getValue(); + assertTrue(exception instanceof BraintreeException); + assertEquals("An unexpected error occurred.", exception.getMessage()); + } + + @Test + public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkURLIsNull_returnsErrorToListener() { + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); + + BrowserSwitchResult browserSwitchResult = mock(BrowserSwitchResult.class); + when(browserSwitchResult.getStatus()).thenReturn(BrowserSwitchStatus.SUCCESS); + when(browserSwitchResult.getDeepLinkUrl()).thenReturn(null); + + braintreeClient = new MockBraintreeClientBuilder() + .deliverBrowserSwitchResult(browserSwitchResult) + .build(); + + SEPADirectDebitClient sut = + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult sepaBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(browserSwitchResult); + + sut.onBrowserSwitchResult(sepaBrowserSwitchResult, sepaBrowserSwitchResultCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.browser-switch.failure"); + + Exception exception = captor.getValue(); + assertTrue(exception instanceof BraintreeException); + assertEquals("Unknown error", exception.getMessage()); + } } // Save for SEPABrowserSwitchResult_Tests From beb346acfd297014aae8e933e9e34020f2a8ee70 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 15:12:00 -0500 Subject: [PATCH 24/29] Add additional onBrowserSwitchResult() tests --- .../api/SEPADirectDebitClient.java | 1 + .../api/SEPADirectDebitClientUnitTest.java | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java index 35b332fb6b..fb0fe6b014 100644 --- a/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java +++ b/SEPADirectDebit/src/main/java/com/braintreepayments/api/SEPADirectDebitClient.java @@ -128,6 +128,7 @@ public void onBrowserSwitchResult(@NonNull SEPADirectDebitBrowserSwitchResult se if (browserSwitchResult == null) { callback.onResult(null, new BraintreeException("An unexpected error occurred.")); + return; } int result = browserSwitchResult.getStatus(); diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java index cb8c171085..ac4cda207a 100644 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java @@ -1,6 +1,7 @@ package com.braintreepayments.api; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -210,6 +211,45 @@ public void tokenize_onCreateMandateError_returnsErrorToListener_andSendsAnalyti }); } + @Test + public void onBrowserSwitchResult_whenErrorNotNull_callsBackError() { + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); + + braintreeClient = new MockBraintreeClientBuilder().build(); + + SEPADirectDebitClient sut = + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + Exception expectedError = new Exception("error"); + SEPADirectDebitBrowserSwitchResult payPalBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult(expectedError); + sut.onBrowserSwitchResult(payPalBrowserSwitchResult, sepaBrowserSwitchResultCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); + + assertNotNull(captor.getValue()); + assertEquals(expectedError, captor.getValue()); + } + + @Test + public void onBrowserSwitchResult_whenResultAndErrorNull_callsBackUnexpectedError() { + SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); + + braintreeClient = new MockBraintreeClientBuilder().build(); + + SEPADirectDebitClient sut = + new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); + + SEPADirectDebitBrowserSwitchResult payPalBrowserSwitchResult = new SEPADirectDebitBrowserSwitchResult((BrowserSwitchResult) null); + sut.onBrowserSwitchResult(payPalBrowserSwitchResult, sepaBrowserSwitchResultCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); + verify(sepaBrowserSwitchResultCallback).onResult(isNull(), captor.capture()); + + assertNotNull(captor.getValue()); + assertEquals("An unexpected error occurred.", captor.getValue().getMessage()); + } + @Test public void onBrowserSwitchResult_whenBrowserSwitchStatusCanceled_callsBackUserCanceledException_andSendsAnalytics() { SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); From e99fa8c95b30d2ed813ea22d091100a6d4e36ea3 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 15:17:19 -0500 Subject: [PATCH 25/29] Add SEPADirectDebitLauncherUnitTest file & tests to mirror PPLauncher file --- .../api/SEPADirectDebitLauncherUnitTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java new file mode 100644 index 0000000000..323c3ece67 --- /dev/null +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java @@ -0,0 +1,97 @@ +package com.braintreepayments.api; + +import static com.braintreepayments.api.BraintreeRequestCodes.SEPA_DEBIT; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Intent; + +import androidx.fragment.app.FragmentActivity; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class SEPADirectDebitLauncherUnitTest { + + + private BrowserSwitchClient browserSwitchClient; + private FragmentActivity activity; + private Intent intent; + private SEPADirectDebitLauncherCallback sepaLauncherCallback; + + @Before + public void beforeEach() { + browserSwitchClient = mock(BrowserSwitchClient.class); + activity = mock(FragmentActivity.class); + intent = new Intent(); + sepaLauncherCallback = mock(SEPADirectDebitLauncherCallback.class); + } + + @Test + public void launch_startsBrowserSwitch() throws BrowserSwitchException { + SEPADirectDebitResponse sepaResponse = mock(SEPADirectDebitResponse.class); + BrowserSwitchOptions options = mock(BrowserSwitchOptions.class); + when(sepaResponse.getBrowserSwitchOptions()).thenReturn(options); + SEPADirectDebitLauncher sut = new SEPADirectDebitLauncher(browserSwitchClient, sepaLauncherCallback); + + sut.launch(activity, sepaResponse); + + verify(browserSwitchClient).start(same(activity), same(options)); + } + + @Test + public void launch_onError_callsBackError() throws BrowserSwitchException { + SEPADirectDebitResponse sepaResponse = mock(SEPADirectDebitResponse.class); + BrowserSwitchOptions options = mock(BrowserSwitchOptions.class); + when(sepaResponse.getBrowserSwitchOptions()).thenReturn(options); + BrowserSwitchException exception = new BrowserSwitchException("error"); + doThrow(exception).when(browserSwitchClient).start(same(activity), same(options)); + SEPADirectDebitLauncher sut = new SEPADirectDebitLauncher(browserSwitchClient, sepaLauncherCallback); + + sut.launch(activity, sepaResponse); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(SEPADirectDebitBrowserSwitchResult.class); + verify(sepaLauncherCallback).onResult(captor.capture()); + assertSame(exception, captor.getValue().getError()); + assertNull(captor.getValue().getBrowserSwitchResult()); + } + + @Test + public void handleReturnToAppFromBrowser_deliversResultToLauncherCallback() { + BrowserSwitchResult result = mock(BrowserSwitchResult.class); + when(browserSwitchClient.parseResult(eq(activity), eq(SEPA_DEBIT), eq(intent))).thenReturn( + result); + SEPADirectDebitLauncher sut = new SEPADirectDebitLauncher(browserSwitchClient, sepaLauncherCallback); + + sut.handleReturnToAppFromBrowser(activity, intent); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(SEPADirectDebitBrowserSwitchResult.class); + verify(sepaLauncherCallback).onResult(captor.capture()); + assertSame(result, captor.getValue().getBrowserSwitchResult()); + assertNull(captor.getValue().getError()); + } + + @Test + public void handleReturnToAppFromBrowser_clearsActiveBrowserSwitchRequests() { + BrowserSwitchResult result = mock(BrowserSwitchResult.class); + when(browserSwitchClient.parseResult(eq(activity), eq(SEPA_DEBIT), eq(intent))).thenReturn( + result); + SEPADirectDebitLauncher sut = new SEPADirectDebitLauncher(browserSwitchClient, sepaLauncherCallback); + + sut.handleReturnToAppFromBrowser(activity, intent); + + verify(browserSwitchClient).clearActiveRequests(same(activity)); + } +} From 4b89d3bf3f6fdef093427d8daf23fa88e2760387 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 15:30:42 -0500 Subject: [PATCH 26/29] Use verify() blocks in-place of callback --- .../api/SEPADirectDebitClientUnitTest.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java index ac4cda207a..92c2a65047 100644 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java @@ -134,11 +134,9 @@ public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_on SEPADirectDebitClient sut = new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(sepaDirectDebitRequest, (sepaDirectDebitResponse, error) -> { - assertEquals(error, exception); - assertNull(sepaDirectDebitResponse); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); - }); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); + verify(sepaFlowStartedCallback).onResult(isNull(), eq(exception)); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.failure"); } @Test @@ -204,11 +202,9 @@ public void tokenize_onCreateMandateError_returnsErrorToListener_andSendsAnalyti SEPADirectDebitClient sut = new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(sepaDirectDebitRequest, (SEPADirectDebitFlowStartedCallback) (sepaDirectDebitResponse, actualError) -> { - assertNull(sepaDirectDebitResponse); - assertEquals(actualError, error); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); - }); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); + verify(sepaFlowStartedCallback).onResult(isNull(), eq(error)); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); } @Test From 1e8fad429af5ec615ef2dcc7a1d6c9f93e76f7af Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Wed, 1 Nov 2023 15:32:13 -0500 Subject: [PATCH 27/29] Cleanup - remove extra newline from SEPALauncherTest --- .../braintreepayments/api/SEPADirectDebitLauncherUnitTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java index 323c3ece67..f72e38314d 100644 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitLauncherUnitTest.java @@ -23,7 +23,6 @@ @RunWith(RobolectricTestRunner.class) public class SEPADirectDebitLauncherUnitTest { - private BrowserSwitchClient browserSwitchClient; private FragmentActivity activity; private Intent intent; From a1b0434bf5b3f4c123fed315201c3e2db2e1c31b Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Thu, 2 Nov 2023 10:23:36 -0500 Subject: [PATCH 28/29] Move away from callback syntax for remaining SEPAClient unit tests --- .../api/SEPADirectDebitClientUnitTest.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java index 92c2a65047..bc3af2c656 100644 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java @@ -1,10 +1,12 @@ package com.braintreepayments.api; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; @@ -107,11 +109,12 @@ public void tokenize_onCreateMandateRequestSuccess_whenMandateAlreadyApproved_on SEPADirectDebitClient sut = new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(sepaDirectDebitRequest, (sepaDirectDebitResponse, error) -> { - assertEquals(sepaDirectDebitResponse.getNonce(), nonce); - assertNull(error); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.success"); - }); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SEPADirectDebitResponse.class); + verify(sepaFlowStartedCallback).onResult(captor.capture(), isNull()); + assertEquals(captor.getValue().getNonce(), nonce); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.tokenize.success"); } @Test @@ -156,12 +159,13 @@ public void tokenize_onCreateMandateRequestSuccess_whenApprovalURLInvalid_callsB SEPADirectDebitClient sut = new SEPADirectDebitClient(braintreeClient, sepaDirectDebitApi); - sut.tokenize(sepaDirectDebitRequest, (response, error) -> { - assertNull(response); - assertTrue(error instanceof BraintreeException); - assertEquals("An unexpected error occurred.", error.getMessage()); - verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); - }); + sut.tokenize(sepaDirectDebitRequest, sepaFlowStartedCallback); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Exception.class); + verify(sepaFlowStartedCallback).onResult(isNull(), captor.capture()); + assertTrue(captor.getValue() instanceof BraintreeException); + assertEquals("An unexpected error occurred.", captor.getValue().getMessage()); + verify(braintreeClient).sendAnalyticsEvent("sepa-direct-debit.create-mandate.failure"); } @Test From bd911a22fb4783c7388b75b678ffe3d85af69d53 Mon Sep 17 00:00:00 2001 From: Sammy Cannillo Date: Thu, 2 Nov 2023 22:39:58 -0500 Subject: [PATCH 29/29] Remove extra test in SEPADirectDebitClientUnitTest --- .../api/SEPADirectDebitClientUnitTest.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java index bc3af2c656..feeea7aefa 100644 --- a/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java +++ b/SEPADirectDebit/src/test/java/com/braintreepayments/api/SEPADirectDebitClientUnitTest.java @@ -477,18 +477,3 @@ public void onBrowserSwitchResult_whenBrowserSwitchStatusSuccess_whenDeepLinkURL assertEquals("Unknown error", exception.getMessage()); } } - -// Save for SEPABrowserSwitchResult_Tests - -// @Test -// public void getBrowserSwitchResult_getBrowserSwitchResultFromBraintreeClient() { -// SEPADirectDebitApi sepaDirectDebitApi = new MockSEPADirectDebitApiBuilder().build(); -// -// braintreeClient = new MockBraintreeClientBuilder() -// .build(); -// -// SEPADirectDebitClient sut = -// new SEPADirectDebitClient(activity, lifecycle, braintreeClient, sepaDirectDebitApi); -// sut.getBrowserSwitchResult(activity); -// verify(braintreeClient).getBrowserSwitchResult(activity); -// } \ No newline at end of file