Skip to content

Commit

Permalink
fix connection issue (#791)
Browse files Browse the repository at this point in the history
* fix connection issue on the first running.

* fix lint and android unit-test issues.

* optimize the codes.

* optimize the codes.

* optimize codes

* fix android unit-test issue
ianlin-bbpos authored and nazli-stripe committed Sep 11, 2024

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent 7b9dec2 commit c56ac60
Showing 8 changed files with 84 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -180,7 +180,8 @@ class StripeTerminalReactNativeModule(reactContext: ReactApplicationContext) :
fun setConnectionToken(params: ReadableMap, promise: Promise) {
tokenProvider.setConnectionToken(
token = params.getString("token"),
error = params.getString("error")
error = params.getString("error"),
callbackId = params.getString("callbackId")
)
promise.resolve(null)
}
Original file line number Diff line number Diff line change
@@ -6,29 +6,40 @@ import com.stripe.stripeterminal.external.callable.ConnectionTokenProvider
import com.stripe.stripeterminal.external.models.ConnectionTokenException
import com.stripeterminalreactnative.ReactExtensions.sendEvent
import com.stripeterminalreactnative.ReactNativeConstants.FETCH_TOKEN_PROVIDER
import java.util.Hashtable
import java.util.UUID

class TokenProvider(private val context: ReactApplicationContext) : ConnectionTokenProvider {
private var connectionTokenCallback: ConnectionTokenCallback? = null
var callbackMap: Hashtable<String, ConnectionTokenCallback> = Hashtable()

fun setConnectionToken(token: String?, error: String?) {
try {
if (!token.isNullOrEmpty()) {
connectionTokenCallback?.onSuccess(token)
connectionTokenCallback = null
} else {
connectionTokenCallback?.onFailure(
ConnectionTokenException(error ?: "", null)
)
}
} catch (e: Throwable) {
connectionTokenCallback?.onFailure(
ConnectionTokenException("Failed to fetch connection token", e)
fun setConnectionToken(token: String?, error: String?, callbackId: String?) {
if (callbackId.isNullOrEmpty() || callbackMap[callbackId] == null) {
throw ConnectionTokenException(
"setConnectionToken requires the callbackId to be set to the callbackId value provided to the tokenProviderHandler."
)
}

val connectionTokenCallback = callbackMap[callbackId]
if (connectionTokenCallback != null) {
try {
if (!token.isNullOrEmpty()) {
connectionTokenCallback.onSuccess(token)
} else {
connectionTokenCallback.onFailure(ConnectionTokenException(error ?: "", null))
}
} catch (e: Throwable) {
connectionTokenCallback.onFailure(ConnectionTokenException("Failed to fetch connection token", e))
} finally {
callbackMap.remove(callbackId)
}
}
}

override fun fetchConnectionToken(callback: ConnectionTokenCallback) {
connectionTokenCallback = callback
context.sendEvent(FETCH_TOKEN_PROVIDER.listenerName, null)
val uuid = UUID.randomUUID().toString()
callbackMap[uuid] = callback
context.sendEvent(FETCH_TOKEN_PROVIDER.listenerName) {
putString("callbackId", uuid)
}
}
}
Original file line number Diff line number Diff line change
@@ -2,14 +2,18 @@ package com.stripeterminalreactnative

import com.facebook.react.bridge.ReactApplicationContext
import com.stripe.stripeterminal.external.callable.ConnectionTokenCallback
import com.stripe.stripeterminal.external.models.ConnectionTokenException
import com.stripeterminalreactnative.ReactExtensions.sendEvent
import com.stripeterminalreactnative.ReactNativeConstants.FETCH_TOKEN_PROVIDER
import io.mockk.Called
import io.mockk.mockk
import io.mockk.verify
import org.junit.ClassRule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue

@RunWith(JUnit4::class)
class TokenProviderTest {
@@ -31,10 +35,15 @@ class TokenProviderTest {
val tokenProvider = TokenProvider(context)
tokenProvider.fetchConnectionToken(callback)

verify(exactly = 1) { context.sendEvent(any()) }
assertTrue { tokenProvider.callbackMap.keys.count() == 1 }

verify(exactly = 1) {
context.sendEvent(FETCH_TOKEN_PROVIDER.listenerName, any())
}
verify { callback wasNot Called }

tokenProvider.setConnectionToken(TOKEN, ERROR)
tokenProvider.setConnectionToken(TOKEN, ERROR, tokenProvider.callbackMap.keys.first())
assertTrue { tokenProvider.callbackMap.keys.count() == 0 }

verify(exactly = 1) { callback.onSuccess(TOKEN) }
verify(exactly = 0) { callback.onFailure(any()) }
@@ -45,17 +54,30 @@ class TokenProviderTest {
val tokenProvider = TokenProvider(context)
tokenProvider.fetchConnectionToken(callback)

verify(exactly = 1) { context.sendEvent(any()) }
assertTrue { tokenProvider.callbackMap.keys.count() == 1 }
verify(exactly = 1) { context.sendEvent(FETCH_TOKEN_PROVIDER.listenerName, any()) }
verify { callback wasNot Called }

tokenProvider.setConnectionToken(null, ERROR)
tokenProvider.setConnectionToken(null, ERROR, tokenProvider.callbackMap.keys.first())

verify(exactly = 0) { callback.onSuccess(any()) }
verify(exactly = 1) { callback.onFailure(any()) }

tokenProvider.setConnectionToken(null, null)
tokenProvider.fetchConnectionToken(callback)

assertTrue { tokenProvider.callbackMap.keys.count() == 1 }
verify(exactly = 2) { context.sendEvent(FETCH_TOKEN_PROVIDER.listenerName, any()) }
tokenProvider.setConnectionToken(null, null, tokenProvider.callbackMap.keys.first())

verify(exactly = 0) { callback.onSuccess(any()) }
verify(exactly = 2) { callback.onFailure(any()) }

tokenProvider.fetchConnectionToken(callback)

assertTrue { tokenProvider.callbackMap.keys.count() == 1 }
verify(exactly = 3) { context.sendEvent(FETCH_TOKEN_PROVIDER.listenerName, any()) }

assertFailsWith<ConnectionTokenException> {
tokenProvider.setConnectionToken(null, null, null)
}
}
}
3 changes: 2 additions & 1 deletion ios/StripeTerminalReactNative.swift
Original file line number Diff line number Diff line change
@@ -164,8 +164,9 @@ class StripeTerminalReactNative: RCTEventEmitter, DiscoveryDelegate, BluetoothRe
func setConnectionToken(params: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void {
let token = params["token"] as? String
let error = params["error"] as? String
let callbackId = params["callbackId"] as? String

TokenProvider.shared.setConnectionToken(token: token, error: error)
TokenProvider.shared.setConnectionToken(token: token, error: error, callbackId: callbackId)
resolve([:])
}

21 changes: 14 additions & 7 deletions ios/TokenProvider.swift
Original file line number Diff line number Diff line change
@@ -6,21 +6,28 @@ enum TokenError: Error {

class TokenProvider: ConnectionTokenProvider {
static let shared = TokenProvider()
var completionCallback: ConnectionTokenCompletionBlock? = nil

var callbackMap: [String : ConnectionTokenCompletionBlock?] = [:]
static var delegate: RCTEventEmitter? = nil

func setConnectionToken(token: String?, error: String?) {
func setConnectionToken(token: String?, error: String?, callbackId: String?) {
guard let callbackId, let completionCallback = self.callbackMap[callbackId] else {
print("⚠️ setConnectionToken requires the callbackId be set to the callbackId value provided to the tokenProviderHandler.")
return
}

let unwrappedToken = token ?? ""
if (!unwrappedToken.isEmpty) {
self.completionCallback?(token, nil)
completionCallback?(unwrappedToken, nil)
} else {
self.completionCallback?(nil, TokenError.runtimeError(error ?? "") )
completionCallback?(nil, TokenError.runtimeError(error ?? ""))
}

callbackMap.removeValue(forKey: callbackId)
}

func fetchConnectionToken(_ completion: @escaping ConnectionTokenCompletionBlock) {
self.completionCallback = completion
TokenProvider.delegate?.sendEvent(withName: ReactNativeConstants.FETCH_TOKEN_PROVIDER.rawValue, body: [:])
let uuid = UUID().uuidString
self.callbackMap[uuid] = completion
TokenProvider.delegate?.sendEvent(withName: ReactNativeConstants.FETCH_TOKEN_PROVIDER.rawValue, body: ["callbackId": uuid])
}
}
10 changes: 7 additions & 3 deletions src/components/StripeTerminalProvider.tsx
Original file line number Diff line number Diff line change
@@ -299,13 +299,17 @@ export function StripeTerminalProvider({
useListener(REPORT_LOW_BATTERY_WARNING, didReportLowBatteryWarning);
useListener(REPORT_READER_EVENT, didReportReaderEvent);

const tokenProviderHandler = async () => {
const tokenProviderHandler = async ({
callbackId,
}: {
callbackId: string;
}) => {
try {
const connectionToken = await tokenProvider();

setConnectionToken(connectionToken);
setConnectionToken(connectionToken, undefined, callbackId);
} catch (error) {
setConnectionToken(undefined, TOKEN_PROVIDER_ERROR_MESSAGE);
setConnectionToken(undefined, TOKEN_PROVIDER_ERROR_MESSAGE, callbackId);

console.error(error);
console.error(TOKEN_PROVIDER_ERROR_MESSAGE);
5 changes: 3 additions & 2 deletions src/functions.ts
Original file line number Diff line number Diff line change
@@ -69,10 +69,11 @@ export async function initialize(

export async function setConnectionToken(
token?: string,
error?: string
error?: string,
callbackId?: string
): Promise<void> {
try {
await StripeTerminalSdk.setConnectionToken({ token, error });
await StripeTerminalSdk.setConnectionToken({ token, error, callbackId });
} catch (e) {
console.warn('Unexpected error:', e);
}
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ export type InitParams = {
export type SetConnectionTokenParams = {
token?: string;
error?: string;
callbackId?: string;
};

export type LogLevel = LogLevelIOS | LogLevelAndroid;

0 comments on commit c56ac60

Please sign in to comment.