Mozilla/5.0 (iPad; CPU OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPad7,2;FBMD/iPad;FBSN/iOS;FBSV/13.7;FBSS/2;FBID/tablet;FBLC/en_US;FBOP/5]
Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBAV/293.0.0.45.117;FBBV/252184617;FBDV/iPhone9,4;FBMD/iPhone;FBSN/iOS;FBSV/13.7;FBSS/3;FBID/phone;FBLC/en_US;FBOP/5;FBRV/253288080]
Mozilla/5.0 (Linux; Android 9; LM-Q720) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.110 Mobile Safari/537.36
Note: We do most advertising on Facebook, and the Facebook browser normally works fine so that's not the issue.
We are getting quite a few emails from customers saying they couldn't enter their credit cards. These are turning out to be the dreaded HOSTED_FIELDS_TIMEOUT error. I have tried both ways of client creation (standalone and internally to hosted fields) and don't see any difference. I cannot reproduce on my machine.
I expect the failure reasons would be in these sort of categories:
Based on my detailed logging the errors are not occurring after the user has navigated away - since I don't log in that case.
I have just about exhausted the debugging I am able to do myself and our customers are external so I cannot reproduce. It seems that looking at data for the past month we sometimes are getting 5-10% of customers with timeouts.
I've also seen cases where the customer has tried refreshing the page themselves (so they definitely have connectivity) and tried 2-5 times they're still getting the hosted fields error each time.
What I really need is some combination of the following:
A way to send a correlation ID (UID) when I create hosted fields that I could submit to support to look for analytics events. I see there's an existing 'sessionId' but I don't seem to be able to access that.
A way to set shorter timeouts for hosted fields. Expecting a user to wait 60 seconds is crazy. Of course this doesn't help my problem unless the rejected promise has useful information.
Logging of internal events for client and hosted field setup that I could access any time after attempting to create an instance of the iframe. I think this is the only way I could truly find the problem.
For example this is the sort of thing I would like to see in a log. The idea being you would be able to see where it got stuck:
{
log: [
{
date: 'Thu Oct 29 2020 11:34:02 GMT-0700 (Pacific Daylight Time)',
desc: 'Create iframe'
},
{
date: 'Thu Oct 29 2020 11:34:03 GMT-0700 (Pacific Daylight Time)',
desc: 'Iframe created OK'
},
{
date: 'Thu Oct 29 2020 11:34:04 GMT-0700 (Pacific Daylight Time)',
desc: 'Graph QL Sent',
data: {
sessionID: '3649b9fb-9936-4adf-934e-555cb79ab537',
operation: 'ClientConfiguration'
}
},
{
date: 'Thu Oct 29 2020 11:34:06 GMT-0700 (Pacific Daylight Time)',
desc: 'Graph QL Received',
data: {
requestID: 'trOsRnXvk97FTdTaQ9q734dYoBEHbH0SwBrFQojtZsp8iVUj3_TMvg',
operation: 'ClientConfiguration'
}
}]
}
Having access to this would make it far easier to diagnose. Right now it's almost impossible. Let's say if hosted fields is still not set up after 15 seconds I could call hostedFields.log and be able to access the log (even before the timeout). I don't want to wait a whole minute, in which time the user may have already given up.
I was also wondering if would help for me to call the 'ping' service on your graphql to confirm connectivity is available? I just don't know who would be blocking your domain unless it's a really strict government or internal network.
Yes I did submit a support request but I only asked for our analytics to be looked at - perhaps you are already recording something that could help find the root cause. And yes I've seen other similar issues, but those seem to have been resolved.
:wave:
A way to send a correlation ID (UID) when I create hosted fields that I could submit to support to look for analytics events. I see there's an existing 'sessionId' but I don't seem to be able to access that.
So the session id you mentioned can't be correlated in our logs, but one that can be is the analytics session id found in the client configuration.
clientInstance.getConfiguration().analyticsMetadata.sessionId
Or from the hosted fields instance (though I wouldn't put any business logic around this staying the same, as it's not part of the public api and could be changed in a future version):
hostedFields._clientPromise.then((client) => {
console.log(client.getConfiguration().analyticsMetadata.sessionId);
});
A way to set shorter timeouts for hosted fields. Expecting a user to wait 60 seconds is crazy. Of course this doesn't help my problem unless the rejected promise has useful information.
I'll bring this up with my team. Ideally, this wouldn't be a requirement, as the fields should set up well before that happens. So it'd be more ideal to figure out _why_ the fields are failing to be set up.
Logging of internal events for client and hosted field setup that I could access any time after attempting to create an instance of the iframe. I think this is the only way I could truly find the problem.
One idea for the short term would be to pull down the un-minified file for hosted fields and add in your own logging and then minify the files. You won't be able to add logging to the iframes themselves, of course, but that should start to give you some insight on your own without waiting on us to release a new sdk version.
I was also wondering if would help for me to call the 'ping' service on your graphql to confirm connectivity is available? I just don't know who would be blocking your domain unless it's a really strict government or internal network.
This seems unlikely to resolve your problem, since you said you tried both passing in a client param and skipping the client and passing authorization instead. When passing in the client, you'd need to _have_ the configuration from the GraphQL API before setting up the hosted fields. When passing authorization, you bypass talking to the GraphQL APIs until you actually try to tokenize.
That makes me think that something is blocking the iframes themselves from loading, not requests to the API.
@crookedneighbor thanks for your detailed response. I'm currently in the process of moving apartments so excuse any slowness in replying. Here's some extra info and what I've tried today:
1) I did try adding a ping test to confirm the user could connect to your server. If the customer gets a HOSTED_FIELDS_TIMEOUT then I record the ping time (provided it completed). Good news is it's succeeding every time and returning in about 50-200ms. As you said maybe that doesn't help in finding the problem, but it's good way to eliminate a lost internet connection.
2) I misunderstood the timeout procedure and thought the HOSTED_FIELDS_TIMEOUT could also be caused by a client not initializing correctly. On further inspection I see it can only happen when events.FRAME_READY doesn't get fired.
With that in mind I've now reverted back to creating the client and then the hosted fields afterwards. This allows me to get the analytics sessionId and I can provide that to you or support if you think it will help in debugging. I'll start tracking that in our next update tomorrow and report back.
Another couple things that may be of note:
1) After I get the HOSTED_FIELDS_TIMEOUT error I check the DOM contents of my host element to see what was or wasn't created. It does contain an INPUT element with focus-intercept class. It doesn't contain the IFRAME. This sort of confirms the obvious, but I mention it as confirmation that your API is loaded, running and that the UI hasn't already been destroyed.
2) My implementation uses Angular Material and the form-field control. The actual container where the iframe is inserted may have opacity: 0 or max-height: 0 in some cases.
This raised my suspicions as a possible cause, but I'm reluctant to undo the way everything works since I don't see why this would cause the problem. If it rings a bell I can certainly try this sooner rather than later.
After I get the HOSTED_FIELDS_TIMEOUT error I check the DOM contents of my host element to see what was or wasn't created. It does contain an INPUT element with focus-intercept class. It doesn't contain the IFRAME. This sort of confirms the obvious, but I mention it as confirmation that your API is loaded, running and that the UI hasn't already been destroyed.
This is curious to me that the iframe is not present. Not sure how the document fragment is not able to be injected: https://github.com/braintree/braintree-web/blob/c2a227f5ebf39d7c6213c13ca6002d7fcd6d58eb/src/hosted-fields/external/inject-frame.js#L16-L20
I would expect if the document fragment containing the iframe could not be injected, that a DOM Exception would have been thrown.
@crookedneighbor I'm now in my new apartment which went smoothly apart from the part when my couch wouldn't fit through the door and knocked and set off the sprinklers during a pending fire marshal inspection.
I've now added a little more debugging and have not recently seen the case where there was no iframe generated.
Most commonly this is what I see in the DOM 1ms after the HOSTED_FIELDS_TIMEOUT.
INPUT.focus-intercept, IFRAME, INPUT.focus-intercept, DIV
With my new update I can now retrieve the session IDS (I switched to creating a client first). Here are 3 session IDs all from the same user who made three attempts reloading the page over a timespan of around 6 minutes. The ping time to reach your API was sub 100ms.
8676d7b1-6cab-4e00-ae2c-e9d3c4f1ad6e
d03a6f16-bd3d-43ed-81da-9557d02e8b13
Their user agent was
Mozilla/5.0 (iPhone; CPU Phone OS 14_1 like Mac OS X) WebKit/8610 (KHTML, like Gecko) Mobile/18A8395 [FBAN/FBIOS;FBDV/iPhone10,6;FBMD/iPhone;FBSN/iOS;FBSV/14.1;FBSS/3;FBID/phone;FBLC/en_US;FBOP/5]
Does this reveal anything your end or do you have any other ideas what I could do at this point (such as communicating with the iframe to try to get a status)? I'm guessing my options are limited for what I can do myself because the iframe is not hosted on my domain.
That looks to be the in-app browser for Facebook on iOS 14.1 on an iPhone X
There is a chance that Facebook has done something to their JS environment in their webview/safari view controller that is preventing the iframes from communicating, but it _should_ just work.
@crookedneighbor we get nearly all our sales from Facebook so it definitely does nearly always work.
I'm going to do some analysis of the failures / success rate based on useragent, but right now it isn't a separate column in our database so may take a few days to collect enough data.
I guess temporarily I could detect the failure and ask the user to open the site in Safari. I've also emailed a couple customers to ask them to try Safari directly.
Is it feasible to add something to the iframe that could give a status? Do those analytics ids give you anything or should I submit them to support?
I've had about 4 days with no timeouts and I didn't change anything notable in my code. Before I was getting half a dozen a day that affected orders - (so likely even more failures that weren't caught).
I did switch to braintree-web 3.69 (I saw something about FrameBus 5 and thought it may have an impact) but I wasn't seeing timeouts on 3.68 either.
Hoping you guys found and fixed something your end?
I'll keep an eye on this and I'm also keeping stats on every hosted fields setup call - as opposed to just the ones with an attempt to place an order.
Nothing intentional. I wouldn't expect the update to Framebus v5 to resolve anything regarding this.
@simeyla any updates on your end? Have you continued to see no timeouts?
@crookedneighbor thanks for checking in. I saw one timeout in the last 200 sessions (I am logging every setup call now).
This time the user agent wasn't even inside Facebook
Mozilla/5.0 (Linux; Android 10; SM-G965U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36
And the analytics ID was 948ea89e-d603-4842-9c41-0714586fe44a
The number is dramatically lower so whether conscious or not somebody somewhere made a change somewhere!
I'm going to close this issue then, but feel free to comment again if there's more info for us to investigate.