stripe / stripe-terminal-android

Stripe Terminal Android SDK
https://stripe.dev/stripe-terminal-android/
Other
93 stars 45 forks source link

tap-to-pay not able to load contactless payment UI #297

Closed L-is-0 closed 1 year ago

L-is-0 commented 1 year ago

Summary

Not able to load the contactless payment UI from point to point until an app restart

Code to reproduce

I have multiple devices (exactly the same ones from hardware perspective), some of the devices are having issues to connect to the mobile reader from time to time, however after an app restart this issue can be fixed, however the connection is not stable and can be dropped at some other point at the day, so we have to keep doing manually app restart.

Android version

Android 11

Impacted devices (Android devices or readers)

mobile reader

SDK version

2.16.0-b1

Logs from the device

Thu Mar 16 14:12:12 GMT 2023 StripeUtil-2901 stripeTerminalCollectPayment Thu Mar 16 14:12:12 GMT 2023 ContactlessPaymentUtil-2901 startPayment() - reconnect TerminalReaderService... Thu Mar 16 14:12:12 GMT 2023 TokenProvider-2901 fetchConnectionToken... Thu Mar 16 14:12:12 GMT 2023 ApiEndpoints-2901 live aws url Thu Mar 16 14:12:12 GMT 2023 StripeUseCase-2901 start payment at: 2023-03-16 14:12:12 Thu Mar 16 14:12:12 GMT 2023 AwsApiManager-2901 New request: URL: stripe/getConnectionToken Type: GET Async: false Auth: true JSON: null Thu Mar 16 14:12:14 GMT 2023 apiEndpoint-2901 onSuccess: Response{protocol=h2, code=200, message=, url= xxx/stripe/getConnectionToken} Thu Mar 16 14:12:14 GMT 2023 TokenProvider-2901 fetchConnectionToken successful Thu Mar 16 14:12:14 GMT 2023 MainApplication-2901 new connection token created Thu Mar 16 14:12:14 GMT 2023 TerminalReaderService-2901 connect() - discoveryCancelable cancel on failure Operation completed before it could be canceled Thu Mar 16 14:12:14 GMT 2023 TerminalReaderService-2901 disconnect success Thu Mar 16 14:12:14 GMT 2023 TerminalReaderService-2901 clear cached credentials Thu Mar 16 14:12:14 GMT 2023 TerminalReaderService-2901 Finished discovering readers Thu Mar 16 14:12:14 GMT 2023 ConnectionStatusChange-2901 CONNECTING Thu Mar 16 14:12:27 GMT 2023 ContactlessPaymentUtil-2901 startPayment() - reconnect TerminalReaderService... Thu Mar 16 14:12:27 GMT 2023 TokenProvider-2901 fetchConnectionToken... Thu Mar 16 14:12:27 GMT 2023 ApiEndpoints-2901 live aws url Thu Mar 16 14:12:27 GMT 2023 AwsApiManager-2901 New request: URL: https://api-v5.tabletopgroup.co.uk/ttintegrations/stripe/getConnectionToken Type: GET Async: false Auth: true JSON: null Thu Mar 16 14:12:27 GMT 2023 apiEndpoint-2901 onSuccess: Response{protocol=h2, code=200, message=, url=xxx/stripe/getConnectionToken} Thu Mar 16 14:12:27 GMT 2023 MainApplication-2901 new connection token created Thu Mar 16 14:12:27 GMT 2023 TokenProvider-2901 fetchConnectionToken successful Thu Mar 16 14:12:44 GMT 2023 AwsApiManager-2901 Response{protocol=h2, code=400, message=, url=xx/getErr}, url = xxx/getErr, error is {"error":{"name":"InternalError","date":"2023-03-16T14:12:41.924Z","statusCode":400,"code":"FAILED_LOAD","usererrmsg":"Error loading contactless payment","details":"Redirecting to other method","log":"Error loading contactless payment.","integration":"stripe"}

Links to a closed issue below

https://github.com/stripe/stripe-terminal-android/issues/113#issuecomment-1410803533

rv-stripe commented 1 year ago

Can you clarify a little about what the issue you're seeing is? Is the UI slow to respond or is it not showing at all? Are you seeing any errors in the logs that may help us look into the issue? The error I see in the logs you provided appear to be from outside of our SDK.

L-is-0 commented 1 year ago

Hi @rv-stripe, thanks for your response, apologise for getting back to this late.

  1. Can you clarify a little about what the issue you're seeing is?
    • We are loading the contactless payment UI in our Android app when user selected to pay via contactless, the flow in the app is: trying to connect to tap to pay simulated reader if it's not connected -> generate payment intent from our backend server -> call retrievePaymentIntent -> collectPaymentMethod -> processPayment.

the issue we are having seems to be related to the app not able to get connected with the reader via calling discoverReaders() - it was a bit tricky to replicate it but when i had this issue i would see "timeout waiting for token connection" with the fact that our backend did generate the token successfully.

  1. Is the UI slow to respond or is it not showing at all? The UI is not showing at all, and the app is ended with a timeout from Stripe server.

  2. Are you seeing any errors in the logs that may help us look into the issue? I can see there is a "ConnectionStatusChange-2901 CONNECTING" log from the SDK and when it is timeout it shows something like "timeout waiting on connection token", but if you look at the original logs i uploaded for this issue. The token has been successfully created by our endpoint.

Please feel free to let me know if any further info is needed.

maggiewhite-stripe commented 1 year ago

@L-is-0 "ConnectionStatusChange-2901 CONNECTING" is still a log from your application's logs although it's likely logging a TerminalListener.onConnectionStatusChange() callback.

Are there any logcat logs that you can share from the Terminal SDK? They look something like this:

2023-03-31 15:57:37.655 14336-14387 StripeTerminal          <app_name>          D  class=ProxyOfflineListener message=onNetworkStatusChange
2023-03-31 15:57:37.656 14336-14409 StripeTerminal          <app_name>          D  class=DefaultOfflineTraceManager message="Start forwarding" isAlreadyForwarding=true
2023-03-31 15:58:08.292 14336-14336 StripeTerminal          <app_name>          W  class=TerminalLifecycleObserver message=applicationDidReceiveMemoryWarning memoryLevel=20 memoryProfile=TRIM_MEMORY_UI_HIDDEN appId=<app_name>

@L-is-0

L-is-0 commented 1 year ago

Hi @maggiewhite-stripe thank you for getting back to me. I have been trying to get the exact logs for this issue mentioned above but it's a bit tricky to replicate, yes the error I was seeing is "NETWORK_ERROR.CONNECTION_TOKEN_PROVIDER_ERROR: waiting for connection token".

maggiewhite-stripe commented 1 year ago

@L-is-0 I see the instance of the NETWORK_ERROR.CONNECTION_TOKEN_PROVIDER_ERROR in your app from the error you reported in the original post.

Here are our server-side logs from that error: client_time client_type class msg
Mar/16/2023 14:12:14.124 ANDROID_SDK ProxyAdapter connectReader reader=null ... connectionConfigurationType=LocalMobileConnectionConfiguration
Mar/16/2023 14:12:14.125 ANDROID_SDK OnlineDirectResourceRepository activateReader  
Mar/16/2023 14:12:14.125 ANDROID_SDK CotsAdapter connectReader reader=null ... connectionConfigurationType=LocalMobileConnectionConfiguration  
Mar/16/2023 14:12:14.125 ANDROID_SDK TerminalStatusManager TerminalStatusManager connecting.  
Mar/16/2023 14:12:14.125 ANDROID_SDK ProxyTerminalListener onConnectionStatusChange(CONNECTING)  
Mar/16/2023 14:12:27.075 ANDROID_SDK Terminal clearCachedCredentials  
Mar/16/2023 14:12:27.776 ANDROID_SDK Terminal disconnectReader  
Mar/16/2023 14:12:42.078 ANDROID_SDK Terminal clearCachedCredentials  
Mar/16/2023 14:12:42.500 ANDROID_SDK Terminal disconnectReader  
Mar/16/2023 14:13:14.127 ANDROID_SDK CotsAdapter disconnectReader  
Mar/16/2023 14:13:14.131 ANDROID_SDK TerminalStatusManager TerminalStatusManager notConnected.  
Mar/16/2023 14:13:14.131 ANDROID_SDK ProxyTerminalListener onConnectionStatusChange(NOT_CONNECTED)

And then this error occurs at Mar/16/2023 14:13:14.133:

com.stripe.stripeterminal.external.models.TerminalException: Timed out waiting for connection token at com.stripe.stripeterminal.internal.common.tokenrepositories.ConnectionTokenRepository.waitForToken(ConnectionTokenRepository.kt:145)
at com.stripe.stripeterminal.internal.common.tokenrepositories.ConnectionTokenRepository.getToken(ConnectionTokenRepository.kt:77) |

You can see that Terminal.disconnectReader is executing at 14:12:27.776, right after the connectReader call completes. That disconnectReader call from your integration code would get queued behind the connectReader call and probably ran right after fetchConnectionToken successful log.

Is your user trying to cancel the reader connection while the reader connection is in progress? If yes, you should return an error from fetching the connection token via ConnectionTokenCallback.onFailure in your ConnectionTokenProvider.fetchConnectionToken implementation: https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.callable/-connection-token-callback/on-failure.html

L-is-0 commented 1 year ago

Hi @maggiewhite-stripe, thanks for providing more information regarding this issue. The logic we have currently is to try connecting to the reader for 3 attempts if it's not connected before starting payments, as some time it would get a failure from the first connection attempt due to poor connectivities. We manually added a 15s delay between each attempt, but we did call disconnectReader before starting a new connection to avoid a connection already taken in place error from the SDK.

Could you please kindly advise on how long it would take for each connection to get completed? Maybe we should increase the delay to 20s for our case to give it enough time to finish reader connection before it has the chance to cancel and start another one? Many thanks.

maggiewhite-stripe commented 1 year ago

@L-is-0 The timeout in the SDK is 60 seconds. If you want to control the timeout for the connectReader request instead, you can do it via the method I described in my response above:

you should return an error from fetching the connection token via ConnectionTokenCallback.onFailure in your ConnectionTokenProvider.fetchConnectionToken implementation: https://stripe.dev/stripe-terminal-android/external/com.stripe.stripeterminal.external.callable/-connection-token-callback/on-failure.html

The easiest solution would be to let the SDK handle the timeout and to retry if the SDK returns a timeout error when attempting to connect the reader.

L-is-0 commented 1 year ago

Thank you @maggiewhite-stripe, the information you provided is very helpful. However a 60s timeout is not ideal for our case as we don't want user to wait more than 20s to start the payment under any circumstances. I understand that the ConnectionTokenCallback.onFailure should only be called when there is a timeout from getting a connection token from our backend server. Could you please clarify how this is related to the connectReader timeout? Thanks in advance for your help.

maggiewhite-stripe commented 1 year ago

@L-is-0 This is incorrect:

ConnectionTokenCallback.onFailure should only be called when there is a timeout from getting a connection token from our backend server

Sorry for the misunderstanding. I am saying the following:

L-is-0 commented 1 year ago

@maggiewhite-stripe Thank you very much, we added the fixes as you suggested, now monitoring how it works.

L-is-0 commented 1 year ago

Hi @maggiewhite-stripe @rv-stripe , sorry to re-post again, but we are getting this issue again in the latest Stripe SDK v2.21.1, but for this time we will don't call /disconnectReader or /clearCachedCredentials after CONNECTING but still get NOT_CONNECTED for each attempt until we restart our app and so the SDK can be re-init. Could you please kindly help us look into the problem so we can resolve it smoothly?

martinzuro commented 9 months ago

Same issue here