Skip to content

Commit 0a3df4e

Browse files
authored
4.2.0 release (#554)
1 parent 77a98a9 commit 0a3df4e

File tree

1,695 files changed

+9963
-7572
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,695 files changed

+9963
-7572
lines changed

CHANGELOG.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
This document details changes made to the SDK by version. The current status
44
of each release can be found in the [Support Lifecycle](SUPPORT.md).
55

6+
## 4.2.0 - 2025-02-24
7+
8+
### Core
9+
- New: Added error code [`READER_TAMPERED`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-terminal-error-code/-r-e-a-d-e-r_-t-a-m-p-e-r-e-d/index.html)
10+
- Fix: Addressed an issue where readers lose optional software updates after auto-reconnecting.
11+
12+
### Tap to Pay
13+
14+
- New: [`HIDE_OVERLAY_WINDOW`](https://developer.android.com/security/fraud-prevention/activities#hide_overlay_windows) permission is now required for Tap to Pay to ensure a secure environment while operating.
15+
- Fix: Allow the Tap to Pay reader to connect from applications that have enabled installer checks in Google Play. Fixes [issue 866](https://github.com/stripe/stripe-terminal-react-native/issues/866).
16+
- Fix: Localization issues for Tap to Pay on Android PIN preview.
17+
618
## 4.1.0 - 2024-11-18
719

820
### Core
@@ -12,7 +24,7 @@ of each release can be found in the [Support Lifecycle](SUPPORT.md).
1224
- Update: Added support for operating offline with simulated Bluetooth and USB readers.
1325
- Preview: Added a new enum value `Manual` to [`CardPresentCaptureMethod`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-card-present-capture-method/-companion/index.html) which will override the top level [`captureMethod`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-payment-intent-parameters/index.html?query=class%20PaymentIntentParameters) set on the Paymentintent specifically for `card_present` transactions.
1426
- If you are interested in joining this preview, please email [[email protected]](mailto:[email protected]).
15-
- Preview: [`Terminal::collectData`](https://stripe.dev/stripe-terminal-android/core/com.stripe.stripeterminal/-terminal/collect-data.html) will be supported on Smart readers.
27+
- Preview: [`Terminal::collectData`](https://stripe.dev/stripe-terminal-android/core/com.stripe.stripeterminal/-terminal/collect-data.html) will be supported on Smart readers.
1628
- _Note: [This feature](https://docs.stripe.com/terminal/features/collect-data) requires [reader software version](https://stripe.com/docs/terminal/readers/bbpos-wisepos-e#reader-software-version) `2.28` or later to be installed on your smart reader._
1729
- If you are interested in joining this preview, please email [[email protected]](mailto:[email protected]).
1830
- Fix: Fixed an issue where, if the SDK was initialized offline and a user immediately attempts to pair a reader offline, the first attempt fails with "The selected reader requires a software update", despite the reader being up-to-date.
@@ -30,7 +42,7 @@ Add support for apps built with `targetSdkVersion = 35` targeting Android 15 dev
3042
- Note: This update includes support for Tap to Pay on Android. Users who were previously advised not to upgrade can now safely target version 35.
3143

3244
#### New Features
33-
* [Mail order / telephone order (MOTO)](https://docs.stripe.com/terminal/features/mail-telephone-orders/overview) payment support for smart readers.
45+
* [Mail order / telephone order (MOTO)](https://docs.stripe.com/terminal/features/mail-telephone-orders/overview) payment support for smart readers.
3446
- Contact Stripe support to enable this feature on your account.
3547
* Global [card saving after payment](https://docs.stripe.com/terminal/features/saving-cards/save-after-payment) support by updating customer consent capture. The following **breaking changes** are required:
3648
- Removed the `customerConsentCollected` parameter from `Terminal::collectSetupIntentPaymentMethod` and replaced it with `allowRedisplay`.
@@ -45,11 +57,11 @@ Add support for apps built with `targetSdkVersion = 35` targeting Android 15 dev
4557
- Update: Internet and Tap to Pay discovery will now call the [`discoverReaders`](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc(cs)SCPTerminal(im)discoverReaders:delegate:completion:) completion block when the operation completes since these are not long running discovery operations.
4658
- Update: Fields on the [`Location`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-location/index.html) object are no longer mutable.
4759

48-
#### Reader connection
60+
#### Reader connection
4961

5062
- Update: There is now a single [`Terminal::connectReader`](https://stripe.dev/stripe-terminal-android/core/com.stripe.stripeterminal/-terminal/connect-reader.html) method for all connection types. This replaces the previous methods: `connectBluetoothReader`, `connectUsbReader`, `connectInternetReader`, `connectLocalMobileReader`, and `connectHandoffReader`.
5163
- For mobile readers, the `readerListener` parameter has been removed from the old `connectBluetoothReader`, `connectUsbReader` methods and moved into the respective `ConnectionConfiguration` object, replacing `ReaderReconnectionListener`.
52-
- For Tap to Pay readers, the `TapToPayConnectionConfiguration` now takes in a `TapToPayReaderListener` parameter, replacing `ReaderReconnectionListener`.
64+
- For Tap to Pay readers, the `TapToPayConnectionConfiguration` now takes in a `TapToPayReaderListener` parameter, replacing `ReaderReconnectionListener`.
5365
- For smart readers, `InternetConnectionConfiguration` now takes in an [`InternetReaderListener`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.callable/-internet-reader-listener/index.html) parameter, which will alert your integration of events such as reader disconnects.
5466
- For [Apps on devices](/terminal/features/apps-on-devices/overview#pos-stripe-device) in handoff mode, [`HandoffReaderListener`]((https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.callable/-handoff-reader-listener/index.html)) has been removed from the old `connectHandoffReader` method as a parameter, and moved into the `HandoffConnectionConfiguration` object.
5567
- Update: [Auto reconnect on unexpected disconnect](https://docs.stripe.com/terminal/payments/connect-reader?terminal-sdk-platform=android&reader-type=tap-to-pay#automatically-attempt-reconnection) is now enabled by default for mobile readers and Tap to Pay readers.
@@ -85,13 +97,15 @@ Add support for apps built with `targetSdkVersion = 35` targeting Android 15 dev
8597
- Update: in `ReaderSoftwareUpdate`, rename `UpdateTimeEstimate` to [`UpdateDurationEstimate`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-reader-software-update/-update-duration-estimate/index.html), and `timeEstimate` to [`durationEstimate`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-reader-software-update/duration-estimate.html).
8698
- Update: Converted `java.util.Date` references to timestamps in milliseconds for the following fields: `ReaderSoftwareUpdate::requiredAt`, `OfflineDetails::storedAt` and `OfflineSetupIntentDetails::storedAt`.
8799

88-
### Tap to Pay
100+
### Tap to Pay
89101

90102
- **Update**: The Maven coordinates for the Tap to Pay on Android feature have changed to `com.stripe:stripeterminal-taptopay:4.0.0`. Please update your build dependencies to point to the new artifact name. The old one will no longer be updated.
91103
- Update: SafetyNet Attestation API has been removed and replaced with Play Integrity API. Fixes [issue 458](https://github.com/stripe/stripe-terminal-android/issues/458).
92104
- Update: `TapToPayConnectionConfiguration` now takes in a `TapToPayReaderListener` parameter. This listener inherits events from both `ReaderReconnectionListener` and `ReaderDisconnectionListener`, providing a unified interface for handling reader events.
93105
- Update: The `collectPaymentMethod` and `collectSetupIntentPaymentMethod` now time out after 60 seconds for Tap to Pay on Android transactions. If a timeout occurs, a [`TerminalException`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-terminal-exception/index.html) will be raised with the error code [`CARD_READ_TIMED_OUT`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-terminal-error-code/-c-a-r-d_-r-e-a-d_-t-i-m-e-d_-o-u-t/index.html)
94106
- Update: When PIN collection is requested for a payment, a [`TerminalException`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-terminal-exception/index.html) will be raised with error code [`FEATURE_NOT_ENABLED_ON_ACCOUNT`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-terminal-error-code/-f-e-a-t-u-r-e_-n-o-t_-e-n-a-b-l-e-d_-o-n_-a-c-c-o-u-n-t/index.html) instead of [`DECLINED_BY_STRIPE_API`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.models/-terminal-error-code/-d-e-c-l-i-n-e-d_-b-y_-s-t-r-i-p-e_-a-p-i/index.html) with an [`ONLINE_OR_OFFLINE_PIN_REQUIRED`](https://docs.stripe.com/declines/codes#:~:text=online_or_offline_pin_required) [`ApiError`](https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.api/-api-error/index.html?query=data%20class%20ApiError(error:%20InnerError)%20:%20Serializable).
107+
- Update: Reduce the amount of time [`Terminal::connectReader`](https://stripe.dev/stripe-terminal-android/core/com.stripe.stripeterminal/-terminal/connect-reader.html) takes to complete attestation.
108+
- Update: Improve acceptance of some cards that previously would have displayed "Please use another card"
95109

96110
- Update: "Local Mobile" has been renamed to "Tap To Pay" in all function names and error codes to align with Stripe branding:
97111
- `LocalMobileDiscoveryConfiguration` has been renamed to `TapToPayDiscoveryConfiguration`.

Example/javaapp/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ android {
3636

3737
val androidxLifecycleVersion = "2.6.2"
3838
val retrofitVersion = "2.11.0"
39-
val stripeTerminalVersion = "4.1.0"
39+
val stripeTerminalVersion = "4.2.0"
4040

4141
dependencies {
4242
implementation("com.google.android.material:material:1.11.0")

Example/javaapp/src/main/java/com/stripe/example/javaapp/MainActivity.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public void onCancelDiscovery() {
9999
}
100100

101101
@Override
102-
public void onRequestChangeLocation() {
102+
public void onRequestLocationSelection() {
103103
navigateTo(
104104
LocationSelectionFragment.TAG,
105105
LocationSelectionFragment.newInstance(),
@@ -108,16 +108,24 @@ public void onRequestChangeLocation() {
108108
);
109109
}
110110

111+
@Override
112+
public void onCancelLocationSelection() {
113+
getSupportFragmentManager().popBackStackImmediate();
114+
}
115+
111116
@Override
112117
public void onRequestCreateLocation() {
113118
navigateTo(LocationCreateFragment.TAG, LocationCreateFragment.newInstance(), false, true);
114119
}
115120

121+
@Override
122+
public void onCancelCreateLocation() {
123+
getSupportFragmentManager().popBackStackImmediate();
124+
}
125+
116126
@Override
117127
public void onLocationCreated() {
118128
getSupportFragmentManager().popBackStackImmediate();
119-
List<Fragment> fragments = getSupportFragmentManager().getFragments();
120-
((LocationSelectionFragment) fragments.get(fragments.size() - 1)).reload();
121129
}
122130

123131
/**
@@ -349,6 +357,10 @@ public void onLocationCleared() {
349357
* ReaderReconnectionListener implementation.
350358
*/
351359
@Override
360+
public void onReaderReconnectStarted(@NonNull Reader reader, @NonNull Cancelable cancelReconnect, @NonNull DisconnectReason reason) {
361+
Log.d("MainActivity", "Reconnection to reader " + reader.getId() + " started!");
362+
}
363+
@Override
352364
public void onReaderReconnectSucceeded(@NonNull Reader reader) {
353365
Log.d("MainActivity", "Reader " + reader.getId() + " reconnected successfully!");
354366
}

Example/javaapp/src/main/java/com/stripe/example/javaapp/NavigationListener.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,23 @@ public interface NavigationListener {
7272
/**
7373
* Notify the `Activity` that the user has requested to change the location.
7474
*/
75-
void onRequestChangeLocation();
75+
void onRequestLocationSelection();
76+
77+
/**
78+
* Notify the `Activity` that the location selection flow has been canceled.
79+
*/
80+
void onCancelLocationSelection();
7681

7782
/**
7883
* Notify the `Activity` that the user has requested to add a location.
7984
*/
8085
void onRequestCreateLocation();
8186

87+
/**
88+
* Notify the `Activity` that the create location flow has been canceled.
89+
*/
90+
void onCancelCreateLocation();
91+
8292
/**
8393
* Notify the `Activity` that the user has finished creating a location.
8494
*/

Example/javaapp/src/main/java/com/stripe/example/javaapp/fragment/location/LocationCreateFragment.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import androidx.fragment.app.Fragment;
1313

1414
import com.google.android.material.snackbar.Snackbar;
15+
import com.stripe.example.javaapp.MainActivity;
1516
import com.stripe.example.javaapp.NavigationListener;
1617
import com.stripe.example.javaapp.R;
1718
import com.stripe.example.javaapp.network.ApiClient;
@@ -30,7 +31,10 @@ public static LocationCreateFragment newInstance() {
3031
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
3132
View view = inflater.inflate(R.layout.fragment_location_create, container, false);
3233
binding = FragmentLocationCreateBinding.bind(view);
33-
binding.locationCreateSubmit.setOnClickListener((View.OnClickListener) v -> onSubmit());
34+
binding.locationCreateSubmit.setOnClickListener(v -> onSubmit());
35+
binding.locationCreateCancelButton.setOnClickListener(v ->
36+
((MainActivity) requireActivity()).onCancelCreateLocation()
37+
);
3438

3539
return view;
3640
}
@@ -39,18 +43,18 @@ private void onSubmit() {
3943
try {
4044
ApiClient.createLocation(
4145
requireValue(binding.locationCreateDisplayNameInput),
42-
getValue(binding.locationCreateAddressCityInput),
43-
requireValue(binding.locationCreateAddressCountryInput),
44-
getValue(binding.locationCreateAddressLine1Input),
46+
requireValue(binding.locationCreateAddressLine1Input),
4547
getValue(binding.locationCreateAddressLine2Input),
48+
getValue(binding.locationCreateAddressCityInput),
4649
getValue(binding.locationCreateAddressPostalInput),
47-
getValue(binding.locationCreateAddressStateInput)
50+
getValue(binding.locationCreateAddressStateInput),
51+
requireValue(binding.locationCreateAddressCountryInput)
4852
);
49-
((NavigationListener) getActivity()).onLocationCreated();
53+
((NavigationListener) requireActivity()).onLocationCreated();
5054
} catch (IllegalStateException e) {
5155
Log.d(TAG, "Missing required input");
5256
} catch (Throwable e) {
53-
Snackbar.make(getView(), e.getMessage() == null ? "Unknown Error" : e.getMessage(), Snackbar.LENGTH_LONG).show();
57+
Snackbar.make(requireView(), e.getMessage() == null ? "Unknown Error" : e.getMessage(), Snackbar.LENGTH_LONG).show();
5458
}
5559
}
5660

@@ -61,7 +65,7 @@ public void onDestroyView() {
6165
}
6266

6367
private String requireValue(EditText receiver) {
64-
if (receiver.getText() == null || receiver.getText().toString().trim().equals("")) {
68+
if (receiver.getText() == null || receiver.getText().toString().trim().isEmpty()) {
6569
receiver.setError(getResources().getString(R.string.field_required));
6670
throw new IllegalStateException();
6771
}
@@ -71,7 +75,7 @@ private String requireValue(EditText receiver) {
7175

7276
@Nullable
7377
private String getValue(EditText receiver) {
74-
if (receiver.getText() == null || receiver.getText().toString().trim().equals("")) {
78+
if (receiver.getText() == null || receiver.getText().toString().trim().isEmpty()) {
7579
return null;
7680
}
7781
return receiver.getText().toString();

Example/javaapp/src/main/java/com/stripe/example/javaapp/fragment/location/LocationSelectionFragment.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import androidx.lifecycle.ViewModelProvider;
1313
import androidx.recyclerview.widget.LinearLayoutManager;
1414

15+
import com.stripe.example.javaapp.MainActivity;
16+
import com.stripe.example.javaapp.NavigationListener;
1517
import com.stripe.example.javaapp.R;
1618
import com.stripe.example.javaapp.recyclerview.InfiniteScrollListener;
1719
import com.stripe.example.javaapp.viewmodel.LocationSelectionViewModel;
@@ -31,6 +33,12 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
3133
viewModel = new ViewModelProvider(this).get(LocationSelectionViewModel.class);
3234
}
3335

36+
@Override
37+
public void onResume() {
38+
super.onResume();
39+
viewModel.reload();
40+
}
41+
3442
@Nullable
3543
@Override
3644
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@@ -43,17 +51,25 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
4351
binding.locationSelectionList.addOnScrollListener(new InfiniteScrollListener(layoutManager, () -> viewModel.loadMoreLocations()));
4452
binding.locationSelectionList.setAdapter(adapter);
4553
binding.locationSelectionToolbar.inflateMenu(R.menu.location_selection);
54+
binding.locationSelectionToolbar.setOnMenuItemClickListener(item -> {
55+
if (item.getItemId() == R.id.menu_location_selection_add) {
56+
((NavigationListener) requireActivity()).onRequestCreateLocation();
57+
return true;
58+
} else {
59+
return false;
60+
}
61+
});
4662

47-
viewModel.listState.observe(getViewLifecycleOwner(), (state) -> adapter.setLocationListState(state));
63+
binding.locationSelectionCancelButton.setOnClickListener(v ->
64+
((MainActivity) requireActivity()).onCancelLocationSelection()
65+
);
66+
67+
viewModel.listState.observe(getViewLifecycleOwner(), adapter::setLocationListState);
4868
viewModel.error.observe(getViewLifecycleOwner(), (error) -> Toast.makeText(getActivity(), error.getErrorMessage(), Toast.LENGTH_LONG).show());
4969

5070
return view;
5171
}
5272

53-
public void reload() {
54-
viewModel.reload();
55-
}
56-
5773
public static LocationSelectionFragment newInstance() {
5874
return new LocationSelectionFragment();
5975
}

Example/javaapp/src/main/java/com/stripe/example/javaapp/network/ApiClient.java

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@
22

33
import com.stripe.example.javaapp.BuildConfig;
44
import com.stripe.example.javaapp.model.ConnectionToken;
5-
import com.stripe.example.javaapp.model.PaymentIntentCreationResponse;
65
import com.stripe.stripeterminal.external.models.ConnectionTokenException;
76

87
import org.jetbrains.annotations.NotNull;
98

109
import java.io.IOException;
11-
import java.util.HashMap;
12-
import java.util.Map;
1310

1411
import okhttp3.OkHttpClient;
1512
import retrofit2.Callback;
@@ -42,17 +39,31 @@ public static String createConnectionToken() throws ConnectionTokenException {
4239
}
4340
}
4441

45-
/** @noinspection unused*/
4642
public static void createLocation(
47-
String displayName,
48-
String city,
49-
String country,
50-
String line1,
43+
@NotNull String displayName,
44+
@NotNull String line1,
5145
String line2,
46+
String city,
5247
String postalCode,
53-
String state
54-
) {
55-
// TODO: Call backend to create location
48+
String state,
49+
@NotNull String country
50+
) throws Exception {
51+
try {
52+
final Response<Void> result = mService.createLocation(
53+
displayName,
54+
line1,
55+
line2,
56+
city,
57+
postalCode,
58+
state,
59+
country
60+
).execute();
61+
if (!result.isSuccessful()) {
62+
throw new Exception("Creating location failed");
63+
}
64+
} catch (IOException e) {
65+
throw new Exception("Creating location failed", e);
66+
}
5667
}
5768

5869
public static void capturePaymentIntent(@NotNull String id) throws IOException {

0 commit comments

Comments
 (0)