# 外部ウォレット

> 専用の Nodaro Cloud デプロイメントを、自分の共有ウォレットに接続します。ウォレットは、すべてのプロダクトを通じて各顧客のクレジットを確保、精算、報告します。

Source: https://nodaro.ai/ja/docs/developers/external-wallet

**外部ウォレット**は、あなたが運用するサービスです。各顧客の共有予算を保持し、専用の Nodaro Cloud デプロイメントは、支出する前にこのサービスに問い合わせます。Nodaro は、生成ごとに、実行前にクレジットの確保をウォレットに依頼し、その後に正確な金額を精算し、残高を読み取って顧客に表示します。デプロイメントの請求アカウントは、それでもプラットフォームの利用分を自分のプリペイド残高から支払いますが、そのうちどれだけを各顧客が使えるかは、すべてのプロダクトを通じて、ウォレットが決めます。

## 仕組み
- **請求アカウントが支払います。**デプロイメントは、1 つの請求アカウント（サーフェスプロファイルの `billing.payerAccount`）を指定し、そのプリペイド残高が、デプロイメント上のすべての操作の代金を支払います。[エディションとサーフェスプロファイル](https://nodaro.ai/docs/self-hosting/editions-and-profiles)を参照してください。
- **SSO が顧客を識別します。**顧客は、デプロイメントの [SSO プロバイダー](https://nodaro.ai/docs/developers/sso)を通じてログインします。顧客に対するプロバイダーのサブジェクトは `sso_subject` として送られ、あなたのウォレットはこれによって顧客を認識します。
- **ウォレットが判断します。**単一ノードのリクエストとワークフローのノードのそれぞれについて、Nodaro はまず請求アカウントのプリペイド残高を確認し、そのうえで、顧客の共有予算に対する確保をあなたのウォレットに依頼します。顧客のローカルな Nodaro クレジット残高は関係しません。

プリペイド残高が空の場合、ウォレットに問い合わせる前にリクエストが止まることがあります。一度問い合わせが行われたら、その顧客の共有予算については、ウォレットの判断が最終的なものになります。

### 確保
顧客が生成を開始します。Nodaro は `operation_id` と金額を付けて `reserve` を送り、あなたのウォレットはその金額をアトミックに保持して、`allow` または `deny` で応答します。

### 実行
`allow` の場合、モデルが実行されます。それ以外の場合、モデルには何も送られません。

### 精算
処理が終わると、Nodaro は実際の金額を付けて `settle` を送ります。あなたのウォレットは、その金額どおりに課金し、確保の残りを解放します。

### 残高の表示
スタジオは、あなたのウォレットに顧客の `balance` を問い合わせ、デプロイメントの表示単位でそれを表示します。

## デプロイメントを設定する
API とワーカーの両方に、次の環境変数を設定します。

| 変数 | 設定する内容 |
| --- | --- |
| `DEPLOYMENT_WALLET_URL` | ウォレットのエンドポイントです。HTTPS である必要があります。 |
| `DEPLOYMENT_WALLET_TOKEN` | Nodaro がベアラートークンとして送る、専用のサーバーシークレットです。 |
| `DEPLOYMENT_WALLET_SSO_PROVIDER` | そのサブジェクトで顧客を識別する、SSO プロバイダーの ID です。 |
| `DEPLOYMENT_WALLET_TIMEOUT_MS` | ウォレットへの各呼び出しの時間制限で、1000〜30000 ミリ秒です。デフォルトは 10000 です。 |

- **最初の 3 つは、まとめて設定します。**デプロイメントの既存の請求方法を維持するには、3 つすべてを未設定のままにします。
- **ローカルの利用枠をオフにします。**ユーザーごとの利用枠の強制、`billing.allowances: "enforce"` は、オフである必要があります。この 2 つを組み合わせた設定は、有効化の時点で拒否されます。
- **単位を先に決めます。**ウォレットを有効化する前に、Nodaro のクレジットと自分のプロダクトの予算との換算方法を決めておきます。サーフェスプロファイルの `billing.unitRate` と `billing.unitLabel` をそれに合わせて設定すると、両方のプロダクトで同じ数字が表示されます。

## リクエストの契約
Nodaro は、すべての呼び出しを `POST` で送ります。JSON ボディには `contract: 1` と `unit: "nodaro_credit"` が含まれ、ヘッダーには `Authorization: Bearer <token>` が付きます。金額は Nodaro のクレジットです。あなたのウォレットが管理する明示的な換算を経ずに、自分のプロダクトの単位として読み取らないでください。

### 確保
```json
{
"contract": 1,
"unit": "nodaro_credit",
"action": "reserve",
"operation_id": "a3354131-4e5e-43a6-8f61-3914f09c658e",
"job_id": null,
"user_id": "5bf0d884-47b1-468e-a7b2-2433f957b267",
"sso_provider": "partner",
"sso_subject": "customer-123",
"model_identifier": "example-model",
"reserved_credits": 30
}
```

| フィールド | 意味 |
| --- | --- |
| `operation_id` | 1 件の利用記録です。1 つのジョブやワークフローに複数含まれることがあります。 |
| `sso_subject` | 顧客です。Nodaro のサーバーが管理する、信頼できるログインデータから取得されるため、顧客の識別に使ってください。 |
| `sso_provider`、`user_id` | リクエストの背後にある SSO プロバイダーと Nodaro のユーザーです。 |
| `model_identifier` | 課金対象のモデルです。 |
| `reserved_credits` | 確保するクレジットの整数値です。 |

応答する前に、顧客の共有予算のうち利用可能な分から、その金額をアトミックに確保してください。リクエストを許可する場合は、次のように応答します。

```json
{"contract":1,"unit":"nodaro_credit","operation_id":"a3354131-4e5e-43a6-8f61-3914f09c658e","decision":"allow","reserved_credits":30}
```

予算が不足している場合や、支出が許可されていないアカウントの場合は、同じエンベロープで HTTP `200` を返し、`"decision": "deny"` とします。金額は不要です。サービス側のエラーには、`2xx` 以外のステータスで応答してください。

あなたのウォレットが許可しない限り、モデルには何も届きません。タイムアウト、形式が正しくない応答、金額や ID の不一致、識別情報の欠落、拒否のいずれも、リクエストを止めます。このリクエストの `Idempotency-Key` ヘッダーは `<operation_id>:reserve` です。

### 精算または解放
処理が終わると、Nodaro は同じ操作のフィールドに `"action": "settle"` と `actual_credits` を付けて送ります。その金額どおりに課金し、確保の残りを解放してください。`0` での精算は、すべてを解放します。

```json
{"contract":1,"unit":"nodaro_credit","operation_id":"a3354131-4e5e-43a6-8f61-3914f09c658e","settled":true,"actual_credits":12}
```

- **キーは `<operation_id>:settle` です。**配信は少なくとも 1 回です。再試行では、再度課金せずに、最初の結果を返す必要があります。
- **矛盾するリクエストは拒否します。**同じ操作に対して、識別情報、確保の上限、最終的な金額のいずれかが異なるリクエストは拒否してください。
- **遅れた確保より先に、精算が届くことがあります。**`0` での精算は、まだ知らない操作であっても、永続的な最終記録を作成する必要があります。その後に届く確保が、その記録を再び開いてはいけません。
- **確保に有効期限はありません。**生成と、その結果に対する人によるレビューは、どの HTTP リクエストよりも長く続くことがあります。各確保は精算されるまで保持し、べき等性の記録も保持してください。コールバックが遅れているという理由だけで、有効な確保を解放しないでください。

### 残高
```json
{"contract":1,"unit":"nodaro_credit","action":"balance","user_id":"5bf0d884-47b1-468e-a7b2-2433f957b267","sso_provider":"partner","sso_subject":"customer-123"}
```

```json
{"contract":1,"unit":"nodaro_credit","sso_subject":"customer-123","available_credits":250}
```

- **利用可能とは、確保後の金額です。**`available_credits` は、すべてのプロダクトを通じて、未精算の確保をすべて除いた金額です。
- **ここでは小数を使えます。**残高は小数になることがあり、課金可能な 1 クレジットに満たない残りも表示できます。確保と精算の金額は、常に整数のクレジットです。
- **スタジオには 1 つの数字だけが表示されます。**スタジオは、残高を一度だけデプロイメントの表示単位に変換し、ユーザーごとの利用枠の表示は隠します。残高を取得できない場合は、不明として表示されます。
- **競合は確保で決まります。**リクエストが同時に届いた場合、判断の基準になるのは、残高ではなく確保の呼び出しです。

## 配信と復旧
- Nodaro は、それぞれの精算をすぐに配信しようとします。失敗した場合は、1 分ごとに動くワーカーが、最大 1 時間のバックオフで再試行します。確認されていない精算は、配信されるまで保持されます。
- Nodaro が自動的にキャンセルするのは、一度も許可されなかった確保だけです。許可されて実行中のジョブは、通常のジョブの復旧に従います。
- **操作が未完了の間は、ウォレットの設定を削除したり、別のウォレットに向けたりしないでください。**トークンを再発行する場合は、同じウォレットの上で再発行してください。

## 料金の確認
`GET /v1/deployment-billing/pricing` は、あなたのウォレット自身の表示や計画のために、デプロイメントの価格表を返します。請求アカウントのセッション、またはその請求連携キーのいずれかが必要です。

| フィールド | 意味 |
| --- | --- |
| `creditCost` | モデルの基本料率です。 |
| `creditIdentifier` | 課金対象の識別子です。 |
| `pricingBasis` | 基本料率が前提とする内容です。 |
| `pricing` | テキストモデルについて、各操作の識別子です。テキストモデルは、デフォルト設定の `llm-chat` として一覧に載ります。 |
| `denomination` | クレジットの単位と、表示用の換算です。 |
| `purchaseOptions` | 標準のチャージ額を USD で示したもので、クレジット単価も含みます。別途の商業条件が適用される場合があります。 |

- メディアのバリアントとテキストの操作は、その時点で有効な価格に基づいて解決されます。価格が設定されていないバリアントは、エラーコードとともに `null` のままです。
- 設定、数量、長さ、実測の使用量によって、最終的な金額は変わることがあります。計上には、確保と精算の金額を使い、このリストにある基本料率は使わないでください。
- 変更を検出するには、`If-None-Match` を使った完全なリクエストを優先してください。`since` パラメーターは価格の行のタイムスタンプに従うため、実行時の設定のすべての変更を検知できるわけではありません。

## Frequently asked questions

### Nodaro の外部ウォレットとは何ですか？

あなたが運用するサービスで、すべてのプロダクトを通じた各顧客の共有予算を保持します。専用の Nodaro Cloud デプロイメントは、生成を実行する前にクレジットの確保をこのサービスに依頼し、その後に実際の金額を精算し、残高を読み取って表示します。

### 外部ウォレットを使えるのは、どの Nodaro デプロイメントですか？

請求アカウントを持つ、専用の Nodaro Cloud デプロイメントです。請求アカウントは、そのデプロイメントのすべての操作の代金を支払う、唯一のアカウントです。その顧客は、デプロイメントの SSO プロバイダーを通じてログインし、それによってウォレットに識別されます。

### ウォレットが時間内に応答しない場合、どうなりますか？

何も生成されません。タイムアウト、形式が正しくない応答、金額や ID の不一致、識別情報の欠落、拒否のいずれも、モデルが実行される前にリクエストを止めます。

### 同じ精算リクエストが 2 回届くことはありますか？

はい。配信は少なくとも 1 回（at least once）なので、あなたのウォレットは、精算が繰り返された場合、再度課金せずに、最初の結果を返す必要があります。繰り返しは Idempotency-Key ヘッダーで識別します。

### 確保に有効期限はありますか？

ありません。生成や人によるレビューは、どの HTTP リクエストよりも長くかかることがあるため、確保は Nodaro が精算するまで残り続けます。コールバックが遅れているという理由だけで、有効な確保を解放しないでください。
