Skip to content

Commit

Permalink
Local Payment Single Result Object (#851)
Browse files Browse the repository at this point in the history
* Add LocalPaymentResult

* Fix unit tests

* Rename existing PaymentAuthRequest to params

* Rename internal callbacks

* Create PaymentAuthRequest callback

* Fix unit tests

* Update migration guide and CHANGELOG

* Refactor demo app

* Fix demo app

* Fix demo app

* Refactor callbacks to kotlin

* Fix lint

* Update v5_MIGRATION_GUIDE.md

---------

Co-authored-by: Tim Chow <[email protected]>
Co-authored-by: sshropshire <[email protected]>
  • Loading branch information
3 people authored Dec 13, 2023
1 parent b37f817 commit 0deb12d
Show file tree
Hide file tree
Showing 22 changed files with 353 additions and 235 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@
* LocalPayment
* Remove `LocalPaymentListener`
* Add `LocalPaymentLauncher`, `LocalPaymentLauncherCallback`, `LocalPaymentTokenizeCallback`,
and `LocalPaymentAuthRequest`
* Rename `LocalPaymentResult` to `LocalPaymentAuthResult`
`LocalPaymentAuthRequest`, `LocalPaymentAuthRequestCallback` and `LocalPaymentAuthResult`
* Change `LocalPaymentResult` type
* Remove overload constructors, `setListener`, `parseBrowserSwitchResult`,
`clearActiveBrowserSwitchResult`, `approveLocalPayment`, and `approvePayment` from
`LocalPaymentClient`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public void getClientToken(@NonNull BraintreeAuthorizationCallback callback) {
callback.onResult(null);
}
});
} else {
callback.onResult(Settings.getTokenizationKey(appContext));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@
import androidx.annotation.Nullable;
import androidx.navigation.fragment.NavHostFragment;

import com.braintreepayments.api.LocalPaymentAuthRequest;
import com.braintreepayments.api.LocalPaymentAuthResult;
import com.braintreepayments.api.LocalPaymentClient;
import com.braintreepayments.api.LocalPaymentLauncher;
import com.braintreepayments.api.LocalPaymentNonce;
import com.braintreepayments.api.LocalPaymentRequest;
import com.braintreepayments.api.LocalPaymentResult;
import com.braintreepayments.api.PostalAddress;
import com.braintreepayments.api.UserCanceledException;

public class LocalPaymentFragment extends BaseFragment {

Expand Down Expand Up @@ -69,22 +73,28 @@ public void launchIdeal(View v) {
request.setMerchantAccountId("altpay_eur");
request.setCurrencyCode("EUR");

localPaymentClient.createPaymentAuthRequest(request, (localPaymentResult, error) -> {
if (localPaymentResult != null) {
localPaymentLauncher.launch(requireActivity(), localPaymentResult);
} else {
handleError(error);
localPaymentClient.createPaymentAuthRequest(request, (paymentAuthRequest) -> {
if (paymentAuthRequest instanceof LocalPaymentAuthRequest.ReadyToLaunch) {
localPaymentLauncher.launch(requireActivity(),
(LocalPaymentAuthRequest.ReadyToLaunch) paymentAuthRequest);
} else if (paymentAuthRequest instanceof LocalPaymentAuthRequest.Failure) {
handleError(((LocalPaymentAuthRequest.Failure) paymentAuthRequest).getError());
}
});
}

protected void handleLocalPaymentResult(LocalPaymentNonce localPaymentNonce, Exception error) {
super.onPaymentMethodNonceCreated(localPaymentNonce);

if (error != null) {
handleError(error);
return;
protected void handleLocalPaymentResult(LocalPaymentResult localPaymentResult) {
if (localPaymentResult instanceof LocalPaymentResult.Success) {
onPaymentMethodNonceCreated(((LocalPaymentResult.Success) localPaymentResult).getNonce());
} else if (localPaymentResult instanceof LocalPaymentResult.Failure) {
handleError(((LocalPaymentResult.Failure) localPaymentResult).getError());
} else if (localPaymentResult instanceof LocalPaymentResult.Cancel) {
handleError(new UserCanceledException("User canceled Local Payment"));
}
}

protected void onPaymentMethodNonceCreated(LocalPaymentNonce localPaymentNonce) {
super.onPaymentMethodNonceCreated(localPaymentNonce);

LocalPaymentFragmentDirections.ActionLocalPaymentFragmentToDisplayNonceFragment action =
LocalPaymentFragmentDirections.actionLocalPaymentFragmentToDisplayNonceFragment(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static junit.framework.Assert.assertNotNull;

import static org.junit.Assert.assertTrue;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;

Expand Down Expand Up @@ -43,9 +45,10 @@ public void createPaymentAuthRequest_callsBack_withApprovalUrl_andPaymentId() th
request.setCurrencyCode("EUR");

LocalPaymentClient sut = new LocalPaymentClient(braintreeClient);
sut.createPaymentAuthRequest(request, (localPaymentResult, error) -> {
assertNotNull(localPaymentResult.getApprovalUrl());
assertNotNull(localPaymentResult.getPaymentId());
sut.createPaymentAuthRequest(request, (localPaymentAuthRequest) -> {
assertTrue(localPaymentAuthRequest instanceof LocalPaymentAuthRequest.ReadyToLaunch);
assertNotNull(((LocalPaymentAuthRequest.ReadyToLaunch) localPaymentAuthRequest).getRequestParams().getApprovalUrl());
assertNotNull(((LocalPaymentAuthRequest.ReadyToLaunch) localPaymentAuthRequest).getRequestParams().getPaymentId());
countDownLatch.countDown();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class LocalPaymentApi {
}

void createPaymentMethod(final LocalPaymentRequest request,
final LocalPaymentAuthRequestCallback callback) {
final LocalPaymentInternalAuthRequestCallback callback) {
String returnUrl = braintreeClient.getReturnUrlScheme() + "://" + LOCAL_PAYMENT_SUCCESS;
String cancel = braintreeClient.getReturnUrlScheme() + "://" + LOCAL_PAYMENT_CANCEL;

Expand All @@ -30,8 +30,8 @@ void createPaymentMethod(final LocalPaymentRequest request,
String paymentToken = responseJson.getJSONObject("paymentResource")
.getString("paymentToken");

LocalPaymentAuthRequest transaction =
new LocalPaymentAuthRequest(request, redirectUrl, paymentToken);
LocalPaymentAuthRequestParams transaction =
new LocalPaymentAuthRequestParams(request, redirectUrl, paymentToken);
callback.onResult(transaction, null);
} catch (JSONException e) {
callback.onResult(null, e);
Expand All @@ -43,7 +43,7 @@ void createPaymentMethod(final LocalPaymentRequest request,
}

void tokenize(String merchantAccountId, String responseString, String clientMetadataID,
final LocalPaymentTokenizeCallback callback) {
final LocalPaymentInternalTokenizeCallback callback) {
JSONObject payload = new JSONObject();

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.braintreepayments.api

/**
* Callback for receiving result of
* [LocalPaymentClient.createPaymentAuthRequest].
*/
fun interface LocalPaymentAuthCallback {
/**
* @param paymentAuthRequest a request used to launch the PayPal web authentication flow
*/
fun onLocalPaymentAuthRequest(paymentAuthRequest: LocalPaymentAuthRequest)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.braintreepayments.api

/**
* A request used to launch the continuation of the local payment flow.
*/
sealed class LocalPaymentAuthRequest {

/**
* The request was successfully created and is ready to be launched by [LocalPaymentLauncher]
*/
class ReadyToLaunch(val requestParams: LocalPaymentAuthRequestParams) :
LocalPaymentAuthRequest()

/**
* There was an [error] creating the request
*/
class Failure(val error: Exception) : LocalPaymentAuthRequest()
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
/**
* Local payment result information.
*/
public class LocalPaymentAuthRequest {
public class LocalPaymentAuthRequestParams {

private final LocalPaymentRequest request;
private final String approvalUrl;
private final String paymentId;

private BrowserSwitchOptions browserSwitchOptions;

LocalPaymentAuthRequest(LocalPaymentRequest request, String approvalUrl, String paymentId) {
LocalPaymentAuthRequestParams(LocalPaymentRequest request, String approvalUrl, String paymentId) {
this.request = request;
this.approvalUrl = approvalUrl;
this.paymentId = paymentId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

/**
* Result received from the local payment web flow through
* {@link LocalPaymentTokenizeCallback}. This result should be passed to
* {@link LocalPaymentClient#tokenize(Context, LocalPaymentAuthResult, LocalPaymentTokenizeCallback)}
* {@link LocalPaymentInternalTokenizeCallback}. This result should be passed to
* {@link LocalPaymentClient#tokenize(Context, LocalPaymentAuthResult, LocalPaymentInternalTokenizeCallback)}
* to complete the local payment flow.
*/
public class LocalPaymentAuthResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ public LocalPaymentClient(@NonNull Context context, @NonNull String authorizatio

/**
* Starts the payment flow for a {@link LocalPaymentRequest} and calls back a
* {@link LocalPaymentAuthRequest} on success that should be used to launch the user
* {@link LocalPaymentAuthRequestParams} on success that should be used to launch the user
* authentication flow.
*
* @param request {@link LocalPaymentRequest} with the payment details.
* @param callback {@link LocalPaymentAuthRequestCallback}
* @param callback {@link LocalPaymentInternalAuthRequestCallback}
*/
public void createPaymentAuthRequest(@NonNull final LocalPaymentRequest request,
@NonNull final LocalPaymentAuthRequestCallback callback) {
@NonNull final LocalPaymentAuthCallback callback) {
Exception exception = null;

//noinspection ConstantConditions
Expand All @@ -74,13 +74,13 @@ public void createPaymentAuthRequest(@NonNull final LocalPaymentRequest request,
}

if (exception != null) {
callback.onResult(null, exception);
callback.onLocalPaymentAuthRequest(new LocalPaymentAuthRequest.Failure(exception));
} else {
braintreeClient.getConfiguration((configuration, error) -> {
if (configuration != null) {
if (!configuration.isPayPalEnabled()) {
callback.onResult(null, new ConfigurationException(
"Local payments are not enabled for this merchant."));
callback.onLocalPaymentAuthRequest(new LocalPaymentAuthRequest.Failure(new ConfigurationException(
"Local payments are not enabled for this merchant.")));
return;
}

Expand All @@ -94,40 +94,41 @@ public void createPaymentAuthRequest(@NonNull final LocalPaymentRequest request,
sendAnalyticsEvent(request.getPaymentType(),
"local-payment.create.succeeded");
} else if (createPaymentMethodError != null) {
callback.onResult(null, new BraintreeException("An error " +
"occurred creating the local payment method."));
callback.onLocalPaymentAuthRequest(new LocalPaymentAuthRequest.Failure(new BraintreeException("An error " +
"occurred creating the local payment method.")));
sendAnalyticsEvent(request.getPaymentType(),
"local-payment.webswitch.initiate.failed");
}
});
} else {
callback.onResult(null, error);
callback.onLocalPaymentAuthRequest(new LocalPaymentAuthRequest.Failure(error));
}
});
}
}

void buildBrowserSwitchOptions(@NonNull LocalPaymentAuthRequest localPaymentAuthRequest,
@NonNull LocalPaymentAuthRequestCallback callback) {
void buildBrowserSwitchOptions(@NonNull
LocalPaymentAuthRequestParams localPaymentAuthRequestParams,
@NonNull LocalPaymentAuthCallback callback) {
BrowserSwitchOptions browserSwitchOptions = new BrowserSwitchOptions()
.requestCode(BraintreeRequestCodes.LOCAL_PAYMENT)
.returnUrlScheme(braintreeClient.getReturnUrlScheme())
.launchAsNewTask(braintreeClient.launchesBrowserSwitchAsNewTask())
.url(Uri.parse(localPaymentAuthRequest.getApprovalUrl()));
.url(Uri.parse(localPaymentAuthRequestParams.getApprovalUrl()));

String paymentType = localPaymentAuthRequest.getRequest().getPaymentType();
String paymentType = localPaymentAuthRequestParams.getRequest().getPaymentType();

try {
browserSwitchOptions.metadata(new JSONObject()
.put("merchant-account-id",
localPaymentAuthRequest.getRequest().getMerchantAccountId())
.put("payment-type", localPaymentAuthRequest.getRequest().getPaymentType()));
localPaymentAuthRequestParams.getRequest().getMerchantAccountId())
.put("payment-type", localPaymentAuthRequestParams.getRequest().getPaymentType()));
} catch (JSONException e) {
callback.onResult(null, new BraintreeException("Error parsing local payment request"));
callback.onLocalPaymentAuthRequest(new LocalPaymentAuthRequest.Failure(new BraintreeException("Error parsing local payment request")));
}

localPaymentAuthRequest.setBrowserSwitchOptions(browserSwitchOptions);
callback.onResult(localPaymentAuthRequest, null);
localPaymentAuthRequestParams.setBrowserSwitchOptions(browserSwitchOptions);
callback.onLocalPaymentAuthRequest(new LocalPaymentAuthRequest.ReadyToLaunch(localPaymentAuthRequestParams));
sendAnalyticsEvent(paymentType, "local-payment.webswitch.initiate.succeeded");
}

Expand All @@ -140,22 +141,22 @@ void buildBrowserSwitchOptions(@NonNull LocalPaymentAuthRequest localPaymentAuth
* @param context Android Context
* @param localPaymentAuthResult a {@link LocalPaymentAuthResult} received
* in the callback of {@link LocalPaymentLauncher}
* @param callback {@link LocalPaymentTokenizeCallback}
* @param callback {@link LocalPaymentInternalTokenizeCallback}
*/
public void tokenize(@NonNull final Context context,
@Nullable LocalPaymentAuthResult localPaymentAuthResult,
@NonNull final LocalPaymentTokenizeCallback callback) {
//noinspection ConstantConditions
if (localPaymentAuthResult == null) {
callback.onResult(null, new BraintreeException("LocalPaymentAuthResult " +
"cannot be null"));
callback.onLocalPaymentResult(new LocalPaymentResult.Failure(new BraintreeException("LocalPaymentAuthResult " +
"cannot be null")));
return;
}

BrowserSwitchResult browserSwitchResult =
localPaymentAuthResult.getBrowserSwitchResult();
if (browserSwitchResult == null && localPaymentAuthResult.getError() != null) {
callback.onResult(null, localPaymentAuthResult.getError());
callback.onLocalPaymentResult(new LocalPaymentResult.Failure(localPaymentAuthResult.getError()));
return;
}

Expand All @@ -168,23 +169,22 @@ public void tokenize(@NonNull final Context context,
switch (result) {
case BrowserSwitchStatus.CANCELED:
sendAnalyticsEvent(paymentType, "local-payment.webswitch.canceled");
callback.onResult(null, new UserCanceledException("User canceled Local Payment."));
callback.onLocalPaymentResult(LocalPaymentResult.Cancel.INSTANCE);
return;
case BrowserSwitchStatus.SUCCESS:
Uri deepLinkUri = browserSwitchResult.getDeepLinkUrl();
if (deepLinkUri == null) {
sendAnalyticsEvent(paymentType, "local-payment.webswitch-response.invalid");
callback.onResult(null,
callback.onLocalPaymentResult(new LocalPaymentResult.Failure(
new BraintreeException("LocalPayment encountered an error, " +
"return URL is invalid."));
"return URL is invalid.")));
return;
}

final String responseString = deepLinkUri.toString();
if (responseString.toLowerCase().contains(LOCAL_PAYMENT_CANCEL.toLowerCase())) {
sendAnalyticsEvent(paymentType, "local-payment.webswitch.canceled");
callback.onResult(null,
new UserCanceledException("User canceled Local Payment."));
callback.onLocalPaymentResult(LocalPaymentResult.Cancel.INSTANCE);
return;
}
braintreeClient.getConfiguration((configuration, error) -> {
Expand All @@ -195,14 +195,15 @@ public void tokenize(@NonNull final Context context,
if (localPaymentNonce != null) {
sendAnalyticsEvent(paymentType,
"local-payment.tokenize.succeeded");
callback.onLocalPaymentResult(new LocalPaymentResult.Success(localPaymentNonce));
} else if (localPaymentError != null) {
sendAnalyticsEvent(paymentType,
"local-payment.tokenize.failed");
callback.onLocalPaymentResult(new LocalPaymentResult.Failure(localPaymentError));
}
callback.onResult(localPaymentNonce, localPaymentError);
});
} else if (error != null) {
callback.onResult(null, error);
callback.onLocalPaymentResult(new LocalPaymentResult.Failure(error));
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.braintreepayments.api;

import androidx.annotation.Nullable;

/**
* Callback for receiving result of
* {@link LocalPaymentClient#createPaymentAuthRequest(LocalPaymentRequest, LocalPaymentAuthCallback)}.
*/
public interface LocalPaymentInternalAuthRequestCallback {

/**
* @param localPaymentAuthRequestParams {@link LocalPaymentAuthRequestParams}
* @param error an exception that occurred while initiating a Local Payment
*/
void onResult(@Nullable LocalPaymentAuthRequestParams localPaymentAuthRequestParams, @Nullable Exception error);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.braintreepayments.api;


import androidx.annotation.Nullable;

interface LocalPaymentInternalTokenizeCallback {

void onResult(@Nullable LocalPaymentNonce localPaymentNonce, @Nullable Exception error);
}
Loading

0 comments on commit 0deb12d

Please sign in to comment.