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

Bump Terminal SDK to 4.0.0 #847

Merged
merged 14 commits into from
Dec 17, 2024
Merged

Bump Terminal SDK to 4.0.0 #847

merged 14 commits into from
Dec 17, 2024

Conversation

tim-lin-bbpos
Copy link
Collaborator

@tim-lin-bbpos tim-lin-bbpos commented Nov 11, 2024

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.
  • Consolidate all connect* methods to a single connectReader method in js layer.

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

updated

@@ -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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

refine the listener structure now, please take a look again to see if there's any other suggestions.

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added allowRedisplay into these two feature

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added

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

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 ts layer it's optional but in native it will default to true for tap to pay now.

): 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added into this pr now.

@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 .

@tim-lin-bbpos tim-lin-bbpos force-pushed the bbpos/sdk4.0.0 branch 2 times, most recently from 1ad049c to 5a2c56f Compare November 14, 2024 07:27
@tim-lin-bbpos
Copy link
Collaborator Author

tim-lin-bbpos commented Dec 3, 2024

@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?

Done.

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

No, but should we do it in another pr if it's not affect the main feature since it's a bit massive now?
And would you mind to provide more context about the cancelable? like when should we deal with this cancelable?

@nazli-stripe
Copy link
Collaborator

No, but should we do it in another pr if it's not affect the main feature since it's a bit massive now?
And would you mind to provide more context about the cancelable? like when should we deal with this cancelable?

In v4 we now support cancelling the confirm operations, and should surface a new method like cancelCollectPaymentIntent in the RN SDK. The native SDK will handle whether the cancellation succeeds or not

I'll create a new ticket and we can address this in a new PR. Once we address the merge conflicts I think this PR is good to merge

@tim-lin-bbpos
Copy link
Collaborator Author

No, but should we do it in another pr if it's not affect the main feature since it's a bit massive now?
And would you mind to provide more context about the cancelable? like when should we deal with this cancelable?

In v4 we now support cancelling the confirm operations, and should surface a new method like cancelCollectPaymentIntent in the RN SDK. The native SDK will handle whether the cancellation succeeds or not

I'll create a new ticket and we can address this in a new PR. Once we address the merge conflicts I think this PR is good to merge

Rebased with main and fix ci too, thanks for follow up.

@tim-lin-bbpos tim-lin-bbpos changed the title [WIP] Bump Terminal SDK to 4.0.0 Bump Terminal SDK to 4.0.0 Dec 16, 2024
@nazli-stripe nazli-stripe merged commit 534ec6a into main Dec 17, 2024
3 checks passed
@nazli-stripe nazli-stripe deleted the bbpos/sdk4.0.0 branch December 17, 2024 00:10
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