# Handle errors and unclear outcomes

All calls throw `TapToPayError`. Most errors mean that no payment occurred. One error, `outcomeUnknown`, means that the card can be charged. Handle it as described in [Unclear outcome](#outcome-unknown).

## Handle errors in code[​](#handle-errors "Direct link to Handle errors in code")

```
do {

  let result = try await TapToPay.acceptPayment(

    amount: amountInCents, orderId: orderId, callbackUrl: webhookUrl)

  show(result)

} catch let error as TapToPayError {

  switch error {

  case .outcomeUnknown(let orderId):

    showPending(orderId) // Do not retry.

  case .cancelled:

    break // No payment occurred.

  case .tokenExpired, .notPrepared, .invalidToken:

    await renewTokenAndPrepare()

  case .locationDenied:

    showLocationSettingsHint()

  case .termsDeclined:

    showTermsRequired()

  case .paymentFailed(let code):

    showFailure(code)

  default:

    showError(error)

  }

} catch {

  showError(error)

}
```

A `switch` without a `default` case needs `@unknown default`. See [Switch statements](https://docs.monei.com/monei-pay/in-app-tap-to-pay/accept-payments.md#unknown-default).

## Errors[​](#errors "Direct link to Errors")

| Error                      | Thrown by                  | Meaning                                                                                               | What to do                                                                                                                                                                       |
| -------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `notSupported`             | All calls                  | The device or the iOS version does not support Tap to Pay on iPhone.                                  | Hide Tap to Pay on iPhone.                                                                                                                                                       |
| `locationDenied`           | `prepare`, `acceptPayment` | The user did not allow location access, or `Info.plist` has no `NSLocationWhenInUseUsageDescription`. | If the user did not allow access: tell the user to allow location access in Settings, then try again. If the key is missing: add it to `Info.plist` and release a corrected app. |
| `termsDeclined`            | `acceptPayment`            | The user did not accept Apple's terms, or the link of the account failed.                             | Tell the user that the terms are necessary. The next `acceptPayment` shows the terms again.                                                                                      |
| `invalidArgument`          | `acceptPayment`            | `amount` is 0 or less, or `orderId` is empty.                                                         | Correct the value.                                                                                                                                                               |
| `tokenExpired`             | `prepare`, `acceptPayment` | The token expired.                                                                                    | Get a new token from your server. Call `prepare`. Then try again.                                                                                                                |
| `invalidToken`             | `prepare`                  | The SDK cannot read the token.                                                                        | Send only the `token` field. Get a new token and call `prepare`. Until then, `acceptPayment` throws `notPrepared`.                                                               |
| `notPrepared`              | `acceptPayment`            | No valid token is stored.                                                                             | Call `prepare` first.                                                                                                                                                            |
| `busy`                     | `acceptPayment`            | A payment is in progress.                                                                             | Wait until it ends. Disable your pay button during a payment.                                                                                                                    |
| `cancelled`                | `acceptPayment`            | The user cancelled on Apple's screen. No payment occurred.                                            | Start a new payment when the customer is ready.                                                                                                                                  |
| `sdkUpgradeRequired`       | `prepare`, `acceptPayment` | MONEI blocked this SDK version. `prepare` and `acceptPayment` fail with this error.                   | Update to a newer SDK version and release your app.                                                                                                                              |
| `outcomeUnknown(orderId:)` | `acceptPayment`            | The SDK cannot tell if the payment reached MONEI. The card can be charged.                            | Do not retry. Wait for the signed webhook, or find the `orderId` in MONEI. See [Unclear outcome](#outcome-unknown).                                                              |
| `paymentFailed(code:)`     | All calls                  | The payment or the setup failed.                                                                      | See [Failure codes](#failure-codes).                                                                                                                                             |

## Failure codes[​](#failure-codes "Direct link to Failure codes")

`paymentFailed(code:)` has one of these `TapToPayErrorCode` values:

| Code              | Meaning                                                     | What to do                                                                                     |
| ----------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `cardDeclined`    | The card was declined during the read. No payment occurred. | Ask for a different card.                                                                      |
| `readerNotReady`  | The reader session was not ready or expired.                | Try again. The SDK prepares the reader again on the next call.                                 |
| `locationTimeout` | The device did not get a location in 15 seconds.            | Make sure that Location Services are on. Then try again.                                       |
| `unknown`         | The payment did not start. No payment occurred.             | Try again. If the error continues, [contact MONEI](https://docs.monei.com/contact-support.md). |

## Declined payments are not errors[​](#declined "Direct link to Declined payments are not errors")

A `PaymentResult` with the status `.declined` is not an error. The card was read and the issuer declined the payment. The payment is in MONEI with its `paymentId`. The reason is in `statusMessage`. See [Show the decline reason](https://docs.monei.com/monei-pay/in-app-tap-to-pay/accept-payments.md#decline-reason). Ask the customer for a different card, and start a new payment.

## Unclear outcome[​](#outcome-unknown "Direct link to Unclear outcome")

`outcomeUnknown(orderId:)` means that the SDK cannot tell if the payment reached MONEI. **The card can be charged.**

Causes:

* A connection or server error at any step of the payment.
* A card read error, a PIN entry error, or a lost reader session during the payment.
* A reply that is not a clear approval or a clear decline. This can also occur when the card is declined.
* A cancelled task.

Some of these occur before the card is charged, but the SDK cannot tell which.

Do not retry

Do not retry the payment, and do not charge again with a new `orderId`. The customer can pay twice.

<!-- -->

1. Show a pending state with the `orderId`.

2. Wait for the [signed webhook](https://docs.monei.com/monei-pay/in-app-tap-to-pay/accept-payments.md#webhook) for this `orderId`.

3. If no webhook arrives, find the `orderId` in MONEI:

   * In the MONEI Dashboard, open **Payments** and [filter by order ID](https://docs.monei.com/manage-account/transaction-history.md#filters).

   * With the GraphQL API, use the [`charges`](https://docs.monei.com/apis/graphql/operations/queries/charges.md) query with `filter.orderId`:

     ```
     query {

       charges(filter: {orderId: {eq: "order-1042"}}) {

         items {

           id

           status

           amount

         }

       }

     }
     ```

4. If no payment shows, this does not prove that the card was not charged. [Contact MONEI support](https://docs.monei.com/contact-support.md) with the `orderId` before you charge the customer again.

## Common questions[​](#common-questions "Direct link to Common questions")

### Can I try again after cancelled?[​](#retry-after-cancelled "Direct link to Can I try again after cancelled?")

Yes. `cancelled` means that the user cancelled on Apple's screen and no payment occurred. Start a new payment when the customer is ready.

### Why do I get notPrepared after prepare failed?[​](#not-prepared-after-failure "Direct link to Why do I get notPrepared after prepare failed?")

When `prepare` throws `invalidToken` or `tokenExpired`, the SDK does not keep a token. Get a new token from your server, send only the `token` field, and call `prepare` again.

### What does sdkUpgradeRequired mean?[​](#sdk-upgrade-required "Direct link to What does sdkUpgradeRequired mean?")

MONEI blocked the SDK version in your app. `prepare` and `acceptPayment` fail with this error. Update to a newer SDK version and release a new version of your app. See [Versions and compatibility](https://docs.monei.com/monei-pay/in-app-tap-to-pay/test-and-go-live.md#versions).

### Why is a card read error an unclear outcome?[​](#card-read-error "Direct link to Why is a card read error an unclear outcome?")

Some reader errors can occur after the SDK sent the payment to MONEI. The SDK cannot tell these errors from errors that occur before, so it throws `outcomeUnknown` for all of them. This prevents a second charge to the customer by mistake.
