---
title: laconicman/yoomoneyapiclient
framework: Swift Package Catalog
role: article
path: packages/laconicman/yoomoneyapiclient
---

# laconicman/yoomoneyapiclient

A Swift client for the [YooKassa API](https://yookassa.ru/developers/api), generated with

## Documentation

The rendered DocC catalog is authoritative for architecture and direction; when it and this README disagree, believe the catalog. Build it with `swift package generate-documentation --target YooMoneyAPI`, or read the sources:

| Article | What it answers | |---|---| | [Design](Sources/YooMoneyAPI.docc/Design.md) | Why the client is generated, and every load-bearing decision with the alternative that was rejected | | [Migrating from 1.x to 2.0](Sources/YooMoneyAPI.docc/Migration.md) | Every source-breaking change, the exact edit that resolves it, and why it was made | | [The Specification Pipeline](Sources/YooMoneyAPI.docc/SpecPipeline.md) | Why an official specification still has to be rewritten before a generator can use it, and what the four transforms do | | [Tech Debt](Sources/YooMoneyAPI.docc/TechDebt.md) | Every compromise carried, what it costs, and what would retire it | | [Roadmap](Sources/YooMoneyAPI.docc/Roadmap.md) | Planned work in priority order |

## Installation

```swift .package(url: "https://github.com/laconicman/YooMoneyAPIClient", from: "2.0.0") ```

Requires Swift 6.1 or newer, macOS 13 / iOS 16 / tvOS 16 / watchOS 9 / visionOS 1.

## Usage

```swift import Foundation import YooMoneyAPI

let client = try YooClient(credentials: .init(username: "<shopId>", password: "<secretKey>"))

let response = try await client.createPayment(     headers: .init(idempotenceKey: UUID().uuidString),     body: .json(         .init(             amount: .init(value: 1924.0, currency: .rub),             description: "Order #42",             confirmation: .redirect(returnUrl: "https://example.com/return")         )     ) )

switch response { case .ok(let ok):     let payment = try ok.body.json     print(payment.id, payment.status) case .badRequest, .unauthorized, .forbidden, .internalServerError, .undocumented:     // Handle per case; the generated enum is exhaustive.     break } ```

### Idempotence keys

Every `POST` takes a required `idempotenceKey` header. Pass **one key per logical operation** and reuse it when retrying that operation, which is what makes a retry safe. A fresh key on every attempt — for instance one generated inside a middleware — defeats the mechanism, so the client does not generate keys for you.

### Amounts

The API expresses money as decimal strings. `Components.Schemas.MonetaryAmount` has a `Double` initializer, and `Double.string` / `String.double` convert in either direction using the format the API expects.

```swift let total = try Components.Schemas.MonetaryAmount.from(items: cart)  // sums price x quantity ```

### Credentials for debugging

`Credentials.environment` reads `APP_USERNAME` and `APP_PASSWORD` and returns `nil` when either is unset. In Xcode, set them under `Edit scheme`. Keep them out of version control.

## Specification pipeline

``` Spec/upstream/yookassa-openapi-specification.yaml   pristine upstream snapshot         │  swift run SpecSync         ▼ Sources/YooMoneyAPIClient/openapi.yaml              derived — do not edit         │  swift package generate-code-from-openapi         ▼ Sources/GeneratedSources/{Types,Client}.swift       generated — do not edit ```

The upstream document is vendored verbatim and never edited; `openapi.yaml` is derived from it. Fed to the generator unchanged, upstream produces a client that **cannot decode a payment response**, so `Tools/SpecSync` applies four transforms — repointing discriminated unions, collapsing annotation-only `allOf` wrappers, hoisting inline bodies, and injecting `operationId`s. Each rule fails the sync rather than guess when the upstream shape it relies on changes, because a client that is quietly wrong is worse than none.

[The Specification Pipeline](Sources/YooMoneyAPI.docc/SpecPipeline.md) explains each rule, why an official specification needs rewriting at all, and where the defects come from.

### Refresh from upstream

```console % swift run SpecSync --fetch     # download a new snapshot, then re-derive openapi.yaml % swift package generate-code-from-openapi ```

Omit `--fetch` to re-derive from the committed snapshot. Review both diffs: the snapshot diff is upstream's change, the `openapi.yaml` diff is its effect on the client.

### Endpoint scope

`openapi.yaml` keeps every upstream operation; `filter.paths` in `Sources/YooMoneyAPIClient/openapi-generator-config.yaml` decides which reach the client. Currently: payments, payment methods, invoices, refunds, receipts, deals, shop settings and POS links. Payouts, personal data, SBP participants and webhooks are excluded because they need an OAuth token or a payout gateway rather than shop credentials — adding them is a change to that list alone.

## Testing

```console % swift test ```

The offline suites need no credentials and cover amount arithmetic and the discriminated union decoding described above. `LiveClientTests` talks to the real API and is skipped unless `APP_USERNAME` and `APP_PASSWORD` are set.

## Migrating from 1.x

Generated type names now follow the official specification. Deprecated aliases in `Deprecations.swift` cover most renames, so the compiler names the replacement for you. Four changes cannot be aliased and need reading:

- **`Confirmation` and `ReceiptItem` still exist but changed meaning.** They are now the   *response* shapes; requests use `ConfirmationData` and `ReceiptDataItem`. No alias can   express that, so these surface as type mismatches at the call site. - **Every `Output` switch changed.** `forbidden` and `internalServerError` are new on   nearly every operation, and `tooManyRequests` is gone from most of them. - **Ten `POST` operations take an `idempotenceKey` header.** Previously a middleware   invented one per request, which never actually made a retry idempotent. - **Six `PaymentMethodData` variants no longer exist upstream** — Alfa-Bank, Apple Pay,   Google Pay, QIWI, WebMoney and installments — and have no replacement.

[Migrating from 1.x to 2.0](Sources/YooMoneyAPI.docc/Migration.md) covers all of it symbol by symbol, with the reasoning behind each change and a checklist to work through.

## Known limitations

Tracked in full, with costs and discharge conditions, in [Tech Debt](Sources/YooMoneyAPI.docc/TechDebt.md). The ones most likely to affect a caller:

- Variants that add no fields of their own (`ConfirmationExternal`, `PaymentMethodDataSbp`)   keep the generator's `value1` / `value2` split. `Types+.swift` provides factory methods   (`.redirect(returnUrl:)`, `.bankCard(card:)`) that hide it for the common cases. - Money is `Double`, not `Decimal`. - SwiftPM reports `openapi.yaml` and `openapi-generator-config.yaml` as unhandled files.   That is expected: the generator plugin finds them by scanning the target's files.

## Package Metadata

Repository: laconicman/yoomoneyapiclient

Default branch: main

README: README.md
