> For the complete documentation index, see [llms.txt](https://docs.ecommerce.seb.ee/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ecommerce.seb.ee/integration/mobile-sdk/ios-sdk/using-the-sdk/handling-the-result.md).

# Handling the result

Read the payment outcome, rely on the callback contract, and plug in your own status lookup.

Use this guide if you want to know exactly what your app is told when a payment run ends, and what it may safely do with that.

Both `startPayment` and `startCardPayment` deliver a single `PaymentResult`.

***

## The result

| Property        | Type             | Notes                                                                               |
| --------------- | ---------------- | ----------------------------------------------------------------------------------- |
| `outcome`       | `PaymentOutcome` | One of four cases, below.                                                           |
| `paymentId`     | `String`         | Always present.                                                                     |
| `paymentStatus` | `String?`        | The platform's raw status, for example `"SETTLED"`.                                 |
| `orderNumber`   | `String?`        | The platform's record of the order number your backend supplied.                    |
| `sessionId`     | `String?`        | The card session identifier. Set by the card flow only.                             |
| `error`         | `NSError?`       | `nil` when the payment simply did not succeed, as opposed to something going wrong. |

`paymentStatus` is deliberately a string rather than an enumeration, so a status added on the platform side reaches your code intact instead of being flattened into something else.

`orderNumber` is the natural key for correlating a result with your own order.

***

## The four outcomes

```swift
switch result.outcome {
case .succeeded:
    showReceipt(reference: result.orderNumber ?? result.paymentId)

case .failed:
    showFailure(message: result.error?.localizedDescription)

case .cancelled:
    // The shopper backed out.
    break

case .unresolved:
    // The SDK does not know. Ask your backend.
    confirmWithBackend(paymentId: result.paymentId)
}
```

The platform's status maps onto the four outcomes like this:

| Outcome       | Payment status                                                                 |
| ------------- | ------------------------------------------------------------------------------ |
| `.succeeded`  | `AUTHORIZED`, `CAPTURED`, `SETTLED`, `REFUNDED`                                |
| `.failed`     | `FAILED`                                                                       |
| `.cancelled`  | `CANCELLED`, `ABANDONED`                                                       |
| `.unresolved` | `INITIALIZED`, `SENT_FOR_PROCESSING`, or any status the SDK does not recognise |

`.failed` also covers the cases where the SDK could not run the flow at all, and `.cancelled` also covers the shopper dismissing the checkout page. When the SDK itself failed, `result.error` says why.

### `.unresolved` is not an error

It means one of three things:

* No `apiBaseURL` is configured and no custom resolver was supplied, so no lookup was attempted.
* The lookup was attempted and failed. `result.error` carries the reason.
* The payment is genuinely still in flight: the shopper has finished with the page, but the platform has not reached a final state yet.

Do not present it as success or as failure. Ask your backend using `result.paymentId`, or show a "we are confirming your payment" state and let your backend tell you the answer.

{% hint style="warning" %}
⚠️ **Never treat any outcome, including `.succeeded`, as authorisation to fulfil an order.** The result tells you what to show the shopper. Fulfilment belongs to your backend, driven by the notifications the platform sends it.
{% endhint %}

***

## The callback contract

For both entry points:

* **Exactly one** callback per call. Never zero, never twice.
* Always on the **main thread**.
* Always **after** the SDK's own interface has been dismissed, so presenting your receipt screen from inside the callback works.
* Every terminating path goes through it: the return URL was reached, the shopper cancelled, the page failed to load, the status lookup failed, the card session failed, or the request was unusable.

One `PaymentKit` instance runs one payment at a time. Starting a second while the first is still on screen finishes the second immediately as `.failed` with an `invalidRequest` error, rather than stacking two checkouts.

***

## How the status is resolved

Once the flow reaches a terminal point, the SDK calls:

```
GET {apiBaseURL}/checkout/api/egw/v1/payments/{paymentId}
Obp-Verification-Key: {verificationKey}
X-Request-Id: {uuid}
```

and maps the `paymentStatus` it gets back onto one of the four outcomes.

With `apiBaseURL` set to `nil` and no custom resolver, no lookup is attempted and every payment finishes `.unresolved`. That is a valid configuration if your backend is going to be asked anyway.

***

## Supplying your own status lookup

`apiBaseURL` works when the host you point it at serves that path shape. If yours does not, or you would rather keep one place in your system that knows about the payment gateway, implement `PaymentStatusResolving`:

```swift
final class BackendStatusResolver: NSObject, PaymentStatusResolving {
    func resolveStatus(
        paymentId: String,
        verificationKey: String,
        completion: @escaping (String?, String?, NSError?) -> Void
    ) {
        myAPI.paymentStatus(paymentId) { status, orderNumber, error in
            // Return the platform's raw status string ("SETTLED", "FAILED", …),
            // the order number, or an error.
            completion(status, orderNumber, error)
        }
    }
}

let paymentKit = PaymentKit(
    configuration: Configuration(apiBaseURL: nil, environment: .live),
    statusResolver: BackendStatusResolver()
)
```

Return the platform's raw status strings and the SDK maps them for you. Call `completion` exactly once, from any queue. A `nil` status with a `nil` error resolves to `.unresolved`.

This replaces hosted checkout's status lookup only; the card flow makes none. In the card flow, the card session calls still go from the device to the checkout API, which is how that flow is built.

***

## Next steps

* [Error handling](/integration/mobile-sdk/ios-sdk/guides/error-handling.md)
* [API reference](/integration/mobile-sdk/ios-sdk/reference/api.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.ecommerce.seb.ee/integration/mobile-sdk/ios-sdk/using-the-sdk/handling-the-result.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
