iOS SDK 2.6.0: ready Tap to Pay reader but empty checkout methods, persists after reauthorization

Hello Square Developer Support,

We are investigating an intermittent Tap to Pay failure in our native iOS app. Successful $1 Tap to Pay transactions have previously completed on this test iPhone, but we can now reproduce the following:

  1. Authorize Square with a production seller access token and location.
  2. Check the merchant’s Apple link status; the account is linked. The SDK reports the iPhone Tap to Pay reader ready and contactless input available.
  3. Start an onlineOnly USD 100-cent payment with a new UUID paymentAttemptID, default prompt mode and AdditionalPaymentMethods([.tapToPay]).
  4. Square’s UI displays “Connect hardware to take card payments”. The active PaymentHandle.additionalPaymentMethods is empty.

Diagnostic values before and throughout the failing checkout:

  • SDK version 2.6.0
  • isDeviceCapable=true
  • requested additional-method mask=2; tapToPay.rawValue=2
  • availableCardInputMethods=Contactless
  • reader model=3 (tapToPay), status=4 (ready), unavailable reason=nil
  • active handle additionalPaymentMethods=[]

An initial recording observed this for ten seconds without the method appearing. Successful prior attempts exposed additional method type=4 (tapToPay). We then added a safeguard to cancel an empty, cancelable checkout through PaymentHandle.cancelPayment, finishing through didCancel. The next user-initiated attempt invalidated local authorization reuse, retrieved scoped credentials, and passed through SDK deauthorization/authorization. It again showed the same mismatch. No card was presented in these failed checks.

The application has one SDK initialization in didFinishLaunching and a process-wide operation guard around authorization/setup/payment. We do not force Apple relinking or automatically start a second payment. The test device is an iPhone 15 Pro Max. Additional device/OS diagnostics can be supplied privately to Square staff if needed.

Could you identify why the reader can report ready while the payment handle exposes no Tap to Pay method, and the supported recovery or integration change? Is the additional-method configuration above correct for the default prompt in 2.6.0? Is a further readiness signal required?

Representative diagnostic excerpt (no account identifiers, tokens, or card data):

before checkout: sdk=2.6.0, capable=true, requested=2, tapConstant=2, inputs=Contactless, methods=no active handle, readers=[model=3, status=4, reason=none]
checkout +0s: sdk=2.6.0, capable=true, requested=2, tapConstant=2, inputs=Contactless, methods=[], readers=[model=3, status=4, reason=none]
checkout +3s: sdk=2.6.0, capable=true, requested=2, tapConstant=2, inputs=Contactless, methods=[], readers=[model=3, status=4, reason=none]

The same values persisted for ten seconds in an earlier attempt, and recurred after cancellation and authorization refresh.

Hello, @youthrush

I’d try the following:

  • Confirm .tapToPay is passed when creating the payment handle, not only when starting checkout.
  • Test with the latest Square SDK instead of 2.6.0.
  • Don’t repeatedly relink or reauthorize as a workaround. mcdvoice
  • If it still occurs, send Square the handle-creation logs and timestamps.

Best Regard,
James

@youthrush - Could you please provide your application ID, merchant token, and a timestamp of when you ran into this issue, ideally within the last two weeks?

Hi, thanks for looking into this.

  • Application ID: sq0idp-_0go7RxWoYPZRGj_kvSUjg
  • Merchant ID (merchant token): [redacted] — this is the personal Square account used in our recent donation test, which we believe was also used for the failed attempt.
  • Timestamp: September 21, 2026, 06:07:54–06:07:57 UTC (September 20, 11:07:54–11:07:57 PM PDT).
  • SDK/device: Square Mobile Payments SDK 2.6.0, iPhone 15 Pro Max.

During that attempt, the reader reported ready and contactless input was available, but the payment handle’s additionalPaymentMethods remained empty. Checkout displayed “Connect hardware to take card payments.”

We pass AdditionalPaymentMethods([.tapToPay]) directly in the startPayment call that creates the handle. The issue also recurred after SDK deauthorization and reauthorization. No card was presented during this failed attempt.

Please let us know if you need additional diagnostics or a private channel for further account details.

Hi @jseok — we have fresh diagnostics from September 23 that show the issue is intermittent.

On the same iPhone, app build, Square business and location, five of seven checkout attempts offered Tap to Pay; two did not. Successful tests included reaching the actual card-reading screen after fully closing and reopening the app.

All times below are UTC on September 23, 2026:

Checkout started Result
21:55:24 Tap to Pay available
21:57:56 No Tap to Pay method; cancellation confirmed at 21:58:00
22:10:10 Tap to Pay available
22:10:27 Tap to Pay available
22:11:50 Tap to Pay available
22:12:23 No Tap to Pay method; cancellation confirmed at 22:12:26
22:12:29 Tap to Pay available again

During both failures, the SDK reported a ready Tap to Pay reader, contactless input available, and no reader-unavailable reason, but the active payment handle’s additionalPaymentMethods was empty.

Configuration:

  • Square Mobile Payments SDK 2.6.0

  • iPhone 15 Pro Max, iOS 27.2 (24B5084k)

  • App 2.14.0 (69)

  • USD 1.00, online-only processing

  • .tapToPay passed directly in the startPayment prompt parameters

All seven attempts ended in cancellation, with no completed-payment callback. We do not automatically retry payments or force Apple relinking.

Account clarification: These new diagnostics identify the affected merchant conclusively, and it differs from the tentative merchant ID in our previous reply. Please provide a private channel where we can send the verified merchant/location and payment-attempt IDs.

Could your SDK team compare these working and failing sessions and advise on the cause or supported recovery? Please also confirm whether this iOS version has any relevant compatibility limitation. This issue is blocking our release.

AdditionalPaymentMethods([.tapToPay]) in the prompt parameters you pass to startPayment is right for the default prompt in 2.6.0. There’s no additional documented readiness API you’re missing, though your integration does need a handle-level check (below). Note also that the docs’ “Create prompt parameters” section lists only keyed and cash — that’s a documentation gap; .tapToPay is valid, and it’s the value behind the mask of 2 you’re logging.

A ready reader can coexist with an empty list. These are two different signals. statusInfo.status == .ready describes the reader; whether Tap to Pay is offered for a given payment is decided separately, at payment-handle creation, based on the Tap to Pay reader’s payment capability being active — which isn’t surfaced through ReaderInfo. So you’re observing one signal while a different, unobservable one gates the prompt.

The key consequence: the handle’s additionalPaymentMethods is fixed when the handle is created and never recalculated. A handle that starts empty stays empty for its whole life, regardless of what the reader does afterward. That’s why the ten-second observation could never have succeeded, and why cancelling and starting a new payment was the only thing that could recover — matching your 22:12:23 failure and 22:12:29 success.

What I’d change:

  1. Check the handle, then restart — don’t wait. After startPayment, look for a .tapToPay entry in the active handle’s additionalPaymentMethods. If it’s missing, cancel and start a new payment. Your existing cancelPayment/didCancel safeguard is the right mechanism; restart instead of polling. Use a short backoff over a couple of attempts — I can’t tell you how long this window lasts, so don’t assume one retry always clears it.
  2. Reauthorizing won’t help. The gate is reader payment-capability state, not authorization, so the deauthorize/reauthorize cycle isn’t addressing it.
  3. Allow settle time before enabling checkout after launch, foregrounding, and any authorization change. Your own note that successes followed fully reopening the app fits this: confirm isDeviceCapable() and a .ready Tap to Pay reader via a ReaderObserver, then give it a moment before a payment can start.

While this explains the mechanism and (hopefully!) gives you a recovery path, I’m not sure why that capability signal is slow to settle on your device. Still looking into that.

If the handle check in (1) doesn’t clear it, let me know!