Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Bump Terminal SDK to 4.0.0 #847

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open

Conversation

tim-lin-bbpos
Copy link
Collaborator

Summary

Bump Terminal SDK to 4.0.0

Motivation

  • Change LocalMobile keyword to TapToPay to align Android/iOS sdk.
  • Remove REPORT_UNEXPECTED_READER_DISCONNECT as we can now use DISCONNECT directly.
  • Modify Listener structure on Android side
  • Increase iOS minimal sdk to 14 to align terminal iOS sdk

Testing

  • I tested this manually
  • I added automated tests

Documentation

Select one:

  • Require doc change.
  • I have added relevant documentation for my changes.
  • This PR does not result in any developer-facing changes.

@@ -659,7 +602,7 @@ private fun mapFromWechatPayDetails(wechatPayDetails: WechatPayDetails?): Readab
private fun mapFromOfflineDetails(offlineDetails: OfflineDetails?): ReadableMap? =
offlineDetails?.let {
nativeMapOf {
putString("storedAt", offlineDetails.storedAt.toString())
putString("storedAt", offlineDetails.storedAtMs.toString())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to update the string name?

Suggested change
putString("storedAt", offlineDetails.storedAtMs.toString())
putString("storedAtMs", offlineDetails.storedAtMs.toString())

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's also used on ios side and name only changed in Android now.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it's still storedAt for iOS. @bric-stripe this could be something to consider for SDKv5?

Android Jira ticket for context.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it makes sense for the RN layer to expose a consistent value though, right? I think RN exposing storedAtMs and then in the swift layer below it can just convert from the s to the ms value so its consistent regardless of platform...and yeah we should fix it up on the native side. we could add the storedAtMs next to storedAt in a minor update and then drop storedAt in v5

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 218 to 219
val autoReconnectOnUnexpectedDisconnect = if (discoveryMethod == DiscoveryMethod.BLUETOOTH_SCAN || discoveryMethod == DiscoveryMethod.USB || discoveryMethod == DiscoveryMethod.TAP_TO_PAY) {
getBoolean(params, "autoReconnectOnUnexpectedDisconnect")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no autoReconnectOnUnexpectedDisconnect is provided, it should default to true.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to make sure are your referring to the scope of BLUETOOTH_SCAN, USB, TAP_TO_PAY only or no matter which discoveryMethod.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's only for BLUETOOTH_SCAN, USB and TAP_TO_PAY. For other connection types, it shouldn't matter if a autoReconnectOnUnexpectedDisconnect value is provided.

@@ -250,7 +194,7 @@ class StripeTerminalReactNativeModule(reactContext: ReactApplicationContext) :
params: ReadableMap,
promise: Promise,
discoveryMethod: DiscoveryMethod,
listener: ReaderListenable? = null
listener: ReaderDisconnectListener? = null

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why we pass in ReaderDisconnectListener here. I think the listener should be one of the following:

  • RNBluetoothReaderListener
  • RNHandoffReaderListener
  • RNInternetReaderListener
  • RNTapToPayReaderListener

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya, but RNInternetReaderListener, RNTapToPayReaderListener are not ReaderListenable, if we want to use the same pattern here we have to find a common parent, or we need to expose new parameter (probably two in this case).
Another idea is to declare listener and discoveryMethod relationship in a sealed class and use that binding for more type safe usage but it require more change the scope of bump sdk version.
What do you think on these two or any other suggestions?
Also add a todo

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we want to use the same pattern here we have to find a common parent, or we need to expose new parameter (probably two in this case).

I think it's fine to expose new parameters in private methods.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
listener: ReaderDisconnectListener? = null
connectionConfiguration: RNConnectionConfiguration? = null

Maybe we can do something like this, so it mirrors the Android and iOS SDKs?

Comment on lines +609 to +610
AllowRedisplay.ALWAYS,
// customerConsentCollected,//TODO: seems removed
Copy link

@sjl-stripe sjl-stripe Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should give an option for the merchant to set the AllowRedisplay parameter. If the merchant doesn't provide one, we should default to AllowRedisplay.UNSPECIFIED

The removal of the customerConsentCollected parameter is expected.

cc @henryx-stripe

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we'll have new parameter for user on AllowRedisplay right?
But what is the different meaning to user/rn sdk? is there other required change corresponding to this attribute?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can find more details about this specific change in our Stripe docs here. We also have Android docs here and iOS docs here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup @tim-lin-bbpos

for collectSetupIntentPaymentMethod the user is required to pass in AllowRedisplay now, they are no longer expected to pass customerConsentCollected

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please also include an optional AllowRedisplay field for CollectConfiguration for CollectPaymentMethod (line ~500) and pass that into the SDK as well

Comment on lines 45 to 49
reader: Reader,
locationId: String,
autoReconnectOnUnexpectedDisconnect: Boolean = false,
listener: ReaderListenable? = null,
reconnectionListener: ReaderReconnectionListener
disconnectListener: ReaderDisconnectListener? = null,
reconnectionListener: ReaderReconnectionListener,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this connectReader(...) method publicly exposed in the RN SDK? If not, can we remove it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RN user should only use RN sdk via js interface not any native Kotlin/Android code.

Comment on lines 45 to 49
reader: Reader,
locationId: String,
autoReconnectOnUnexpectedDisconnect: Boolean = false,
listener: ReaderListenable? = null,
reconnectionListener: ReaderReconnectionListener
disconnectListener: ReaderDisconnectListener? = null,
reconnectionListener: ReaderReconnectionListener,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if we can make ReaderReconnectionListener optional to pass in, since it's not required by internet and handoff readers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can make the declaration optional but it won't help much in this case since there's only one usage.
Maybe best way is to initiate this listener when need if it's not common for all the cases but it also imply a bigger refactoring which I try to avoid in this stage.
Mark a todo here.


class RNBluetoothReaderListener(
private val context: ReactApplicationContext,
private val onStartInstallingUpdate: (cancelable: Cancelable?) -> Unit
) : ReaderListener {
) : MobileReaderListener, ReaderDisconnectListener by readerDisconnectDelete(context) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why are we adding a readerDisconnectDelete delegate here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid adding same code again and again, as these interface is not required so people can remove if not carefully, for example the RNHandoffReaderListener is not implement the disconnect part before.

Comment on lines 34 to 38
reason: DisconnectReason
) {
onReaderReconnectStarted(cancelReconnect)
context.sendEvent(START_READER_RECONNECT.listenerName) {
putMap("reader", mapFromReader(reader))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add the disconnect reason to the event?

src/types/Reader.ts Outdated Show resolved Hide resolved
@@ -47,7 +47,7 @@ export type ConnectUsbReaderParams = {
autoReconnectOnUnexpectedDisconnect?: boolean;
};

export type ConnectLocalMobileParams = {
export type ConnectTapToPayParams = {
reader: Reader.Type;
locationId?: string;
onBehalfOf?: string;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update ConnectTapToPayParams so that autoReconnectOnUnexpectedDisconnect defaults to true

): Reader = when (discoveryMethod) {
DiscoveryMethod.BLUETOOTH_SCAN -> {
val connConfig = BluetoothConnectionConfiguration(
locationId,
autoReconnectOnUnexpectedDisconnect,
reconnectionListener
(disconnectListener as? MobileReaderListener).bindReconnectionListener(reconnectionListener)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like we don't give an option to pass in a ReaderListenable here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing as above, right now disconnectListener is acted like the original listener but I'm also struggling if there's other better alternative.

Comment on lines +241 to +248
case .tapToPayReaderFailedToPrepare:
return "TapToPayReaderFailedToPrepare"
case .tapToPayReaderDeviceBanned:
return "TapToPayReaderDeviceBanned"
case .tapToPayReaderTOSNotYetAccepted:
return "TapToPayReaderTOSNotYetAccepted"
case .tapToPayReaderTOSAcceptanceFailed:
return "TapToPayReaderTOSAcceptanceFailed"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've added a new error code to the iOS and Android SDK. (for iOS, it's SCPErrorGenericReaderError, and for Android it's GENERIC_READER_ERROR). Can we add a new external-facing RN error code for this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Android we use toString so it doesn't matter if there's new element or not, but on iOS side besides .genericReaderError we still have all of these:

        case .cancelFailedUnavailable:
            <#code#>
        case .invalidConnectionConfiguration:
            <#code#>
        case .invalidRequiredParameterOnBehalfOf:
            <#code#>
        case .requestDynamicCurrencyConversionRequiresUpdatePaymentIntent:
            <#code#>
        case .dynamicCurrencyConversionNotAvailable:
            <#code#>
        case .surchargeNoticeRequiresUpdatePaymentIntent:
            <#code#>
        case .surchargeUnavailableWithDynamicCurrencyConversion:
            <#code#>
        case .canceledDueToIntegrationError:
            <#code#>
        case .collectInputsInvalidParameter:
            <#code#>
        case .collectInputsUnsupported:
            <#code#>
        case .readerConnectionOfflineNeedsUpdate:
            <#code#>
        case .readerConnectionOfflinePairingUnseenDisabled:
            <#code#>
        case .collectInputsTimedOut:
            <#code#>
        case .usbDiscoveryTimedOut:
            <#code#>
        case .tapToPayReaderAccountDeactivated:
            <#code#>
        case .readerMissingEncryptionKeys:
            <#code#>
        case .usbDisconnected:
            <#code#>
        case .encryptionKeyFailure:
            <#code#>
        case .encryptionKeyStillInitializing:
            <#code#>
        case .collectInputsApplicationError:
            <#code#>
        case .genericReaderError:
            <#code#>
        case .commandInvalidAllowRedisplay:
            <#code#>
        case .onlinePinNotSupportedOffline:
            <#code#>
        case .offlineTestCardInLivemode:

Do we need to add them all or only the one you mentioned?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to add all these iOS errors (cc @bric-stripe @mindy-stripe to confirm)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems we still need some time to merge this, create in separate pr here: #851
Will add new ones here when able to rebase.

Also would like to know if we're consider some ways to automatically convert enum to string without hardcode like Android side?
https://stackoverflow.com/questions/24701075/swift-convert-enum-value-to-string

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also would like to know if we're consider some ways to automatically convert enum to string without hardcode

unfortunately the enum backing ErrorCode.Code in the native SDK is an ObjC enum and so can't be declared as being backed by a String.

in the native SDK we do have a code to string helper already and we could expose that, but it wouldn't be available until 4.2 at the earliest.

@nazli-stripe
Copy link
Collaborator

@tim-lin-bbpos To match the new native commands can we consolidate all connect* methods to a single connectReader method? @sjl-stripe are there other method / parameter name changes we are missing in the React Native layer?

@sjl-stripe
Copy link

@tim-lin-bbpos you might've already made this change - confirmPaymentIntent, confirmSetupIntent and confirmRefund now return a Cancelable .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants