From 7ca4cde20fbef5eb2222a56aed7662f5436cdb05 Mon Sep 17 00:00:00 2001 From: Michael Shafrir Date: Thu, 11 Apr 2019 11:04:03 -0400 Subject: [PATCH] Create ActivityPaymentSessionListener **Summary** `ActivityPaymentSessionListener` is an abstract implementation of `PaymentSessionListener` that handles holding a `WeakReference` to an `Activity` context to avoid memory leaks in the listener's callback methods. **Motivation** ANDROID-348 **Testing** Manually tested in sample apps --- .../activity/PaymentSessionActivity.java | 101 +++++++++----- .../controller/AsyncTaskTokenController.java | 14 +- .../stripe/samplestore/PaymentActivity.java | 130 +++++++++++------- .../com/stripe/android/PaymentSession.java | 60 ++++---- .../stripe/android/PaymentSessionData.java | 20 ++- .../com/stripe/android/TokenCallback.java | 6 +- .../java/com/stripe/android/StripeTest.java | 8 +- 7 files changed, 214 insertions(+), 125 deletions(-) diff --git a/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.java b/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.java index 41ab5ab734a..9a981a001b3 100644 --- a/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.java +++ b/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.java @@ -76,18 +76,27 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { setupCustomerSession(); // CustomerSession only needs to be initialized once per app. mBroadcastReceiver = new BroadcastReceiver() { @Override - public void onReceive(Context context, Intent intent) { - ShippingInformation shippingInformation = intent.getParcelableExtra(EXTRA_SHIPPING_INFO_DATA); + public void onReceive(@NonNull Context context, @NonNull Intent intent) { + ShippingInformation shippingInformation = intent + .getParcelableExtra(EXTRA_SHIPPING_INFO_DATA); Intent shippingInfoProcessedIntent = new Intent(EVENT_SHIPPING_INFO_PROCESSED); - if (shippingInformation.getAddress() == null || !shippingInformation.getAddress().getCountry().equals(Locale.US.getCountry())) { + if (!isValidShippingInfo(shippingInformation)) { shippingInfoProcessedIntent.putExtra(EXTRA_IS_SHIPPING_INFO_VALID, false); } else { ArrayList shippingMethods = createSampleShippingMethods(); shippingInfoProcessedIntent.putExtra(EXTRA_IS_SHIPPING_INFO_VALID, true); - shippingInfoProcessedIntent.putParcelableArrayListExtra(EXTRA_VALID_SHIPPING_METHODS, shippingMethods); - shippingInfoProcessedIntent.putExtra(EXTRA_DEFAULT_SHIPPING_METHOD, shippingMethods.get(1)); + shippingInfoProcessedIntent.putParcelableArrayListExtra( + EXTRA_VALID_SHIPPING_METHODS, shippingMethods); + shippingInfoProcessedIntent.putExtra(EXTRA_DEFAULT_SHIPPING_METHOD, + shippingMethods.get(1)); } - LocalBroadcastManager.getInstance(PaymentSessionActivity.this).sendBroadcast(shippingInfoProcessedIntent); + LocalBroadcastManager.getInstance(PaymentSessionActivity.this) + .sendBroadcast(shippingInfoProcessedIntent); + } + + private boolean isValidShippingInfo(@NonNull ShippingInformation shippingInfo) { + return shippingInfo.getAddress() != null && + Locale.US.getCountry().equals(shippingInfo.getAddress().getCountry()); } }; LocalBroadcastManager.getInstance(this).registerReceiver(mBroadcastReceiver, @@ -141,27 +150,7 @@ public void onError(int httpCode, @Nullable String errorMessage, private void setupPaymentSession() { mPaymentSession = new PaymentSession(this); - boolean paymentSessionInitialized = mPaymentSession.init(new PaymentSession.PaymentSessionListener() { - @Override - public void onCommunicatingStateChanged(boolean isCommunicating) { - if (isCommunicating) { - mProgressBar.setVisibility(View.VISIBLE); - } else { - mProgressBar.setVisibility(View.INVISIBLE); - } - } - - @Override - public void onError(int errorCode, @Nullable String errorMessage) { - mErrorDialogHandler.showError(errorMessage); - } - - @Override - public void onPaymentSessionDataChanged(@NonNull PaymentSessionData data) { - mPaymentSessionData = data; - checkForCustomerUpdates(); - } - }, new PaymentSessionConfig.Builder() + boolean paymentSessionInitialized = mPaymentSession.init(new PaymentSessionListenerImpl(this), new PaymentSessionConfig.Builder() .setPrepopulatedShippingInfo(getExampleShippingInfo()) .setHiddenShippingInfoFields(ShippingInfoWidget.PHONE_FIELD, ShippingInfoWidget.CITY_FIELD) .build()); @@ -194,7 +183,8 @@ public void onError(int httpCode, @Nullable String errorMessage, }); } - private String formatStringResults(PaymentSessionData data) { + @NonNull + private String formatStringResults(@NonNull PaymentSessionData data) { Currency currency = Currency.getInstance("USD"); StringBuilder stringBuilder = new StringBuilder(); @@ -232,10 +222,13 @@ private String formatStringResults(PaymentSessionData data) { return stringBuilder.toString(); } + @NonNull private ArrayList createSampleShippingMethods() { - ArrayList shippingMethods = new ArrayList<>(); - shippingMethods.add(new ShippingMethod("UPS Ground", "ups-ground", "Arrives in 3-5 days", 0, "USD")); - shippingMethods.add(new ShippingMethod("FedEx", "fedex", "Arrives tomorrow", 599, "USD")); + final ArrayList shippingMethods = new ArrayList<>(); + shippingMethods.add(new ShippingMethod("UPS Ground", "ups-ground", + "Arrives in 3-5 days", 0, "USD")); + shippingMethods.add(new ShippingMethod("FedEx", "fedex", + "Arrives tomorrow", 599, "USD")); return shippingMethods; } @@ -252,8 +245,9 @@ protected void onDestroy() { LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver); } + @NonNull private ShippingInformation getExampleShippingInfo() { - Address address = new Address.Builder() + final Address address = new Address.Builder() .setCity("San Francisco") .setCountry("US") .setLine1("123 Market St") @@ -263,4 +257,47 @@ private ShippingInformation getExampleShippingInfo() { .build(); return new ShippingInformation(address, "Fake Name", "(555) 555-5555"); } + + private void onPaymentSessionDataChanged(@NonNull PaymentSessionData data) { + mPaymentSessionData = data; + checkForCustomerUpdates(); + } + + private static final class PaymentSessionListenerImpl + extends PaymentSession.ActivityPaymentSessionListener { + private PaymentSessionListenerImpl(@NonNull PaymentSessionActivity activity) { + super(activity); + } + + @Override + public void onCommunicatingStateChanged(boolean isCommunicating) { + final PaymentSessionActivity activity = getListenerActivity(); + if (activity == null) { + return; + } + + activity.mProgressBar + .setVisibility(isCommunicating ? View.VISIBLE : View.INVISIBLE); + } + + @Override + public void onError(int errorCode, @Nullable String errorMessage) { + final PaymentSessionActivity activity = getListenerActivity(); + if (activity == null) { + return; + } + + activity.mErrorDialogHandler.showError(errorMessage); + } + + @Override + public void onPaymentSessionDataChanged(@NonNull PaymentSessionData data) { + final PaymentSessionActivity activity = getListenerActivity(); + if (activity == null) { + return; + } + + activity.onPaymentSessionDataChanged(data); + } + } } diff --git a/example/src/main/java/com/stripe/example/controller/AsyncTaskTokenController.java b/example/src/main/java/com/stripe/example/controller/AsyncTaskTokenController.java index 8391f3e1765..5f803c60850 100644 --- a/example/src/main/java/com/stripe/example/controller/AsyncTaskTokenController.java +++ b/example/src/main/java/com/stripe/example/controller/AsyncTaskTokenController.java @@ -19,9 +19,9 @@ */ public class AsyncTaskTokenController { - @NonNull private final Context mContext; + @NonNull private final Stripe mStripe; @NonNull private final ErrorDialogHandler mErrorDialogHandler; - @NonNull private ListViewController mOutputListController; + @NonNull private final ListViewController mOutputListController; @NonNull private final ProgressDialogController mProgressDialogController; @Nullable private CardInputWidget mCardInputWidget; @@ -34,7 +34,7 @@ public AsyncTaskTokenController( @NonNull ListViewController outputListController, @NonNull ProgressDialogController progressDialogController) { mCardInputWidget = cardInputWidget; - mContext = context; + mStripe = new Stripe(context); mErrorDialogHandler = errorDialogHandler; mProgressDialogController = progressDialogController; mOutputListController = outputListController; @@ -52,21 +52,21 @@ public void detach() { } private void saveCard() { - Card cardToSave = mCardInputWidget.getCard(); + final Card cardToSave = mCardInputWidget.getCard(); if (cardToSave == null) { mErrorDialogHandler.showError("Invalid Card Data"); return; } mProgressDialogController.startProgress(); - new Stripe(mContext).createToken( + mStripe.createToken( cardToSave, PaymentConfiguration.getInstance().getPublishableKey(), new TokenCallback() { - public void onSuccess(Token token) { + public void onSuccess(@NonNull Token token) { mOutputListController.addToList(token); mProgressDialogController.finishProgress(); } - public void onError(Exception error) { + public void onError(@NonNull Exception error) { mErrorDialogHandler.showError(error.getLocalizedMessage()); mProgressDialogController.finishProgress(); } diff --git a/samplestore/src/main/java/com/stripe/samplestore/PaymentActivity.java b/samplestore/src/main/java/com/stripe/samplestore/PaymentActivity.java index 9b94decb892..db3507f7e89 100644 --- a/samplestore/src/main/java/com/stripe/samplestore/PaymentActivity.java +++ b/samplestore/src/main/java/com/stripe/samplestore/PaymentActivity.java @@ -294,7 +294,9 @@ private void proceedWithPurchaseIf3DSCheckIsNotNecessary(Source source, String c } } - private Map createParams(long price, String sourceId, String customerId, ShippingInformation shippingInformation){ + @NonNull + private Map createParams(long price, String sourceId, String customerId, + ShippingInformation shippingInformation) { Map params = new HashMap<>(); params.put("amount", Long.toString(price)); params.put("source", sourceId); @@ -372,40 +374,58 @@ private void finishCharge() { private void setupPaymentSession() { mPaymentSession = new PaymentSession(this); - mPaymentSession.init(new PaymentSession.PaymentSessionListener() { - @Override - public void onCommunicatingStateChanged(boolean isCommunicating) { - if (isCommunicating) { - mProgressDialogFragment.show(getSupportFragmentManager(), "progress"); - } else { - mProgressDialogFragment.dismiss(); - } - } + mPaymentSession.init(new PaymentSessionListenerImpl(this), + new PaymentSessionConfig.Builder().build()); + } - @Override - public void onError(int errorCode, @Nullable String errorMessage) { - displayError(errorMessage); - } + private String formatSourceDescription(Source source) { + if (Source.CARD.equals(source.getType())) { + final SourceCardData sourceCardData = (SourceCardData) source.getSourceTypeModel(); + return sourceCardData.getBrand() + getString(R.string.ending_in) + + sourceCardData.getLast4(); + } + return source.getType(); + } - @Override - public void onPaymentSessionDataChanged(@NonNull PaymentSessionData data) { - if (data.getShippingMethod() != null) { - mEnterShippingInfo.setText(data.getShippingMethod().getLabel()); - mShippingCosts = data.getShippingMethod().getAmount(); - addCartItems(); - updateConfirmPaymentButton(); - } + @NonNull + private ArrayList getValidShippingMethods(@NonNull ShippingInformation shippingInformation) { + ArrayList shippingMethods = new ArrayList<>(); + shippingMethods.add(new ShippingMethod("UPS Ground", "ups-ground", "Arrives in 3-5 days", 0, "USD")); + shippingMethods.add(new ShippingMethod("FedEx", "fedex", "Arrives tomorrow", 599, "USD")); + if (shippingInformation.getAddress() != null && + shippingInformation.getAddress().getPostalCode().equals("94110")) { + shippingMethods.add(new ShippingMethod("1 Hour Courier", "courier", "Arrives in the next hour", 1099, "USD")); + } + return shippingMethods; + } + + private void onCommunicatingStateChanged(boolean isCommunicating) { + if (isCommunicating) { + mProgressDialogFragment.show(getSupportFragmentManager(), "progress"); + } else { + mProgressDialogFragment.dismiss(); + } + } - if (data.getSelectedPaymentMethodId() != null) { - CustomerSession.getInstance().retrieveCurrentCustomer(new CustomerSession.CustomerRetrievalListener() { + private void onPaymentSessionDataChanged(@NonNull PaymentSessionData data) { + if (data.getShippingMethod() != null) { + mEnterShippingInfo.setText(data.getShippingMethod().getLabel()); + mShippingCosts = data.getShippingMethod().getAmount(); + addCartItems(); + updateConfirmPaymentButton(); + } + + if (data.getSelectedPaymentMethodId() != null) { + CustomerSession.getInstance().retrieveCurrentCustomer( + new CustomerSession.CustomerRetrievalListener() { @Override public void onCustomerRetrieved(@NonNull Customer customer) { - String sourceId = customer.getDefaultSource(); + final String sourceId = customer.getDefaultSource(); if (sourceId == null) { displayError("No payment method selected"); return; } - CustomerSource source = customer.getSourceById(sourceId); + final CustomerSource source = customer.getSourceById(sourceId); mEnterPaymentInfo.setText(formatSourceDescription(source.asSource())); } @@ -415,35 +435,47 @@ public void onError(int httpCode, @Nullable String errorMessage, displayError(errorMessage); } }); - } + } - if (data.isPaymentReadyToCharge()) { - mConfirmPaymentButton.setEnabled(true); - } + if (data.isPaymentReadyToCharge()) { + mConfirmPaymentButton.setEnabled(true); + } + } + + private static final class PaymentSessionListenerImpl + extends PaymentSession.ActivityPaymentSessionListener { + private PaymentSessionListenerImpl(@NonNull PaymentActivity activity) { + super(activity); + } + @Override + public void onCommunicatingStateChanged(boolean isCommunicating) { + final PaymentActivity activity = getListenerActivity(); + if (activity == null) { + return; } - }, new PaymentSessionConfig.Builder().build()); - } - private String formatSourceDescription(Source source) { - if (Source.CARD.equals(source.getType())) { - final SourceCardData sourceCardData = (SourceCardData) source.getSourceTypeModel(); - return sourceCardData.getBrand() + getString(R.string.ending_in) + - sourceCardData.getLast4(); + activity.onCommunicatingStateChanged(isCommunicating); } - return source.getType(); - } - @NonNull - private ArrayList getValidShippingMethods(@NonNull ShippingInformation shippingInformation) { - ArrayList shippingMethods = new ArrayList<>(); - shippingMethods.add(new ShippingMethod("UPS Ground", "ups-ground", "Arrives in 3-5 days", 0, "USD")); - shippingMethods.add(new ShippingMethod("FedEx", "fedex", "Arrives tomorrow", 599, "USD")); - if (shippingInformation.getAddress() != null && - shippingInformation.getAddress().getPostalCode().equals("94110")) { - shippingMethods.add(new ShippingMethod("1 Hour Courier", "courier", "Arrives in the next hour", 1099, "USD")); + @Override + public void onError(int errorCode, @Nullable String errorMessage) { + final PaymentActivity activity = getListenerActivity(); + if (activity == null) { + return; + } + + activity.displayError(errorMessage); } - return shippingMethods; - } + @Override + public void onPaymentSessionDataChanged(@NonNull PaymentSessionData data) { + final PaymentActivity activity = getListenerActivity(); + if (activity == null) { + return; + } + + activity.onPaymentSessionDataChanged(data); + } + } } diff --git a/stripe/src/main/java/com/stripe/android/PaymentSession.java b/stripe/src/main/java/com/stripe/android/PaymentSession.java index b21efb67c58..b7f60f96e17 100644 --- a/stripe/src/main/java/com/stripe/android/PaymentSession.java +++ b/stripe/src/main/java/com/stripe/android/PaymentSession.java @@ -12,6 +12,8 @@ import com.stripe.android.view.PaymentMethodsActivity; import com.stripe.android.view.PaymentMethodsActivityStarter; +import java.lang.ref.WeakReference; + /** * Represents a single start-to-finish payment operation. */ @@ -28,6 +30,7 @@ public class PaymentSession { @NonNull private final Activity mHostActivity; @NonNull private final PaymentMethodsActivityStarter mPaymentMethodsActivityStarter; + @NonNull private final CustomerSession mCustomerSession; private PaymentSessionData mPaymentSessionData; @Nullable private PaymentSessionListener mPaymentSessionListener; private PaymentSessionConfig mPaymentSessionConfig; @@ -42,6 +45,7 @@ public class PaymentSession { */ public PaymentSession(@NonNull Activity hostActivity) { mHostActivity = hostActivity; + mCustomerSession = CustomerSession.getInstance(); mPaymentMethodsActivityStarter = new PaymentMethodsActivityStarter(hostActivity); mPaymentSessionData = new PaymentSessionData(); } @@ -57,7 +61,7 @@ public void completePayment(@NonNull PaymentCompletionProvider provider) { @Override public void onPaymentResult(@NonNull @PaymentResult String paymentResult) { mPaymentSessionData.setPaymentResult(paymentResult); - CustomerSession.getInstance().resetUsageTokens(); + mCustomerSession.resetUsageTokens(); if (mPaymentSessionListener != null) { mPaymentSessionListener .onPaymentSessionDataChanged(mPaymentSessionData); @@ -87,9 +91,9 @@ public boolean handlePaymentData(int requestCode, int resultCode, @NonNull Inten return true; } case PAYMENT_SHIPPING_DETAILS_REQUEST: { - PaymentSessionData paymentSessionData = data.getParcelableExtra( + final PaymentSessionData paymentSessionData = data.getParcelableExtra( PAYMENT_SESSION_DATA_KEY); - updateIsPaymentReadyToCharge(mPaymentSessionConfig, paymentSessionData); + paymentSessionData.updateIsPaymentReadyToCharge(mPaymentSessionConfig); mPaymentSessionData = paymentSessionData; if (mPaymentSessionListener != null) { mPaymentSessionListener.onPaymentSessionDataChanged(paymentSessionData); @@ -104,29 +108,6 @@ public boolean handlePaymentData(int requestCode, int resultCode, @NonNull Inten return false; } - /** - * Function that looks at the {@link PaymentSessionConfig} and determines whether the data in - * the provided {@link PaymentSessionData} is ready to charge. - * Return with whether the data is ready to charge. - * - * @param paymentSessionConfig specifies what data is required. - * @param paymentSessionData holds the data that has been collected. - * @return whether the data in the provided {@link PaymentSessionData} is ready to charge. - */ - public boolean updateIsPaymentReadyToCharge(@NonNull PaymentSessionConfig paymentSessionConfig, - @NonNull PaymentSessionData paymentSessionData) { - if (StripeTextUtils.isBlank(paymentSessionData.getSelectedPaymentMethodId()) || - (paymentSessionConfig.isShippingInfoRequired() && - paymentSessionData.getShippingInformation() == null) || - (paymentSessionConfig.isShippingMethodRequired() && - paymentSessionData.getShippingMethod() == null)) { - paymentSessionData.setPaymentReadyToCharge(false); - return false; - } - paymentSessionData.setPaymentReadyToCharge(true); - return true; - } - /** * Initialize the PaymentSession with a {@link PaymentSessionListener} to be notified of * data changes. @@ -165,9 +146,9 @@ public boolean init( // will throw a runtime exception if none is ready. try { if (savedInstanceState == null) { - CustomerSession.getInstance().resetUsageTokens(); + mCustomerSession.resetUsageTokens(); } - CustomerSession.getInstance().addProductUsageTokenIfValid(TOKEN_PAYMENT_SESSION); + mCustomerSession.addProductUsageTokenIfValid(TOKEN_PAYMENT_SESSION); } catch (IllegalStateException illegalState) { mPaymentSessionListener = null; return false; @@ -248,13 +229,13 @@ private void fetchCustomer() { if (mPaymentSessionListener != null) { mPaymentSessionListener.onCommunicatingStateChanged(true); } - CustomerSession.getInstance().retrieveCurrentCustomer( + mCustomerSession.retrieveCurrentCustomer( new CustomerSession.CustomerRetrievalListener() { @Override public void onCustomerRetrieved(@NonNull Customer customer) { String paymentId = customer.getDefaultSource(); mPaymentSessionData.setSelectedPaymentMethodId(paymentId); - updateIsPaymentReadyToCharge(mPaymentSessionConfig, mPaymentSessionData); + mPaymentSessionData.updateIsPaymentReadyToCharge(mPaymentSessionConfig); if (mPaymentSessionListener != null) { mPaymentSessionListener .onPaymentSessionDataChanged(mPaymentSessionData); @@ -303,4 +284,23 @@ public interface PaymentSessionListener { void onPaymentSessionDataChanged(@NonNull PaymentSessionData data); } + /** + * Abstract implementation of {@link PaymentSessionListener} that holds a + * {@link WeakReference} to an {@link Activity} object. + */ + public abstract static class ActivityPaymentSessionListener + implements PaymentSessionListener { + + @NonNull + private final WeakReference mActivityRef; + + public ActivityPaymentSessionListener(@NonNull A activity) { + this.mActivityRef = new WeakReference<>(activity); + } + + @Nullable + protected A getListenerActivity() { + return mActivityRef.get(); + } + } } diff --git a/stripe/src/main/java/com/stripe/android/PaymentSessionData.java b/stripe/src/main/java/com/stripe/android/PaymentSessionData.java index ef24556c7f2..1b5d8d7943f 100644 --- a/stripe/src/main/java/com/stripe/android/PaymentSessionData.java +++ b/stripe/src/main/java/com/stripe/android/PaymentSessionData.java @@ -141,6 +141,24 @@ void setShippingTotal(long shippingTotal) { mShippingTotal = shippingTotal; } + /** + * Function that looks at the {@link PaymentSessionConfig} and determines whether the data is + * ready to charge. + * + * @param config specifies what data is required + * @return whether the data is ready to charge + */ + public boolean updateIsPaymentReadyToCharge(@NonNull PaymentSessionConfig config) { + if (StripeTextUtils.isBlank(getSelectedPaymentMethodId()) || + (config.isShippingInfoRequired() && getShippingInformation() == null) || + (config.isShippingMethodRequired() && getShippingMethod() == null)) { + setPaymentReadyToCharge(false); + return false; + } + setPaymentReadyToCharge(true); + return true; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -204,7 +222,7 @@ public PaymentSessionData[] newArray(int size) { } }; - private PaymentSessionData(Parcel in) { + private PaymentSessionData(@NonNull Parcel in) { mCartTotal = in.readLong(); mIsPaymentReadyToCharge = in.readInt() == 1; mPaymentResult = PaymentSessionUtils.paymentResultFromString(in.readString()); diff --git a/stripe/src/main/java/com/stripe/android/TokenCallback.java b/stripe/src/main/java/com/stripe/android/TokenCallback.java index 1cc0ec86475..30368a0e553 100644 --- a/stripe/src/main/java/com/stripe/android/TokenCallback.java +++ b/stripe/src/main/java/com/stripe/android/TokenCallback.java @@ -1,5 +1,7 @@ package com.stripe.android; +import android.support.annotation.NonNull; + import com.stripe.android.model.Token; /** @@ -12,11 +14,11 @@ public interface TokenCallback { * Error callback method. * @param error the error that occurred. */ - void onError(Exception error); + void onError(@NonNull Exception error); /** * Success callback method. * @param token the {@link Token} that was found or created. */ - void onSuccess(Token token); + void onSuccess(@NonNull Token token); } diff --git a/stripe/src/test/java/com/stripe/android/StripeTest.java b/stripe/src/test/java/com/stripe/android/StripeTest.java index 6d2bbcb6b62..2b0c210015d 100644 --- a/stripe/src/test/java/com/stripe/android/StripeTest.java +++ b/stripe/src/test/java/com/stripe/android/StripeTest.java @@ -55,10 +55,10 @@ public class StripeTest { private static final Card DEFAULT_CARD = new Card(null, null, null, null); private static final TokenCallback DEFAULT_TOKEN_CALLBACK = new TokenCallback() { @Override - public void onError(Exception error) { + public void onError(@NonNull Exception error) { } @Override - public void onSuccess(Token token) { + public void onSuccess(@NonNull Token token) { } }; @@ -140,12 +140,12 @@ public void createTokenShouldFailWithNullPublishableKey() { Stripe stripe = new Stripe(mContext); stripe.createToken(DEFAULT_CARD, new TokenCallback() { @Override - public void onError(Exception error) { + public void onError(@NonNull Exception error) { fail("Should not call method"); } @Override - public void onSuccess(Token token) { + public void onSuccess(@NonNull Token token) { fail("Should not call method"); } });