# 仮想通貨トランザクションを作成する

> 1回のアトミックトランザクションで、プロファイルに対して1つ以上の仮想通貨をクレジットまたはデビットします。クレジット（付与）する場合は正の `amount` を、デビット（消費）する場合は負の `amount` を使用してください。すべての項目は一括で適用されます — いずれかの項目が失敗した場合、何も適用されません。
>
> 通貨コードはリクエストごとに1回のみ使用できます。このエンドポイントを通じたクレジットは、常に有効期限なしの残高を作成します。

## OpenAPI

```yaml
/api-specs/adapty-api.yaml post /api/v2/server-side-api/vc/transactions/
openapi: 3.1.0
info:
  title: Adapty サーバーサイド API
  version: 1.0.0
servers:
  - url: https://api.adapty.io
    description: 本番サーバー
paths:
  /api/v2/server-side-api/vc/transactions/:
    post:
      summary: 仮想通貨トランザクションを作成する
      description: |
        1回のアトミックトランザクションで、プロファイルに対して1つ以上の仮想通貨をクレジットまたはデビットします。クレジット（付与）する場合は正の `amount` を、デビット（消費）する場合は負の `amount` を使用してください。すべての項目は一括で適用されます — いずれかの項目が失敗した場合、何も適用されません。

        通貨コードはリクエストごとに1回のみ使用できます。このエンドポイントを通じたクレジットは、常に有効期限なしの残高を作成します。
      operationId: createVirtualCurrencyTransaction
      tags:
        - Virtual Currency
      security:
        - apikeyAuth: []
      parameters:
        - name: adapty-customer-user-id
          in: header
          required: false
          schema:
            type: string
          description: お客様のシステムにおける顧客の一意のID。`adapty-customer-user-id` または `adapty-profile-id` のいずれかが必須です。
        - name: adapty-profile-id
          in: header
          required: false
          schema:
            type: string
          description: お客様のシステムにおけるプロファイルの一意のID。匿名プロファイルを扱う場合に最適な選択肢です。`adapty-customer-user-id` または `adapty-profile-id` のいずれかが必須です。
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
            format: uuid
          description: |
            リクエストを冪等にするUUID v4。同じキーでリクエストを再試行した場合、Adaptyは残高を再度変更することなく元のトランザクションの結果を返します。有効なUUID v4である必要があります。

            キーは最大1時間、単一プロファイルのスコープで記憶されます。その期間を過ぎると、同じキーでのリクエストは新しいトランザクションとして適用されます — つまり、このキーは長期的な付与の重複排除ではなく、安全にコールを再試行するために使用してください。
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/VirtualCurrencyTransactionRequest"
            examples:
              debit:
                summary: 1つの通貨を消費（デビット）する
                value:
                  items:
                    - currency_code: COINS
                      amount: -10
              credit:
                summary: 1つの通貨を付与（クレジット）する
                value:
                  items:
                    - currency_code: COINS
                      amount: 500
              multi_currency:
                summary: 2つの通貨を同時にデビットする
                value:
                  items:
                    - currency_code: GOLD
                      amount: -50
                    - currency_code: SILVER
                      amount: -200
              conversion:
                summary: 一方の通貨を他方に変換する
                value:
                  items:
                    - currency_code: SILVER
                      amount: -100
                    - currency_code: GOLD
                      amount: 10
                  metadata:
                    reason: exchange
      responses:
        "200":
          description: トランザクションが正常に適用されました
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VirtualCurrencyTransactionResponse"
              example:
                transaction_id: 0190e8a4-1c2b-7def-8abc-2c1a4b6d8e90
                balances:
                  - code: COINS
                    name: Gold Coins
                    balance: 12950
                    held: 0
                    available: 12950
        "400":
          description: |
            不正なリクエスト。`error_code` フィールドが原因を示します: `insufficient_balance`、`unknown_currency`、`duplicate_currency`、`amount_zero`、`balance_overflow`、`empty_items`、または `idempotency_key_invalid`。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                errors:
                  - source: currency_code
                    errors:
                      - Insufficient balance for currency COINS
                error_code: insufficient_balance
                status_code: 400
        "401":
          description: 認証エラー。APIキーが見つからないか無効です。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: 禁止。このアプリでは仮想通貨のServer APIが有効になっていません。アクセスをリクエストするにはAdaptyサポートにお問い合わせください。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                errors:
                  - source: null
                    errors:
                      - Server API for virtual currencies is not enabled for this app. Contact Adapty support to request access.
                error_code: feature_not_enabled
                status_code: 403
        "404":
          description: プロファイルが見つかりません。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: |
            競合。`error_code` フィールドが原因を示します:

            - `idempotency_in_flight` — 同じ `Idempotency-Key` を持つリクエストがまだ処理中です。`Retry-After` レスポンスヘッダーの間隔後に再試行してください。
            - `duplicate_source_transaction` — 基となるストアトランザクションはすでにクレジット済みのため、再度適用されません。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                errors:
                  - source: null
                    errors:
                      - A similar request is being processed.
                error_code: idempotency_in_flight
                status_code: 409
        "422":
          description: |
            処理不可能なエンティティ。リクエストボディがスキーマバリデーションに失敗しました — たとえば、`items` に20件を超えるエントリが含まれているか、`metadata` のキーまたは値が制約に違反している場合などです。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: |
            リクエスト過多。アプリごとまたはグローバルのレート制限を超えました（`error_code` は `rate_limited`）。デフォルトの制限は、アプリごとに1分あたり600リクエスト、グローバルで1分あたり6000リクエストです。
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: 内部サーバーエラー
components:
  schemas:
    VirtualCurrencyTransactionRequest:
      type: object
      description: バーチャル通貨トランザクションを作成するためのリクエストボディ。
      properties:
        items:
          type: array
          minItems: 1
          maxItems: 20
          description: アトミックに適用する残高調整。各通貨コードは一度のみ指定できます。
          items:
            $ref: "#/components/schemas/VirtualCurrencyBalanceAdjustment"
        metadata:
          type: object
          nullable: true
          additionalProperties:
            type: string
          description: |
            トランザクションとともに保存されるオプションのキーと値のペア。最大5つのキーを指定できます。各キーは `^[a-z0-9_]{1,30}$` に一致し、各値は最大200文字です。
      required:
        - items
    VirtualCurrencyTransactionResponse:
      type: object
      description: バーチャル通貨トランザクションの結果。
      properties:
        transaction_id:
          type: string
          format: uuid
          description: 作成されたトランザクションの一意のID。
        balances:
          type: array
          description: |
            このリクエストによって影響を受けた各通貨のトランザクション後の残高（`items` の各アイテムにつき1エントリ）。プロファイルの残高一覧全体を取得するには、`GET /api/v2/server-side-api/vc/balances/` を使用してください。
          items:
            $ref: "#/components/schemas/VirtualCurrencyBalanceSnapshot"
      required:
        - transaction_id
        - balances
    ErrorResponse:
      type: object
      properties:
        errors:
          type: array
          items:
            type: object
            properties:
              source:
                type: string
                nullable: true
                description: エラーの発生源
              errors:
                type: array
                items:
                  type: string
                description: エラーメッセージの配列
        error_code:
          type: string
          description: エラーの短い名称
        status_code:
          type: integer
          description: HTTPステータスコード
      required:
        - errors
        - error_code
        - status_code
    VirtualCurrencyBalanceAdjustment:
      type: object
      properties:
        currency_code:
          type: string
          description: 調整するバーチャル通貨コード。
        amount:
          type: integer
          format: int32
          description: |
            適用する金額。正の値は通貨を付与（クレジット）し、負の値は通貨を消費（デビット）します。ゼロは指定できません。
      required:
        - currency_code
        - amount
    VirtualCurrencyBalanceSnapshot:
      type: object
      properties:
        code:
          type: string
          description: 仮想通貨コード。
        name:
          type: string
          description: 仮想通貨の表示名。
        balance:
          type: integer
          format: int32
          minimum: 0
          description: 現在保留中の金額を含む合計残高。
        held:
          type: integer
          format: int32
          minimum: 0
          description: すべてのアクティブな保留（予約済み金額）の合計。現在は常に 0。
        available:
          type: integer
          format: int32
          description: 使用可能な残高。`balance - held` として計算されます。
      required:
        - code
        - name
        - balance
        - held
        - available
  securitySchemes:
    apikeyAuth:
      type: apiKey
      name: Authorization
      in: header
      default: Api-Key {Your secret API key}
      description: |
        APIリクエストは、シークレットAPIキーを **Authorization** ヘッダーに `Api-Key {your_secret_api_key}` の形式で設定することで認証されます。例えば、`Api-Key secret_live_...` のように指定します。このキーは Adapty ダッシュボード -> **App Settings** -> **General** タブ -> **API keys** セクションで確認できます。
```
