diff --git a/android/src/main/java/com/stripeterminalreactnative/Mappers.kt b/android/src/main/java/com/stripeterminalreactnative/Mappers.kt index 45119ab4..4865ddaa 100644 --- a/android/src/main/java/com/stripeterminalreactnative/Mappers.kt +++ b/android/src/main/java/com/stripeterminalreactnative/Mappers.kt @@ -12,6 +12,7 @@ import com.stripe.stripeterminal.external.models.CartLineItem import com.stripe.stripeterminal.external.models.Charge import com.stripe.stripeterminal.external.models.ConnectionStatus import com.stripe.stripeterminal.external.models.DeviceType +import com.stripe.stripeterminal.external.models.DisconnectReason import com.stripe.stripeterminal.external.models.Location import com.stripe.stripeterminal.external.models.LocationStatus import com.stripe.stripeterminal.external.models.NetworkStatus @@ -562,3 +563,15 @@ fun mapFromOfflineStatus(offlineStatus: OfflineStatus): ReadableMap { putMap("reader", readerMap) } } + +fun mapFromReaderDisconnectReason(reason: DisconnectReason): String { + return when (reason) { + DisconnectReason.DISCONNECT_REQUESTED -> "disconnectRequested" + DisconnectReason.REBOOT_REQUESTED -> "rebootRequested" + DisconnectReason.SECURITY_REBOOT -> "securityReboot" + DisconnectReason.CRITICALLY_LOW_BATTERY -> "criticallyLowBattery" + DisconnectReason.POWERED_OFF -> "poweredOff" + DisconnectReason.BLUETOOTH_DISABLED -> "bluetoothDisabled" + else -> { "unknown" } + } +} diff --git a/android/src/main/java/com/stripeterminalreactnative/ReactNativeConstants.kt b/android/src/main/java/com/stripeterminalreactnative/ReactNativeConstants.kt index 80e2de30..d2faea38 100644 --- a/android/src/main/java/com/stripeterminalreactnative/ReactNativeConstants.kt +++ b/android/src/main/java/com/stripeterminalreactnative/ReactNativeConstants.kt @@ -19,4 +19,5 @@ enum class ReactNativeConstants(val listenerName: String) { CHANGE_OFFLINE_STATUS("didChangeOfflineStatus"), FORWARD_PAYMENT_INTENT("didForwardPaymentIntent"), REPORT_FORWARDING_ERROR("didReportForwardingError"), + DISCONNECT("didDisconnect") } diff --git a/android/src/main/java/com/stripeterminalreactnative/listener/RNBluetoothReaderListener.kt b/android/src/main/java/com/stripeterminalreactnative/listener/RNBluetoothReaderListener.kt index 7f9e4e28..f3c1bd00 100644 --- a/android/src/main/java/com/stripeterminalreactnative/listener/RNBluetoothReaderListener.kt +++ b/android/src/main/java/com/stripeterminalreactnative/listener/RNBluetoothReaderListener.kt @@ -3,17 +3,20 @@ package com.stripeterminalreactnative.listener import com.facebook.react.bridge.ReactApplicationContext import com.stripe.stripeterminal.external.callable.Cancelable import com.stripe.stripeterminal.external.callable.ReaderListener +import com.stripe.stripeterminal.external.models.DisconnectReason import com.stripe.stripeterminal.external.models.ReaderDisplayMessage import com.stripe.stripeterminal.external.models.ReaderInputOptions import com.stripe.stripeterminal.external.models.ReaderSoftwareUpdate import com.stripe.stripeterminal.external.models.TerminalException import com.stripeterminalreactnative.ReactExtensions.sendEvent +import com.stripeterminalreactnative.ReactNativeConstants.DISCONNECT import com.stripeterminalreactnative.ReactNativeConstants.FINISH_INSTALLING_UPDATE import com.stripeterminalreactnative.ReactNativeConstants.REPORT_AVAILABLE_UPDATE import com.stripeterminalreactnative.ReactNativeConstants.REPORT_UPDATE_PROGRESS import com.stripeterminalreactnative.ReactNativeConstants.REQUEST_READER_DISPLAY_MESSAGE import com.stripeterminalreactnative.ReactNativeConstants.REQUEST_READER_INPUT import com.stripeterminalreactnative.ReactNativeConstants.START_INSTALLING_UPDATE +import com.stripeterminalreactnative.mapFromReaderDisconnectReason import com.stripeterminalreactnative.mapFromReaderDisplayMessage import com.stripeterminalreactnative.mapFromReaderInputOptions import com.stripeterminalreactnative.mapFromReaderSoftwareUpdate @@ -72,4 +75,10 @@ class RNBluetoothReaderListener( putString("result", mapFromReaderDisplayMessage(message)) } } + + override fun onDisconnect(reason: DisconnectReason) { + context.sendEvent(DISCONNECT.listenerName) { + putString("reason", mapFromReaderDisconnectReason(reason)) + } + } } diff --git a/android/src/main/java/com/stripeterminalreactnative/listener/RNUsbReaderListener.kt b/android/src/main/java/com/stripeterminalreactnative/listener/RNUsbReaderListener.kt index e754c71b..00daa13a 100644 --- a/android/src/main/java/com/stripeterminalreactnative/listener/RNUsbReaderListener.kt +++ b/android/src/main/java/com/stripeterminalreactnative/listener/RNUsbReaderListener.kt @@ -3,17 +3,20 @@ package com.stripeterminalreactnative.listener import com.facebook.react.bridge.ReactApplicationContext import com.stripe.stripeterminal.external.callable.Cancelable import com.stripe.stripeterminal.external.callable.ReaderListener +import com.stripe.stripeterminal.external.models.DisconnectReason import com.stripe.stripeterminal.external.models.ReaderDisplayMessage import com.stripe.stripeterminal.external.models.ReaderInputOptions import com.stripe.stripeterminal.external.models.ReaderSoftwareUpdate import com.stripe.stripeterminal.external.models.TerminalException import com.stripeterminalreactnative.ReactExtensions.sendEvent +import com.stripeterminalreactnative.ReactNativeConstants.DISCONNECT import com.stripeterminalreactnative.ReactNativeConstants.FINISH_INSTALLING_UPDATE import com.stripeterminalreactnative.ReactNativeConstants.REPORT_AVAILABLE_UPDATE import com.stripeterminalreactnative.ReactNativeConstants.REPORT_UPDATE_PROGRESS import com.stripeterminalreactnative.ReactNativeConstants.REQUEST_READER_DISPLAY_MESSAGE import com.stripeterminalreactnative.ReactNativeConstants.REQUEST_READER_INPUT import com.stripeterminalreactnative.ReactNativeConstants.START_INSTALLING_UPDATE +import com.stripeterminalreactnative.mapFromReaderDisconnectReason import com.stripeterminalreactnative.mapFromReaderDisplayMessage import com.stripeterminalreactnative.mapFromReaderInputOptions import com.stripeterminalreactnative.mapFromReaderSoftwareUpdate @@ -72,4 +75,10 @@ class RNUsbReaderListener( putString("result", mapFromReaderDisplayMessage(message)) } } + + override fun onDisconnect(reason: DisconnectReason) { + context.sendEvent(DISCONNECT.listenerName) { + putString("reason", mapFromReaderDisconnectReason(reason)) + } + } } diff --git a/dev-app/src/screens/HomeScreen.tsx b/dev-app/src/screens/HomeScreen.tsx index 6ca6a64d..e8d01c81 100644 --- a/dev-app/src/screens/HomeScreen.tsx +++ b/dev-app/src/screens/HomeScreen.tsx @@ -8,6 +8,7 @@ import { Text, Image, Switch, + Alert, } from 'react-native'; import { colors } from '../colors'; import { AppContext } from '../AppContext'; @@ -76,6 +77,12 @@ export default function HomeScreen() { Toast.hide(toast); }, 3000); }, + onDidDisconnect(reason) { + Alert.alert( + 'Reader disconnected!', + 'Reader disconnected with reason ' + reason + ); + }, } ); const batteryPercentage = diff --git a/ios/Mappers.swift b/ios/Mappers.swift index 3826c66c..950b8a3e 100644 --- a/ios/Mappers.swift +++ b/ios/Mappers.swift @@ -600,6 +600,18 @@ class Mappers { return(["sdk": sdkDict, "reader": readerDict]) } + + class func mapFromReaderDisconnectReason(_ reason: DisconnectReason) -> String { + switch reason { + case DisconnectReason.disconnectRequested: return "disconnectRequested" + case DisconnectReason.rebootRequested: return "rebootRequested" + case DisconnectReason.securityReboot: return "securityReboot" + case DisconnectReason.criticallyLowBattery: return "criticallyLowBattery" + case DisconnectReason.poweredOff: return "poweredOff" + case DisconnectReason.bluetoothDisabled: return "bluetoothDisabled" + default: return "unknown" + } + } } extension UInt { diff --git a/ios/StripeTerminalReactNative.swift b/ios/StripeTerminalReactNative.swift index 3af483dd..81af3163 100644 --- a/ios/StripeTerminalReactNative.swift +++ b/ios/StripeTerminalReactNative.swift @@ -20,6 +20,7 @@ enum ReactNativeConstants: String, CaseIterable { case CHANGE_OFFLINE_STATUS = "didChangeOfflineStatus" case FORWARD_PAYMENT_INTENT = "didForwardPaymentIntent" case REPORT_FORWARDING_ERROR = "didReportForwardingError" + case DISCONNECT = "didDisconnect" } @objc(StripeTerminalReactNative) @@ -982,6 +983,11 @@ class StripeTerminalReactNative: RCTEventEmitter, DiscoveryDelegate, BluetoothRe let result = Mappers.mapFromReaderDisplayMessage(displayMessage) sendEvent(withName: ReactNativeConstants.REQUEST_READER_DISPLAY_MESSAGE.rawValue, body: ["result": result]) } + + func reader(_ reader: Reader, didDisconnect reason: DisconnectReason) { + let result = Mappers.mapFromReaderDisconnectReason(reason) + sendEvent(withName: ReactNativeConstants.DISCONNECT.rawValue, body: ["reason": result]) + } func localMobileReader(_ reader: Reader, didStartInstallingUpdate update: ReaderSoftwareUpdate, cancelable: Cancelable?) { self.installUpdateCancelable = cancelable diff --git a/src/components/StripeTerminalProvider.tsx b/src/components/StripeTerminalProvider.tsx index 7250bffd..cfcb1ac0 100644 --- a/src/components/StripeTerminalProvider.tsx +++ b/src/components/StripeTerminalProvider.tsx @@ -34,6 +34,7 @@ const { CHANGE_OFFLINE_STATUS, FORWARD_PAYMENT_INTENT, REPORT_FORWARDING_ERROR, + DISCONNECT, } = NativeModules.StripeTerminalReactNative.getConstants(); const emitter = new EventEmitter(); @@ -232,6 +233,14 @@ export function StripeTerminalProvider({ [log] ); + const didDisconnect = useCallback( + ({ reason }: { reason?: Reader.DisconnectReason }) => { + log('didDisconnect', reason); + emitter?.emit(DISCONNECT, reason); + }, + [log] + ); + useListener(REPORT_AVAILABLE_UPDATE, didReportAvailableUpdate); useListener(START_INSTALLING_UPDATE, didStartInstallingUpdate); useListener(REPORT_UPDATE_PROGRESS, didReportReaderSoftwareUpdateProgress); @@ -256,6 +265,8 @@ export function StripeTerminalProvider({ useListener(FORWARD_PAYMENT_INTENT, didForwardPaymentIntent); useListener(REPORT_FORWARDING_ERROR, didReportForwardingError); + useListener(DISCONNECT, didDisconnect); + const tokenProviderHandler = async () => { try { const connectionToken = await tokenProvider(); diff --git a/src/hooks/useStripeTerminal.tsx b/src/hooks/useStripeTerminal.tsx index 6292ca08..25ade935 100644 --- a/src/hooks/useStripeTerminal.tsx +++ b/src/hooks/useStripeTerminal.tsx @@ -83,6 +83,7 @@ export const { CHANGE_OFFLINE_STATUS, FORWARD_PAYMENT_INTENT, REPORT_FORWARDING_ERROR, + DISCONNECT, } = NativeModules.StripeTerminalReactNative.getConstants(); const NOT_INITIALIZED_ERROR_MESSAGE = @@ -143,6 +144,7 @@ export function useStripeTerminal(props?: Props) { onDidChangeOfflineStatus, onDidForwardPaymentIntent, onDidForwardingFailure, + onDidDisconnect, } = props || {}; const _discoverReaders = useCallback( @@ -298,6 +300,13 @@ export function useStripeTerminal(props?: Props) { [onDidForwardingFailure] ); + const didDisconnect = useCallback( + ({ reason }: { reason?: Reader.DisconnectReason }) => { + onDidDisconnect?.(reason); + }, + [onDidDisconnect] + ); + useListener(REPORT_AVAILABLE_UPDATE, didReportAvailableUpdate); useListener(START_INSTALLING_UPDATE, didStartInstallingUpdate); useListener(REPORT_UPDATE_PROGRESS, didReportReaderSoftwareUpdateProgress); @@ -322,6 +331,8 @@ export function useStripeTerminal(props?: Props) { useListener(FORWARD_PAYMENT_INTENT, didForwardPaymentIntent); useListener(REPORT_FORWARDING_ERROR, didReportForwardingError); + useListener(DISCONNECT, didDisconnect); + const _initialize = useCallback(async () => { if (!initialize || typeof initialize !== 'function') { const errorMessage = diff --git a/src/types/Reader.ts b/src/types/Reader.ts index 1da7caee..40cec96c 100644 --- a/src/types/Reader.ts +++ b/src/types/Reader.ts @@ -108,4 +108,13 @@ export namespace Reader { | 'cardRemovedTooEarly'; export type ConnectionStatus = 'connected' | 'connecting' | 'notConnected'; + + export type DisconnectReason = + | 'disconnectRequested' + | 'rebootRequested' + | 'securityReboot' + | 'criticallyLowBattery' + | 'poweredOff' + | 'bluetoothDisabled' + | 'unknown'; } diff --git a/src/types/index.ts b/src/types/index.ts index 95aeec80..6087b2b3 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -351,6 +351,8 @@ export type UserCallbacks = { error: StripeError ): void; onDidForwardingFailure?(error?: StripeError): void; + + onDidDisconnect?(reason?: Reader.DisconnectReason): void; }; export namespace PaymentMethod {