3DS/SCA setup guidance needed

We’ve recently built a mobile ordering app for a client (a hospitality venue) using the Square SDK and APIs. The app lets customers browse a menu, place food and drink orders, and pay in-app - using the In-App Payments SDK using the Flutter plugin.

During a test purchase, the 3D Secure (SCA) challenge triggered authentication and opened in a browser - the user was asked to approve the transaction in their banking app. After approving, the flow attempted to redirect the browser back to complete checkout, but landed on requestor.netcetera.com, which returns ERR_NAME_NOT_RESOLVED (“This site can’t be reached”). The user was left stranded in the browser with no way back into our app to complete the transaction apart from navigating back to the app manually.

We’ve struggled to find complete documentation covering the 3DS flow and if there is something we should have setup on how to handle these edge cases (such as a web view within the app) or if there is something within the SDK config to manage where a user is redirected to.

We’re working to a tight launch deadline and this is currently blocking card payments in the app, so any documentation, examples, or guidance you can point us to would be hugely appreciated.

@rs97 - Thanks for the detailed write-up. The In-App Payments SDK is designed to manage the entire 3DS challenge flow internally and return a verification_token back to your app. The fact that the challenge is breaking out into an external browser suggests the buyer verification flow isn’t being invoked correctly in your Flutter integration. You have two options:

  1. Call startCardEntryFlowWithBuyerVerification and the SDK handles card entry and 3DS in a single flow.
  2. Collect the card with startCardEntryFlow, then pass the resulting nonce into startBuyerVerificationFlow to run 3DS. This gives you more control between card entry and verification.

Both methods return a nonce and a verification token, which your backend sends to CreatePayment together.

If you’re currently calling startCardEntryFlow alone (without any buyer verification step), that’s the root cause — the nonce is created but no 3DS authentication is attached, so the issuer’s ACS challenge has nowhere to render within your app and falls through to the browser.

@jseok Thanks for the reply. We’ve checked, and we’re already using the first option you described. All three places the app takes a card payment call startCardEntryFlowWithBuyerVerification. We never call startCardEntryFlow on its own. For example:

await InAppPayments.startCardEntryFlowWithBuyerVerification(
onBuyerVerificationSuccess: …,
onBuyerVerificationFailure: …,
onCardEntryCancel: …,
buyerAction: ‘Charge’,
money: money, // amount in pence, GBP
squareLocationId: locationId,
contact: contact, // given name, family name, email, phone, country GB
collectPostalCode: false,
);
The 3DS challenge itself works. The customer is sent to their banking app and approves the payment. The problem is the redirect afterwards: Chrome opens at https://requestor.netcetera.com/?… and shows ERR_NAME_NOT_RESOLVED (screenshot attached).
That hostname doesn’t exist. A DNS lookup returns NXDOMAIN, while other netcetera.com hosts resolve normally. It matches the example threeDSRequestorAppURL in Netcetera’s 3DS SDK documentation (https://requestor.netcetera.com?transID=``...), which integrators are expected to replace with their own app link.
We inspected the Android In-App Payments SDK (com.squareup.sdk.in-app-payments:buyer-verification-internals:1.6.8, the default version pulled in by the Flutter plugin 1.7.13). The class sqip.internal.verification.vendor.netcetera.NetceteraThreeDS2ServiceAdapter contains the hardcoded string https://requestor.netcetera.com?transID= and passes it to setThreeDSRequestorAppURL. The same hardcoded value is also present in 1.6.9, the latest release, so upgrading doesn’t resolve it. As far as we can tell, the SDK is sending Netcetera’s documentation example as the return URL, and there’s no way for us to override it.
Could you confirm, and let us know whether a fix is planned? If there’s a setting on our end that controls where the customer is returned after approving in their banking app, we’d be glad to know about it.
We’re close to launch and this is blocking card payments, so any help is much appreciated.

It looks like THREEDS_URL_PREFIX = "https://requestor.netcetera.com?transID=" is hardcoded in NetceteraThreeDS2ServiceAdapter and passed to threeDSRequestorAppURL. It’s in both 1.6.8 and 1.6.9, you can’t override it, and that host doesn’t resolve. It only breaks the out-of-band flow, which is why in-SDK challenges work.

I’m raising it with the SDK team and will follow up with more detail soon!

Three things that would help:

  1. Is this Android-only for you, or do you see it on iOS too? The iOS SDK doesn’t have this hardcoded value, so iOS may work.
  2. Your application ID, plus a payment or order ID from a failed attempt.
  3. Does the payment still complete? When the buyer approves and lands on the dead page, does it show in your Square dashboard? Worth checking before launch, if authorisations are landing while the app hangs, buyers could be charged for orders with no confirmation.

Although not ideal if you are in a time crunch before we can resolve this, our Web Payments SDK in a webview handles 3DS inside tokenize and doesn’t depend on this fix.

We found the issue on our side! IAP SDK 1.7.0 with the fix has been released.

@ashley-square thanks for looking into this and coming back so swiftly! We’ll take a look at SDK 1.7.0 and confirm this resolves the issue.