diff --git a/example/src/main/java/com/stripe/example/activity/CustomerSessionActivity.java b/example/src/main/java/com/stripe/example/activity/CustomerSessionActivity.java index 2185ee3ad7d..e2960be7f34 100644 --- a/example/src/main/java/com/stripe/example/activity/CustomerSessionActivity.java +++ b/example/src/main/java/com/stripe/example/activity/CustomerSessionActivity.java @@ -12,6 +12,7 @@ import androidx.appcompat.app.AppCompatActivity; import com.stripe.android.CustomerSession; +import com.stripe.android.StripeError; import com.stripe.android.model.Customer; import com.stripe.android.model.Source; import com.stripe.android.model.SourceCardData; @@ -64,7 +65,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { mSelectSourceButton.setEnabled(false); mErrorDialogHandler.showError(errorMessage); mProgressBar.setVisibility(View.INVISIBLE); 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 5d35633167d..a488d50a169 100644 --- a/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.java +++ b/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.java @@ -19,6 +19,7 @@ import com.stripe.android.PaymentSession; import com.stripe.android.PaymentSessionConfig; import com.stripe.android.PaymentSessionData; +import com.stripe.android.StripeError; import com.stripe.android.model.Address; import com.stripe.android.model.Customer; import com.stripe.android.model.CustomerSource; @@ -128,7 +129,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { mCustomer = null; mSelectPaymentButton.setEnabled(false); mSelectShippingButton.setEnabled(false); @@ -186,7 +188,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { mProgressBar.setVisibility(View.INVISIBLE); } }); diff --git a/samplestore/src/main/java/com/stripe/samplestore/PaymentActivity.java b/samplestore/src/main/java/com/stripe/samplestore/PaymentActivity.java index 3885b539713..ad396b16745 100644 --- a/samplestore/src/main/java/com/stripe/samplestore/PaymentActivity.java +++ b/samplestore/src/main/java/com/stripe/samplestore/PaymentActivity.java @@ -26,6 +26,7 @@ import com.stripe.android.PaymentSession; import com.stripe.android.PaymentSessionConfig; import com.stripe.android.PaymentSessionData; +import com.stripe.android.StripeError; import com.stripe.android.model.Customer; import com.stripe.android.model.CustomerSource; import com.stripe.android.model.ShippingInformation; @@ -134,7 +135,8 @@ public void call(Void aVoid) { public void onReceive(Context context, 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 (shippingInformation.getAddress() == null || + !shippingInformation.getAddress().getCountry().equals(Locale.US.getCountry())) { shippingInfoProcessedIntent.putExtra(EXTRA_IS_SHIPPING_INFO_VALID, false); } else { ArrayList shippingMethods = getValidShippingMethods(shippingInformation); @@ -242,8 +244,7 @@ private TextView[] getItemViews(View view) { TextView quantityView = view.findViewById(R.id.tv_cart_quantity); TextView unitPriceView = view.findViewById(R.id.tv_cart_unit_price); TextView totalPriceView = view.findViewById(R.id.tv_cart_total_price); - TextView[] itemViews = { labelView, quantityView, unitPriceView, totalPriceView }; - return itemViews; + return new TextView[]{labelView, quantityView, unitPriceView, totalPriceView}; } private void attemptPurchase() { @@ -260,7 +261,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { displayError("Error getting payment method"); } }); @@ -300,7 +302,8 @@ private void completePurchase(String sourceId, String customerId) { ShippingInformation shippingInformation = mPaymentSession.getPaymentSessionData().getShippingInformation(); - Observable stripeResponse = stripeService.createQueryCharge(createParams(price, sourceId, customerId, shippingInformation)); + final Observable stripeResponse = stripeService.createQueryCharge( + createParams(price, sourceId, customerId, shippingInformation)); final FragmentManager fragmentManager = getSupportFragmentManager(); mCompositeSubscription.add(stripeResponse .subscribeOn(Schedulers.io()) @@ -399,7 +402,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { displayError(errorMessage); } }); @@ -415,19 +419,20 @@ public void onError(int errorCode, @Nullable String errorMessage) { private String formatSourceDescription(Source source) { if (Source.CARD.equals(source.getType())) { - SourceCardData sourceCardData = (SourceCardData) source.getSourceTypeModel(); - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(sourceCardData.getBrand()).append(getString(R.string.ending_in)).append(sourceCardData.getLast4()); - return stringBuilder.toString(); + final SourceCardData sourceCardData = (SourceCardData) source.getSourceTypeModel(); + return sourceCardData.getBrand() + getString(R.string.ending_in) + + sourceCardData.getLast4(); } return source.getType(); } - private ArrayList getValidShippingMethods(ShippingInformation shippingInformation) { + @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")) { + 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; diff --git a/stripe/src/main/java/com/stripe/android/CustomerSession.java b/stripe/src/main/java/com/stripe/android/CustomerSession.java index 149a2768ea3..c147577abac 100644 --- a/stripe/src/main/java/com/stripe/android/CustomerSession.java +++ b/stripe/src/main/java/com/stripe/android/CustomerSession.java @@ -552,12 +552,12 @@ public void onKeyError(int errorCode, @Nullable String errorMessage) { // Any error eliminates all listeners if (mCustomerRetrievalListener != null) { - mCustomerRetrievalListener.onError(errorCode, errorMessage); + mCustomerRetrievalListener.onError(errorCode, errorMessage, null); mCustomerRetrievalListener = null; } if (mSourceRetrievalListener != null) { - mSourceRetrievalListener.onError(errorCode, errorMessage); + mSourceRetrievalListener.onError(errorCode, errorMessage, null); mSourceRetrievalListener = null; } } @@ -597,7 +597,7 @@ public void handleMessage(@NonNull Message msg) { case CUSTOMER_SHIPPING_INFO_SAVED: { if (messageObject instanceof Customer) { mCustomer = (Customer) messageObject; - Intent intent = new Intent(EVENT_SHIPPING_INFO_SAVED); + final Intent intent = new Intent(EVENT_SHIPPING_INFO_SAVED); LocalBroadcastManager.getInstance(mContextRef.get()) .sendBroadcast(intent); } @@ -605,14 +605,14 @@ public void handleMessage(@NonNull Message msg) { } case CUSTOMER_ERROR: { if (messageObject instanceof StripeException) { - StripeException exception = (StripeException) messageObject; + final StripeException exception = (StripeException) messageObject; if (mCustomerRetrievalListener != null) { final int errorCode = exception.getStatusCode() == null ? 400 : exception.getStatusCode(); - mCustomerRetrievalListener.onError( - errorCode, - exception.getLocalizedMessage()); + mCustomerRetrievalListener.onError(errorCode, + exception.getLocalizedMessage(), + exception.getStripeError()); mCustomerRetrievalListener = null; } resetUsageTokens(); @@ -620,14 +620,14 @@ public void handleMessage(@NonNull Message msg) { break; } case SOURCE_ERROR: { - StripeException exception = (StripeException) messageObject; + final StripeException exception = (StripeException) messageObject; if (mSourceRetrievalListener != null) { final int errorCode = exception.getStatusCode() == null ? 400 : exception.getStatusCode(); - mSourceRetrievalListener.onError( - errorCode, - exception.getLocalizedMessage()); + mSourceRetrievalListener.onError(errorCode, + exception.getLocalizedMessage(), + exception.getStripeError()); mSourceRetrievalListener = null; resetUsageTokens(); } @@ -806,13 +806,15 @@ private static void sendErrorIntent(@NonNull WeakReference contextRef, public interface CustomerRetrievalListener { void onCustomerRetrieved(@NonNull Customer customer); - void onError(int errorCode, @Nullable String errorMessage); + void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError); } public interface SourceRetrievalListener { void onSourceRetrieved(@NonNull Source source); - void onError(int errorCode, @Nullable String errorMessage); + void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError); } interface StripeApiProxy { diff --git a/stripe/src/main/java/com/stripe/android/EphemeralKeyManager.java b/stripe/src/main/java/com/stripe/android/EphemeralKeyManager.java index b2eab256b52..aaa7eef2b17 100644 --- a/stripe/src/main/java/com/stripe/android/EphemeralKeyManager.java +++ b/stripe/src/main/java/com/stripe/android/EphemeralKeyManager.java @@ -35,13 +35,13 @@ class EphemeralKeyManager { retrieveEphemeralKey(null, null); } - void retrieveEphemeralKey(@Nullable String actionString, Map arguments) { + void retrieveEphemeralKey(@Nullable String actionString, + @Nullable Map arguments) { if (shouldRefreshKey( mEphemeralKey, mTimeBufferInSeconds, mOverrideCalendar)) { - mEphemeralKeyProvider.createEphemeralKey( - StripeApiHandler.API_VERSION, + mEphemeralKeyProvider.createEphemeralKey(StripeApiHandler.API_VERSION, new ClientKeyUpdateListener(this, actionString, arguments)); } else { mListener.onKeyUpdate(mEphemeralKey, actionString, arguments); @@ -115,23 +115,22 @@ void onKeyUpdate(@NonNull TEphemeralKey ephemeralKey, private static class ClientKeyUpdateListener implements EphemeralKeyUpdateListener { - private @Nullable String mActionString; - private @Nullable Map mArguments; - private @NonNull - WeakReference mEphemeralKeyManagerWeakReference; + @Nullable private final String mActionString; + @Nullable private final Map mArguments; + @NonNull private final WeakReference mEphemeralKeyManagerRef; ClientKeyUpdateListener( @NonNull EphemeralKeyManager keyManager, @Nullable String actionString, @Nullable Map arguments) { - mEphemeralKeyManagerWeakReference = new WeakReference<>(keyManager); + mEphemeralKeyManagerRef = new WeakReference<>(keyManager); mActionString = actionString; mArguments = arguments; } @Override public void onKeyUpdate(@NonNull String rawKey) { - final EphemeralKeyManager keyManager = mEphemeralKeyManagerWeakReference.get(); + final EphemeralKeyManager keyManager = mEphemeralKeyManagerRef.get(); if (keyManager != null) { keyManager.updateKey(rawKey, mActionString, mArguments); } @@ -139,7 +138,7 @@ public void onKeyUpdate(@NonNull String rawKey) { @Override public void onKeyUpdateFailure(int responseCode, @Nullable String message) { - final EphemeralKeyManager keyManager = mEphemeralKeyManagerWeakReference.get(); + final EphemeralKeyManager keyManager = mEphemeralKeyManagerRef.get(); if (keyManager != null) { keyManager.updateKeyError(responseCode, message); } diff --git a/stripe/src/main/java/com/stripe/android/PaymentSession.java b/stripe/src/main/java/com/stripe/android/PaymentSession.java index c682c6c45c3..fe0ddb04b7b 100644 --- a/stripe/src/main/java/com/stripe/android/PaymentSession.java +++ b/stripe/src/main/java/com/stripe/android/PaymentSession.java @@ -256,7 +256,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { if (mPaymentSessionListener != null) { mPaymentSessionListener.onError(errorCode, errorMessage); mPaymentSessionListener.onCommunicatingStateChanged(false); diff --git a/stripe/src/main/java/com/stripe/android/view/AddSourceActivity.java b/stripe/src/main/java/com/stripe/android/view/AddSourceActivity.java index d26e9d63900..eae5ea37069 100644 --- a/stripe/src/main/java/com/stripe/android/view/AddSourceActivity.java +++ b/stripe/src/main/java/com/stripe/android/view/AddSourceActivity.java @@ -19,6 +19,7 @@ import com.stripe.android.R; import com.stripe.android.SourceCallback; import com.stripe.android.Stripe; +import com.stripe.android.StripeError; import com.stripe.android.model.Card; import com.stripe.android.model.Source; import com.stripe.android.model.SourceParams; @@ -168,7 +169,8 @@ public void onSourceRetrieved(@NonNull Source source) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { // No need to show this error, because it will be broadcast // from the CustomerSession setCommunicatingProgress(false); diff --git a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.java b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.java index 989475c4572..cbc71bf10a0 100644 --- a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.java +++ b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.java @@ -22,6 +22,7 @@ import com.stripe.android.CustomerSession; import com.stripe.android.R; +import com.stripe.android.StripeError; import com.stripe.android.model.Customer; import com.stripe.android.model.CustomerSource; @@ -107,7 +108,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { String displayedError = errorMessage == null ? "" : errorMessage; showError(displayedError); setCommunicatingProgress(false); @@ -213,7 +215,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { // Note: if this Activity is changed to subclass StripeActivity, // this code will make the error message show twice, since StripeActivity // will listen to the broadcast version of the error @@ -304,7 +307,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { setCommunicatingProgress(false); } }; @@ -344,7 +348,8 @@ public void onCustomerRetrieved(@NonNull Customer customer) { } @Override - public void onError(int errorCode, @Nullable String errorMessage) { + public void onError(int errorCode, @Nullable String errorMessage, + @Nullable StripeError stripeError) { String displayedError = errorMessage == null ? "" : errorMessage; showError(displayedError); setCommunicatingProgress(false); diff --git a/stripe/src/test/java/com/stripe/android/CustomerSessionTest.java b/stripe/src/test/java/com/stripe/android/CustomerSessionTest.java index cb0af662a90..a7334e8adb6 100644 --- a/stripe/src/test/java/com/stripe/android/CustomerSessionTest.java +++ b/stripe/src/test/java/com/stripe/android/CustomerSessionTest.java @@ -585,7 +585,7 @@ public void addSourceToCustomer_whenApiThrowsError_tellsListenerBroadcastsAndEmp captured.getSerializableExtra(CustomerSession.EXTRA_EXCEPTION); assertNotNull(ex); - verify(mockListener).onError(404, "The card is invalid"); + verify(mockListener).onError(404, "The card is invalid", null); assertTrue(session.getProductUsageTokens().isEmpty()); } @@ -698,7 +698,7 @@ public void removeSourceFromCustomer_whenApiThrowsError_tellsListenerBroadcastsA captured.getSerializableExtra(CustomerSession.EXTRA_EXCEPTION); assertNotNull(ex); - verify(mockListener).onError(404, "The card does not exist"); + verify(mockListener).onError(404, "The card does not exist", null); assertTrue(session.getProductUsageTokens().isEmpty()); } @@ -809,7 +809,7 @@ public void setDefaultSourceForCustomer_whenApiThrows_tellsListenerBroadcastsAnd APIException ex = (APIException) captured.getSerializableExtra(CustomerSession.EXTRA_EXCEPTION); assertNotNull(ex); - verify(mockListener).onError(405, "auth error"); + verify(mockListener).onError(405, "auth error", null); assertTrue(session.getProductUsageTokens().isEmpty()); } diff --git a/stripe/src/test/java/com/stripe/android/view/AddSourceActivityTest.java b/stripe/src/test/java/com/stripe/android/view/AddSourceActivityTest.java index 203e2b5d683..3ec85b1ca6a 100644 --- a/stripe/src/test/java/com/stripe/android/view/AddSourceActivityTest.java +++ b/stripe/src/test/java/com/stripe/android/view/AddSourceActivityTest.java @@ -166,16 +166,18 @@ public void softEnterKey_whenDataIsValid_hidesKeyboardAndFinishesWithIntent() { assertEquals(View.VISIBLE, mProgressBar.getVisibility()); assertEquals(Source.CARD, params.getType()); - callback.onSuccess(Source.fromString(CardInputTestActivity.EXAMPLE_JSON_CARD_SOURCE)); + final Source source = Source.fromString(CardInputTestActivity.EXAMPLE_JSON_CARD_SOURCE); + assertNotNull(source); + callback.onSuccess(source); assertEquals(RESULT_OK, mShadowActivity.getResultCode()); Intent intent = mShadowActivity.getResultIntent(); assertTrue(mActivityController.get().isFinishing()); assertTrue(intent.hasExtra(AddSourceActivity.EXTRA_NEW_SOURCE)); - Source source = + final Source newSource = Source.fromString(intent.getStringExtra(AddSourceActivity.EXTRA_NEW_SOURCE)); - assertNotNull(source); - assertEquals(Source.CARD, source.getType()); + assertNotNull(newSource); + assertEquals(Source.CARD, newSource.getType()); } @Test @@ -231,16 +233,18 @@ public void addCardData_whenDataIsValidAndServerReturnsSuccess_finishesWithInten assertEquals(View.VISIBLE, mProgressBar.getVisibility()); assertEquals(Source.CARD, params.getType()); - callback.onSuccess(Source.fromString(CardInputTestActivity.EXAMPLE_JSON_CARD_SOURCE)); + final Source source = Source.fromString(CardInputTestActivity.EXAMPLE_JSON_CARD_SOURCE); + assertNotNull(source); + callback.onSuccess(source); assertEquals(RESULT_OK, mShadowActivity.getResultCode()); Intent intent = mShadowActivity.getResultIntent(); assertTrue(mActivityController.get().isFinishing()); assertTrue(intent.hasExtra(AddSourceActivity.EXTRA_NEW_SOURCE)); - Source source = + final Source newSource = Source.fromString(intent.getStringExtra(AddSourceActivity.EXTRA_NEW_SOURCE)); - assertNotNull(source); - assertEquals(Source.CARD, source.getType()); + assertNotNull(newSource); + assertEquals(Source.CARD, newSource.getType()); } @Test @@ -414,7 +418,7 @@ public void addCardData_whenSourceCreationWorksButAddToCustomerFails_showsErrorN StripeException error = mock(StripeException.class); final String errorMessage = "Oh no! An Error!"; when(error.getLocalizedMessage()).thenReturn(errorMessage); - listener.onError(400, errorMessage); + listener.onError(400, errorMessage, null); // We're mocking the CustomerSession, so we have to replicate its broadcast behavior. Bundle bundle = new Bundle();