> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS

> iOS 앱의 SFSafariViewController에서 Dodo Payments의 호스팅 checkout을 열고 한 번의 호출로 타입이 지정된 결과를 받습니다.

<Info>
  Swift용 공식 Dodo Payments iOS checkout SDK입니다. 네이티브 브라우저 뷰에서 Dodo의 호스팅 checkout을 열고 타입이 지정된 결과를 반환합니다.
</Info>

<CardGroup cols={2}>
  <Card title="Checkout Sessions API" icon="cart-shopping" href="/developer-resources/checkout-session">
    이 SDK가 열 checkout\_url을 backend에서 생성합니다.
  </Card>

  <Card title="Mobile Integration Guide" icon="mobile" href="/developer-resources/mobile-integration">
    전체 모바일 결제 흐름에서 어떻게 구성되는지 확인하세요.
  </Card>
</CardGroup>

iOS SDK는 Dodo의 호스팅 checkout을 `SFSafariViewController`에서 열고 API key를 보유하지 않으며 Dodo API를 직접 호출하지 않습니다. 모든 checkout 로직은 브라우저에서 실행되고, SDK는 뷰 수명 주기를 관리하며 return URL을 캡처하기만 합니다.

iOS 16 이상 및 Swift 6이 필요합니다.

## 설치

<Steps>
  <Step title="Add the Package">
    Xcode에서 **File → Add Package Dependencies**로 이동한 다음 다음을 입력합니다:

    ```
    https://github.com/dodopayments/dodopayments-mobile-sdk-ios
    ```

    버전 1.0.0 이상을 선택합니다.

    또는 `Package.swift`에 추가합니다:

    ```swift Package.swift theme={null}
    .package(url: "https://github.com/dodopayments/dodopayments-mobile-sdk-ios", from: "1.0.0")
    ```
  </Step>

  <Step title="Register a Callback URL Scheme">
    checkout에서 반환되는 URL을 수신하려면 앱에서 URL scheme을 등록해야 합니다. `Info.plist`에 다음을 추가합니다:

    ```xml Info.plist theme={null}
    <key>CFBundleURLTypes</key>
    <array>
      <dict>
        <key>CFBundleURLName</key>
        <string>myapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
          <string>myapp</string>
        </array>
      </dict>
    </array>
    ```

    Xcode의 **Info → URL Types** UI를 통해 추가할 수도 있습니다.
  </Step>
</Steps>

## 사용법

```swift theme={null}
import DodoCheckout

let result = try await DodoCheckout.start(
    checkoutUrl: checkoutUrl,   // from your backend's checkout session
    returnUrl: URL(string: "myapp://checkout/return")!,
    onEvent: { event in print(event.name) }  // logging only
)

switch result.status {
case .succeeded: showSuccess(result.paymentId)
case .failed:    showFailure()
case .cancelled: dismiss()
case .pending:   showPending()
case .expired:   showExpired()
}
```

## Return URL 전달

`SFSafariViewController`에는 자체 return URL을 가로챌 수 있는 프로세스 내 방법이 없습니다. 앱에서 수신한 URL을 SDK로 전달해야 합니다.

<Tabs>
  <Tab title="SwiftUI">
    ```swift theme={null}
    .onOpenURL { url in
        DodoCheckout.handleOpenURL(url)
    }
    ```
  </Tab>

  <Tab title="SceneDelegate">
    ```swift SceneDelegate.swift theme={null}
    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        guard let url = URLContexts.first?.url else { return }
        DodoCheckout.handleOpenURL(url)
    }
    ```
  </Tab>
</Tabs>

<Note>
  여기서는 모든 URL을 전달해도 안전합니다. `handleOpenURL`는 등록된 `returnUrl`와 일치하는 URL에만 작동하며, 그 외의 URL에는 `false`를 반환합니다.
</Note>

## 결과의 의미

<Warning>
  `result.status`는 UI 힌트일 뿐 결제가 완료되었다는 증거가 아닙니다. `payment.succeeded` / `subscription.active` webhook을 통해 backend에서 모든 결제를 확인하세요.
</Warning>

<ParamField body="status" type="CheckoutStatus" required>
  `succeeded`, `failed`, `cancelled`, `pending`, `expired` 중 하나입니다.
</ParamField>

<ParamField body="paymentId" type="String?">
  return URL에 해당 값이 포함된 경우 설정됩니다. UI에 표시하되 액세스 권한을 부여하는 데 사용하지 마세요. 아래의 결제 확인을 참조하세요.
</ParamField>

<ParamField body="subscriptionId" type="String?">
  subscription checkout에 대해 설정됩니다.
</ParamField>

<ParamField body="licenseKeys" type="[String]?">
  checkout에 license key 제품이 포함된 경우 설정됩니다.
</ParamField>

<ParamField body="customerEmail" type="String?">
  checkout에서 이메일을 수집하는 경우 설정됩니다.
</ParamField>

<ParamField body="raw" type="[String: String]">
  return URL의 모든 query parameter를 있는 그대로 포함합니다.
</ParamField>

## 결제 확인

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/developer-resources/webhooks">
    결제가 성공하거나 subscription이 활성화되면 Dodo Payments가 backend를 호출합니다.
  </Card>

  <Card title="Get Payment Detail" icon="magnifying-glass" href="/api-reference/payments/get-payments-1">
    secret key를 사용하여 `paymentId`를 조회하고 상태를 직접 확인합니다.
  </Card>
</CardGroup>

이 중 하나가 결제를 확인한 후에 액세스 권한을 부여하세요. `result.status`만으로 부여해서는 안 됩니다.

## 오류

`start`는 잘못된 사용이나 플랫폼 장애가 발생한 경우에만 `CheckoutError`를 발생시킵니다. 취소되거나 거부된 결제는 항상 결과로 반환되며 예외가 아닙니다.

* `invalidCheckoutUrl` (`INVALID_CHECKOUT_URL`): `checkout.dodopayments.com` session URL이 아닙니다.
* `invalidReturnUrl` (`INVALID_RETURN_URL`): 유효한 absolute URL이 아닙니다.
* `alreadyInProgress` (`ALREADY_IN_PROGRESS`): checkout이 이미 실행 중입니다.
* `platformError` (`PLATFORM_ERROR`): 예상치 못한 플랫폼 장애입니다.

## 중단된 세션

<Info>
  checkout 중 앱이 종료된 경우 다음 실행 시 세션을 복구하고 backend와 대조하여 처리하세요.
</Info>

```swift theme={null}
import DodoCheckout

if let abandoned = DodoCheckout.getAbandonedSession() {
    // reconcile abandoned.sessionId with your backend, then:
    DodoCheckout.clearAbandonedSession()
}
```

## 관련 문서

<CardGroup cols={2}>
  <Card title="Mobile Integration Guide" icon="mobile" href="/developer-resources/mobile-integration">
    Android, React Native, Flutter에도 동일한 계약이 적용됩니다.
  </Card>

  <Card title="React Native SDK" icon="react" href="/developer-resources/sdks/react-native">
    iOS에서 동일한 Swift core를 래핑합니다.
  </Card>
</CardGroup>
