diff --git a/android/src/main/java/com/stripeterminalreactnative/StripeTerminalReactNativeModule.kt b/android/src/main/java/com/stripeterminalreactnative/StripeTerminalReactNativeModule.kt index ef81e545..363d0c0f 100644 --- a/android/src/main/java/com/stripeterminalreactnative/StripeTerminalReactNativeModule.kt +++ b/android/src/main/java/com/stripeterminalreactnative/StripeTerminalReactNativeModule.kt @@ -300,6 +300,13 @@ class StripeTerminalReactNativeModule(reactContext: ReactApplicationContext) : terminal.disconnectReader(NoOpCallback(promise)) } + @ReactMethod + @Suppress("unused") + fun rebootReader(promise: Promise) { + paymentIntents.clear() + terminal.rebootReader(NoOpCallback(promise)) + } + @ReactMethod @Suppress("unused") fun cancelReaderReconnection(promise: Promise) { diff --git a/dev-app/src/components/ListItem.tsx b/dev-app/src/components/ListItem.tsx index 0f6186c6..907b9c8a 100644 --- a/dev-app/src/components/ListItem.tsx +++ b/dev-app/src/components/ListItem.tsx @@ -16,6 +16,7 @@ type Props = { onPress?(): void; color?: string; testID?: string; + visible?: boolean; }; export default function ListItem({ @@ -26,8 +27,9 @@ export default function ListItem({ onPress, testID, disabled, + visible = true, }: Props) { - return ( + return visible ? ( {rightElement && rightElement} - ); + ) : null; } const styles = StyleSheet.create({ diff --git a/dev-app/src/screens/HomeScreen.tsx b/dev-app/src/screens/HomeScreen.tsx index a82cbfa8..6ca6a64d 100644 --- a/dev-app/src/screens/HomeScreen.tsx +++ b/dev-app/src/screens/HomeScreen.tsx @@ -31,48 +31,53 @@ export default function HomeScreen() { const [online, setOnline] = useState(true); const [discoveryMethod, setDiscoveryMethod] = useState('bluetoothScan'); - const { disconnectReader, connectedReader } = useStripeTerminal({ - onDidChangeOfflineStatus(status: OfflineStatus) { - console.log(status); - setOnline(status.sdk.networkStatus === 'online' ? true : false); - }, - onDidForwardingFailure(error) { - console.log('onDidForwardingFailure ' + error?.message); - let toast = Toast.show(error?.message ? error.message : 'unknown error', { - duration: Toast.durations.LONG, - position: Toast.positions.BOTTOM, - shadow: true, - animation: true, - hideOnPress: true, - delay: 0, - }); + const { disconnectReader, connectedReader, rebootReader } = useStripeTerminal( + { + onDidChangeOfflineStatus(status: OfflineStatus) { + console.log(status); + setOnline(status.sdk.networkStatus === 'online' ? true : false); + }, + onDidForwardingFailure(error) { + console.log('onDidForwardingFailure ' + error?.message); + let toast = Toast.show( + error?.message ? error.message : 'unknown error', + { + duration: Toast.durations.LONG, + position: Toast.positions.BOTTOM, + shadow: true, + animation: true, + hideOnPress: true, + delay: 0, + } + ); - setTimeout(function () { - Toast.hide(toast); - }, 3000); - }, - onDidForwardPaymentIntent(paymentIntent, error) { - let toastMsg = - 'Payment Intent ' + - paymentIntent.id + - ' forwarded. ErrorCode' + - error?.code + - '. ErrorMsg = ' + - error?.message; - let toast = Toast.show(toastMsg, { - duration: Toast.durations.LONG, - position: Toast.positions.BOTTOM, - shadow: true, - animation: true, - hideOnPress: true, - delay: 0, - }); + setTimeout(function () { + Toast.hide(toast); + }, 3000); + }, + onDidForwardPaymentIntent(paymentIntent, error) { + let toastMsg = + 'Payment Intent ' + + paymentIntent.id + + ' forwarded. ErrorCode' + + error?.code + + '. ErrorMsg = ' + + error?.message; + let toast = Toast.show(toastMsg, { + duration: Toast.durations.LONG, + position: Toast.positions.BOTTOM, + shadow: true, + animation: true, + hideOnPress: true, + delay: 0, + }); - setTimeout(function () { - Toast.hide(toast); - }, 3000); - }, - }); + setTimeout(function () { + Toast.hide(toast); + }, 3000); + }, + } + ); const batteryPercentage = (connectedReader?.batteryLevel ? connectedReader?.batteryLevel : 0) * 100; const batteryStatus = batteryPercentage @@ -106,6 +111,19 @@ export default function HomeScreen() { await disconnectReader(); }} /> + { + await rebootReader(); + }} + visible={ + discoveryMethod === 'bluetoothScan' || + discoveryMethod === 'bluetoothProximity' || + discoveryMethod === 'usb' + } + /> diff --git a/ios/StripeTerminalReactNative.m b/ios/StripeTerminalReactNative.m index 335a7fb9..516f8c38 100644 --- a/ios/StripeTerminalReactNative.m +++ b/ios/StripeTerminalReactNative.m @@ -43,6 +43,11 @@ @interface RCT_EXTERN_MODULE(StripeTerminalReactNative, RCTEventEmitter) rejecter: (RCTPromiseRejectBlock)reject ) +RCT_EXTERN_METHOD( + rebootReader:(RCTPromiseResolveBlock)resolve + rejecter: (RCTPromiseRejectBlock)reject + ) + RCT_EXTERN_METHOD( createPaymentIntent:(NSDictionary *)params resolver: (RCTPromiseResolveBlock)resolve diff --git a/ios/StripeTerminalReactNative.swift b/ios/StripeTerminalReactNative.swift index bcf03349..3af483dd 100644 --- a/ios/StripeTerminalReactNative.swift +++ b/ios/StripeTerminalReactNative.swift @@ -347,6 +347,18 @@ class StripeTerminalReactNative: RCTEventEmitter, DiscoveryDelegate, BluetoothRe } } + @objc(rebootReader:rejecter:) + func rebootReader(resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) { + Terminal.shared.rebootReader() { error in + if let error = error as NSError? { + resolve(Errors.createError(nsError: error)) + } else { + self.paymentIntents = [:] + resolve([:]) + } + } + } + func terminal(_ terminal: Terminal, didReportUnexpectedReaderDisconnect reader: Reader) { let error = Errors.createError(code: ErrorCode.unexpectedSdkError, message: "Reader has been disconnected unexpectedly") sendEvent(withName: ReactNativeConstants.REPORT_UNEXPECTED_READER_DISCONNECT.rawValue, body: error) diff --git a/src/StripeTerminalSdk.tsx b/src/StripeTerminalSdk.tsx index e0d98c77..52790a2f 100644 --- a/src/StripeTerminalSdk.tsx +++ b/src/StripeTerminalSdk.tsx @@ -7,6 +7,7 @@ import type { CancelDiscoveringResultType, ConnectBluetoothReaderParams, DisconnectReaderResultType, + RebootReaderResultType, Reader, ConnectInternetReaderParams, ConnectUsbReaderParams, @@ -72,6 +73,8 @@ export interface StripeTerminalSdkType { ): Promise; // Disconnect reader disconnectReader(): Promise; + // Reboot reader + rebootReader(): Promise; // Create a payment intent createPaymentIntent( params: CreatePaymentIntentParams diff --git a/src/__tests__/__snapshots__/functions.test.ts.snap b/src/__tests__/__snapshots__/functions.test.ts.snap index 5301dc68..7c2de426 100644 --- a/src/__tests__/__snapshots__/functions.test.ts.snap +++ b/src/__tests__/__snapshots__/functions.test.ts.snap @@ -30,6 +30,7 @@ Object { "getOfflineStatus": [Function], "initialize": [Function], "installAvailableUpdate": [Function], + "rebootReader": [Function], "retrievePaymentIntent": [Function], "retrieveSetupIntent": [Function], "setConnectionToken": [Function], diff --git a/src/__tests__/functions.test.ts b/src/__tests__/functions.test.ts index daf9ee76..a50e8b70 100644 --- a/src/__tests__/functions.test.ts +++ b/src/__tests__/functions.test.ts @@ -59,6 +59,7 @@ describe('functions.test.ts', () => { setConnectionToken: jest.fn(), simulateReaderUpdate: jest.fn(), disconnectReader: jest.fn(), + rebootReader: jest.fn(), clearCachedCredentials: jest.fn(), discoverReaders: jest.fn().mockImplementation(() => ({})), @@ -389,6 +390,7 @@ describe('functions.test.ts', () => { disconnectReader: jest .fn() .mockImplementation(() => ({ error: '_error' })), + rebootReader: jest.fn().mockImplementation(() => ({ error: '_error' })), connectInternetReader: jest .fn() .mockImplementation(() => ({ error: '_error' })), diff --git a/src/functions.ts b/src/functions.ts index 2062adcc..6aa0c18e 100644 --- a/src/functions.ts +++ b/src/functions.ts @@ -9,6 +9,7 @@ import type { ConnectBluetoothReaderParams, CancelDiscoveringResultType, DisconnectReaderResultType, + RebootReaderResultType, ConnectInternetReaderParams, ConnectUsbReaderParams, CreatePaymentIntentParams, @@ -255,6 +256,22 @@ export async function disconnectReader(): Promise { }, 'disconnectReader')(); } +export async function rebootReader(): Promise { + return Logger.traceSdkMethod(async () => { + try { + const { error } = await StripeTerminalSdk.rebootReader(); + + return { + error: error, + }; + } catch (error) { + return { + error: error as any, + }; + } + }, 'rebootReader')(); +} + export async function createPaymentIntent( params: CreatePaymentIntentParams ): Promise { diff --git a/src/hooks/__tests__/__snapshots__/useStripeTerminal.test.tsx.snap b/src/hooks/__tests__/__snapshots__/useStripeTerminal.test.tsx.snap index 3e839356..f8742f75 100644 --- a/src/hooks/__tests__/__snapshots__/useStripeTerminal.test.tsx.snap +++ b/src/hooks/__tests__/__snapshots__/useStripeTerminal.test.tsx.snap @@ -36,6 +36,7 @@ Object { "installAvailableUpdate": [Function], "isInitialized": false, "loading": false, + "rebootReader": [Function], "retrievePaymentIntent": [Function], "retrieveSetupIntent": [Function], "setReaderDisplay": [Function], diff --git a/src/hooks/__tests__/useStripeTerminal.test.tsx b/src/hooks/__tests__/useStripeTerminal.test.tsx index 51791815..25310028 100644 --- a/src/hooks/__tests__/useStripeTerminal.test.tsx +++ b/src/hooks/__tests__/useStripeTerminal.test.tsx @@ -70,7 +70,9 @@ function spyAllFunctions({ returnWith = null }: { returnWith?: any } = {}) { .spyOn(functions, 'disconnectReader') .mockImplementation(disconnectReader); // - + const rebootReader = jest.fn(() => returnWith); + jest.spyOn(functions, 'rebootReader').mockImplementation(rebootReader); + // const installAvailableUpdate = jest.fn(() => returnWith); jest .spyOn(functions, 'installAvailableUpdate') @@ -173,6 +175,7 @@ function spyAllFunctions({ returnWith = null }: { returnWith?: any } = {}) { cancelDiscovering, connectBluetoothReader, disconnectReader, + rebootReader, connectInternetReader, connectUsbReader, createPaymentIntent, @@ -320,6 +323,7 @@ describe('useStripeTerminal.test.tsx', () => { result.current.createPaymentIntent({} as any); result.current.createSetupIntent({} as any); result.current.disconnectReader(); + result.current.rebootReader(); result.current.retrievePaymentIntent(''); result.current.getLocations({} as any); result.current.confirmPaymentIntent({} as any); @@ -371,6 +375,7 @@ describe('useStripeTerminal.test.tsx', () => { await result.current.createPaymentIntent({} as any); await result.current.createSetupIntent({} as any); await result.current.disconnectReader(); + await result.current.rebootReader(); await result.current.retrievePaymentIntent(''); await result.current.getLocations({} as any); await result.current.confirmPaymentIntent({} as any); @@ -467,6 +472,7 @@ describe('useStripeTerminal.test.tsx', () => { await expect(result.current.disconnectReader()).resolves.toEqual( '_value' ); + await expect(result.current.rebootReader()).resolves.toEqual('_value'); await expect( result.current.retrievePaymentIntent({} as any) ).resolves.toEqual('_value'); diff --git a/src/hooks/useStripeTerminal.tsx b/src/hooks/useStripeTerminal.tsx index 8043fa92..6292ca08 100644 --- a/src/hooks/useStripeTerminal.tsx +++ b/src/hooks/useStripeTerminal.tsx @@ -27,6 +27,7 @@ import { cancelDiscovering, connectBluetoothReader, disconnectReader, + rebootReader, connectInternetReader, connectUsbReader, createPaymentIntent, @@ -474,6 +475,20 @@ export function useStripeTerminal(props?: Props) { return response; }, [setLoading, setConnectedReader, setDiscoveredReaders, _isInitialized]); + const _rebootReader = useCallback(async () => { + if (!_isInitialized()) { + console.error(NOT_INITIALIZED_ERROR_MESSAGE); + return; + } + setLoading(true); + + const response = await rebootReader(); + + setLoading(false); + + return response; + }, [setLoading, _isInitialized]); + const _createPaymentIntent = useCallback( async (params: CreatePaymentIntentParams) => { if (!_isInitialized()) { @@ -850,6 +865,7 @@ export function useStripeTerminal(props?: Props) { cancelDiscovering: _cancelDiscovering, connectBluetoothReader: _connectBluetoothReader, disconnectReader: _disconnectReader, + rebootReader: _rebootReader, connectInternetReader: _connectInternetReader, connectUsbReader: _connectUsbReader, createPaymentIntent: _createPaymentIntent, diff --git a/src/types/index.ts b/src/types/index.ts index a1b9e5db..95aeec80 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -114,6 +114,10 @@ export type DisconnectReaderResultType = { error: StripeError; }; +export type RebootReaderResultType = { + error: StripeError; +}; + export type UpdateSoftwareResultType = { update?: Reader.SoftwareUpdate; error?: StripeError;