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

# Stripe 移行ツール連携ガイド

> 有効期限が近づいた Stripe サブスクリプションを Waffo へ移行する手順と、照会、解約、Sandbox 検証を説明します。

このガイドでは、Stripe 移行ツールを Java プロジェクトへ導入し、有効期限が近づいた Stripe サブスクリプションを Waffo へ移行する手順を説明します。

[Stripe 移行ツール](/docs/ja/developer-docs/tools-and-references/references/stripe-adapter)を読み、この移行方法がプロジェクトに合うことを確認してから進めてください。

## 前提条件

* Waffo とサブスクリプション契約を締結し、Sandbox の API キー、RSA 鍵ペア、加盟店番号を取得している。
* `stripe-java` が 24.11.0 以上である。
* Waffo 通知を受け取る公開 HTTPS エンドポイントがある。
* 使用する通貨が契約範囲に含まれている。`paymethodconfig/inquiry` で確認できる。

## ステップ 1：依存関係を追加

<Tabs>
  <Tab title="Maven">
    ```xml theme={null}
    <dependency>
        <groupId>com.waffo</groupId>
        <artifactId>waffo-java-stripe</artifactId>
        <version>0.2.0</version>
    </dependency>
    ```
  </Tab>

  <Tab title="Gradle">
    ```groovy theme={null}
    implementation 'com.waffo:waffo-java-stripe:0.2.0'
    ```
  </Tab>
</Tabs>

**プロジェクトで使用中の `stripe-java` バージョンは変更しないでください。** これは provided 依存関係です。`waffo-java` はアダプターから推移的に導入されるため、別途宣言する必要はありません。

<Note>
  導入時は [Maven Central](https://central.sonatype.com/artifact/com.waffo/waffo-java-stripe) で最新バージョンを確認し、`mvn dependency:get -Dartifact=com.waffo:waffo-java-stripe:<version>` で解決できることを確認してから `pom.xml` に追加してください。
</Note>

## ステップ 2：ルーティングクライアントを作成

`new StripeClient(key)` を `WaffoStripe.client(...)` に置き換えます。アダプター自身は Stripe のシークレットキーを保持しません。

```java theme={null}
import com.stripe.Stripe;
import com.stripe.StripeClient;
import com.waffo.stripe.WaffoStripe;
import com.waffo.stripe.config.WaffoConfig;

// 1. waffo-java の設定クラスで Waffo 認証情報を作成
com.waffo.types.config.WaffoConfig waffoJavaConfig =
        com.waffo.types.config.WaffoConfig.builder()
                .apiKey(System.getenv("WAFFO_API_KEY"))
                .privateKey(System.getenv("WAFFO_PRIVATE_KEY"))
                .waffoPublicKey(System.getenv("WAFFO_PUBLIC_KEY"))
                .merchantId(System.getenv("WAFFO_MERCHANT_ID"))
                .environment(com.waffo.types.config.Environment.SANDBOX)
                .build();

// 2. ルーティング設定
WaffoConfig routing = WaffoConfig.builder()
        .waffoConfig(waffoJavaConfig)
        .notifyUrl("https://your-app.example/webhooks/waffo")
        .onUnsupported(WaffoConfig.OnUnsupported.FAIL_LOUD)
        .build();

// 3. Stripe キーは stripe-java の通常の方法で設定
Stripe.apiKey = System.getenv("STRIPE_SECRET_KEY");

StripeClient client = WaffoStripe.client(routing);
```

アダプターは Waffo 向けリクエストへ `X-Waffo-Client: waffo-stripe-java/<version>` ヘッダーを自動で設定します。独自にトランスポート層を包んでこの値を作る必要はありません。

<Tip>
  **移行中は `FAIL_LOUD` を使用してください。** 既定の `FALLBACK` はルーティングできない作成リクエストを Stripe へ転送するため、移行漏れを見つけにくくなります。問題をすべて確認した後、運用時の安全策として `FALLBACK` に戻してください。
</Tip>

タイムアウトやプロキシを設定した既存の `StripeClient` がある場合は、`WaffoStripe.client(routing, existingClient)` でその設定を維持できます。

## ステップ 3：移行対象へタグを追加

既存のパラメーター生成に `metadata` を 1 行追加します。

```java theme={null}
SessionCreateParams params = SessionCreateParams.builder()
        .setMode(SessionCreateParams.Mode.SUBSCRIPTION)
        .setSuccessUrl("https://your-app.example/subscription/success")
        .setCancelUrl("https://your-app.example/subscription/cancel")
        .addLineItem(SessionCreateParams.LineItem.builder()
                .setPrice("price_123")
                .setQuantity(1L)
                .build())
        .putMetadata("source", "waffo")
        .build();

RequestOptions options = RequestOptions.builder()
        .setIdempotencyKey(persistedSubscriptionRequest)
        .build();

Session session = client.checkout().sessions().create(params, options);
redirect(session.getUrl());
```

この例では `uiMode` を意図的に設定していません。Stripe の既定値はリダイレクト型で、移行ツールも未設定をリダイレクト型として扱います。これにより `stripe-java` 24.11.x、32.x、33.x で同じコードを使用できます。32.x または 33.x の `HOSTED_PAGE` に置き換えないでください。そのシリアライズ値 `hosted_page` は非リダイレクト型と判定されます。

### 有効期限が近づいた Stripe サブスクリプションを Waffo へ移行

元の Stripe サブスクリプションから現在の支払い済み期間の終了時刻を取得し、`handoffAt` とします。既存の Stripe フローで更新を `handoffAt` に停止してください。移行ツールは元の Stripe サブスクリプションを変更しません。

Waffo へルーティングするリクエストでは、同じ時刻を `billing_cycle_anchor` に設定し、`proration_behavior=none` を明示します。

```java theme={null}
long handoffAt = stripeSubscription.getCurrentPeriodEnd();

SessionCreateParams params = SessionCreateParams.builder()
        .setMode(SessionCreateParams.Mode.SUBSCRIPTION)
        .setSuccessUrl("https://your-app.example/subscription/success")
        .setCancelUrl("https://your-app.example/subscription/cancel")
        .addLineItem(SessionCreateParams.LineItem.builder()
                .setPrice("price_123")
                .setQuantity(1L)
                .build())
        .setSubscriptionData(SessionCreateParams.SubscriptionData.builder()
                .setBillingCycleAnchor(handoffAt)
                .setProrationBehavior(
                        SessionCreateParams.SubscriptionData.ProrationBehavior.NONE)
                .build())
        .putMetadata("source", "waffo")
        .build();
```

| 時刻                 | 動作                                                                                                                     |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| `handoffAt` より前    | ユーザーが Waffo Cashier でカード入力と必要な 3DS 認証を完了する。初回料金はまだ請求しない                                                                |
| 待機中の照会             | `status=active`。`billing_cycle_anchor` と `current_period_end` は `handoffAt` と一致し、`metadata.waffo_current_period=0` になる |
| `handoffAt` 到達時    | Waffo が初回請求を自動的に開始する。ユーザーの再操作は不要                                                                                       |
| `handoffAt` より前の解約 | `Subscription.cancel("wsub_…")` が Waffo サブスクリプションを即時解約し、予定された初回請求を停止する                                                 |

<Note>
  `billing_cycle_anchor` は Waffo `startTime` に正確に変換されます。設定可能な最大期間は Waffo バックエンドが検証し、移行ツールは 365 日または 366 日の制限をハードコードしません。`trial_end` または `trial_period_days` と同時に設定しないでください。この組み合わせはマッピングエラーとして扱われます。
</Note>

<Warning>
  **冪等キーは 32 文字以内にしてください。呼び出し前に生成して永続化し、再試行でも同じキーを使用します。**

  アダプターはこのキーを Waffo の `subscriptionRequest` として使い、作成結果の確認とサブスクリプションの重複防止を行います。32 文字を超える業務注文 ID を直接渡さないでください。現行バージョンは長いキーを不可逆な 32 文字のダイジェストへ変換するため、Webhook から元の値を復元できません。既存の注文 ID が長い場合は、32 文字以内の安定した関連付けキーを別途生成し、注文 ID との対応を永続化してください。

  `stripe-java` が自動生成する冪等キーに依存しないでください。アダプターは Stripe の通信層が自動キーを生成する前にリクエストをインターセプトするため、そのキーは Waffo の `subscriptionRequest` になりません。`RequestOptions` でキーを明示的に渡さない場合、次回の呼び出しで永続化して再利用することもできません。
</Warning>

作成後の id は次のようにルーティングされます。

* `Session.id` は `wcs_` で始まり、`client.checkout().sessions().retrieve("wcs_…")` で取得できます。
* `session.getSubscription()` は対応する `wsub_…` サブスクリプション id を返し、`client.subscriptions().retrieve("wsub_…")` で Waffo へ自動ルーティングされます。
* 作成後、`wsub_…` サブスクリプション id と業務注文 ID の対応を永続化してください。Webhook 処理ではこのサブスクリプション id から業務レコードを取得し、冪等キーから注文 ID を逆算しないでください。
* Stripe の `sub_…` と `cs_…` は引き続き Stripe へ送られます。

## ステップ 4：Webhook 通知を変換

設定した `notifyUrl` で `handle(...)` を呼び出します。SDK は署名検証、解析、イベント変換を行い、Waffo が配信確認に使用する確認レスポンスも生成します。

```java theme={null}
import com.stripe.exception.StripeException;
import com.stripe.model.Event;
import com.waffo.stripe.net.WaffoStripeWebhooks;
import com.waffo.stripe.net.WaffoStripeWebhookResult;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;

WaffoStripeWebhooks webhooks = new WaffoStripeWebhooks(waffoJavaConfig);

@PostMapping(value = "/webhooks/waffo", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> onWaffo(@RequestBody String body,
                                      @RequestHeader("X-SIGNATURE") String signature) throws StripeException {
    WaffoStripeWebhookResult result = webhooks.handle(body, signature);   // 署名検証、変換、確認レスポンス生成

    if (result.getPaymentNotification() != null) {
        subscriptionPaymentHandler.handle(result.getPaymentNotification().getResult());
    }

    Event event = result.getEvent();
    if (event != null) {
        existingStripeWebhookDispatcher.dispatch(event);
    }

    // SDK は Web フレームワークに依存しないため、ここで Spring のレスポンスへマッピング
    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_JSON)
            .body(result.getResponseBody());
}
```

### SDK が生成した確認レスポンスを返す

`handle(...)` が返す `WaffoStripeWebhookResult` には、確認レスポンスの本文が含まれています。SDK は Web フレームワークに依存しないため、Spring の `ResponseEntity` は直接返しません。Spring では次の 2 項目をマッピングし、別の Web フレームワークでは同等のマッピングを行います。

| 要件                               | 詳細                        |
| -------------------------------- | ------------------------- |
| レスポンス本文                          | `getResponseBody()` を直接返す |
| `Content-Type: application/json` | `text/plain` を返さない        |

このレスポンスは、エンドポイントが通知を受信したことを Waffo に伝えます。`"ok"` に置き換えると、Waffo は成功結果として認識できず、同じ通知を再送します。

署名検証に失敗した場合は、ビジネス処理を実行せず、セキュリティイベントを記録してサブスクリプション照会で状態を突合してください。

### イベント対応表

| Waffo 通知                                   | 変換後の Stripe イベント                                        |
| ------------------------------------------ | ------------------------------------------------------- |
| `SUBSCRIPTION_STATUS_NOTIFICATION`         | `customer.subscription.created` / `updated` / `deleted` |
| `SUBSCRIPTION_PERIOD_CHANGED_NOTIFICATION` | 初回と各更新の `invoice.paid` / `invoice.payment_failed`       |
| `REFUND_NOTIFICATION`                      | `charge.refunded`。返金失敗時は `refund.updated`               |

変換後のデータオブジェクトは、通常どおり `event.getDataObjectDeserializer().getObject()` で取得できます。

次の通知は変換されず、`getEvent()` は `null` を返します。

* `PAYMENT_NOTIFICATION`：期間変更通知ですでに `invoice.paid` / `invoice.payment_failed` が生成されるため、二重計上を避けます。`getPaymentNotification()` から別途取得し、`paymentInfo.productName` でサブスクリプション課金と 1 回払いを区別してください。
* `SUBSCRIPTION_CHANGE_NOTIFICATION`：初期リリース対象外のプラン変更です。
* 終端状態ではない返金通知。

<Warning>
  **`checkout.session.completed` で提供処理を行うプロジェクトは改修が必要です。** このイベントは変換されません。サブスクリプションの有効化と権限付与を `customer.subscription.created` と `invoice.paid` に移し、ビジネスレベルの冪等性で二重付与を防いでください。
</Warning>

<Note>
  従来の `translate(body, signature)` はソース互換性のため残っていますが、戻り値は `Event` だけで、上記の確認レスポンスを取得できません。**新規連携では `handle(...)` を使用してください。**
</Note>

## ステップ 5：即時解約を組み込む

移行ツールは Stripe の既定の解約呼び出しを使った Waffo サブスクリプションの即時解約に対応します。期間終了時解約、指定時刻での解約、サブスクリプション更新は再現しません。

まず両者の違いを整理します。

|          | Stripe                      | Waffo                                     |
| -------- | --------------------------- | ----------------------------------------- |
| 即時解約     | 対応                          | 対応                                        |
| 期間終了時の解約 | `cancel_at_period_end=true` | **対等な設定がなく**、即時解約のみ                       |
| 指定時刻での解約 | `cancel_at`                 | 非対応                                       |
| 解約後の取り消し | 期間終了時解約は満了前に撤回できる           | 解約は最終状態                                   |
| 呼び出し口    | Stripe SDK                  | `client.subscriptions().cancel("wsub_…")` |

既定の解約は Waffo `subscription/cancel` を呼び出し、同じサブスクリプションを照会して解約済みの最終状態を確認します。結果が不明な場合、移行ツールは同じ `wsub_…` の照会だけで結果を回復し、Stripe へ転送しません。`handoffAt` を待っている間に解約すると、予定された初回請求は発生しません。

<Warning>
  `invoice_now=true`、`prorate=true` など追加の請求セマンティクスを持つ解約はエラーになります。Waffo には Stripe の期間終了時解約に相当する機能もありません。`cancel_at_period_end`、指定時刻での解約、解約の取り消し、または `Subscription.update("wsub_…")` に依存するフローは Stripe に残してください。
</Warning>

## 連携チェックリスト

### 依存関係と設定

* Maven Central から依存バージョンを解決できることを確認した。
* `stripe-java` が 24.11.0 以上である。
* `WaffoStripe.client(...)` と Sandbox 設定を組み込んだ。

### コード変更

* 対象リクエストに `metadata.source=waffo` があり、冪等キーを呼び出し前に永続化した。
* Webhook エンドポイントが `handle(...)` を使い、SDK が生成したレスポンス本文を返す。
* `PAYMENT_NOTIFICATION` をサブスクリプション課金として別に処理する。
* 提供処理を `checkout.session.completed` から `customer.subscription.created` と `invoice.paid` へ移した。
* `wsub_` サブスクリプションを照会して既定の即時解約を実行でき、期間終了時解約などの未対応機能に依存していない。
* 同じ `handoffAt` を使って Stripe の更新を停止し、Waffo `billing_cycle_anchor` を設定している。

### 検証

* `FAIL_LOUD` が示したすべてのフォールバックを確認した。
* プロジェクト自身のビルドとテストが成功した。
* Sandbox のエンドツーエンドフローを完了した。

## Sandbox 検証

検証は**プロジェクト自身の HTTP エンドポイント**から実行してください。アダプター内部のテストだけでは不十分です。

| 項目      | 確認内容                                                           |
| ------- | -------------------------------------------------------------- |
| 作成      | タグ付きリクエストが Waffo へルーティングされ、`wcs_` セッションと Waffo Cashier URL が返る |
| 支払い     | ブラウザーで実際の Cashier 支払いを完了する                                     |
| 照会      | `wsub_` サブスクリプションを取得でき、状態変換が正しい                                |
| 引き継ぎ待機  | カード入力と必要な 3DS 認証が完了し、照会フィールドで最初の請求期間が未開始と確認できる                 |
| 初回の自動請求 | `handoffAt` にユーザーの再操作なしで初回請求が発生する                              |
| 更新課金    | 更新通知を受信して正しく処理する                                               |
| 開始前の解約  | 待機中の `wsub_…` を即時解約し、初回請求が発生しないことを確認する                         |
| 解約機能    | 既定の即時解約が成功し、期間終了時解約に依存するフローは Stripe に残している                     |
| フォールバック | 条件に該当するリクエストが Stripe へ送られ、`metadata` の理由コードが正しい                |
| パススルー   | タグのないリクエストが移行前と同じように動作する                                       |
| Webhook | レスポンス本文と `Content-Type` が仕様どおりである                              |

***

## 設定リファレンス

連携では同名でパッケージが異なる 2 つの `WaffoConfig` を使用します。

### 1. ルーティング設定 `com.waffo.stripe.config.WaffoConfig`

| パラメーター          | 型                                    | 必須                | 既定値        | 説明                                                                         |
| --------------- | ------------------------------------ | ----------------- | ---------- | -------------------------------------------------------------------------- |
| `waffoConfig`   | `com.waffo.types.config.WaffoConfig` | Waffo へルーティングする場合 | `null`     | Waffo 認証情報。未設定では Stripe パススルーになり、タグ付き作成は `waffo_not_configured` でフォールバックする |
| `notifyUrl`     | `String`                             | Waffo へルーティングする場合 | なし         | Waffo が通知を送る公開 URL。ルーティングされたすべてのサブスクリプションに適用される                            |
| `onUnsupported` | `OnUnsupported`                      | いいえ               | `FALLBACK` | ルーティングできない作成リクエストの処理方法                                                     |

### 2. `OnUnsupported` の値

| 値              | 動作                                                                                                                                                            | 用途                                                                  |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `FALLBACK`（既定） | 作成リクエストを Stripe へ転送して通常どおり完了させ、戻り値の `metadata` に `waffo_routing=stripe_fallback` と `waffo_fallback_reason`、Waffo が明確な拒否コードを返した場合は `waffo_fallback_code` も設定する | 運用環境。ユーザーは常に支払えるが、Waffo を通らなかったサブスクリプションは `metadata` から事後に見つける必要がある |
| `FAIL_LOUD`    | Stripe へ転送せず、フォールバック理由を含む `StripeException` を送出する                                                                                                             | 移行期と結合テスト。ルーティングできないケースをすべて即座に洗い出し、1 件ずつ確認してから `FALLBACK` に戻す       |

<Warning>
  **このパラメーターが及ぶ範囲は限られています。** 制御するのは、送信前または明確な拒否を受けた時点でルーティング不可と判断できる 5 つのケースだけです——レッドラインに該当、Waffo による明確な拒否、支払い方法が未対応、フィールド変換の失敗、Waffo クライアント未設定。

  冪等競合とネットワーク上の不明状態の扱いには**影響しません**。それらは独立した処理に委ねられます（[ユーザーの二重支払いを防ぐためフォールバックしないケース](#ユーザーの二重支払いを防ぐためフォールバックしないケース)を参照）。
</Warning>

### 3. Waffo 認証情報 `com.waffo.types.config.WaffoConfig`

| パラメーター           | 型             | 必須  | 説明                                |
| ---------------- | ------------- | --- | --------------------------------- |
| `apiKey`         | `String`      | はい  | Waffo が発行した API キー                |
| `privateKey`     | `String`      | はい  | リクエスト署名用の base64 エンコードされた RSA 秘密鍵 |
| `waffoPublicKey` | `String`      | はい  | レスポンスと通知の署名検証用 Waffo RSA 公開鍵      |
| `merchantId`     | `String`      | はい  | 加盟店番号                             |
| `environment`    | `Environment` | はい  | `SANDBOX` または `PRODUCTION`        |
| `connectTimeout` | `int`         | いいえ | 接続タイムアウト                          |
| `readTimeout`    | `int`         | いいえ | 読み取りタイムアウト                        |

次のいずれかで作成できます。

<CodeGroup>
  ```java Builder theme={null}
  com.waffo.types.config.WaffoConfig cfg =
          com.waffo.types.config.WaffoConfig.builder()
                  .apiKey(System.getenv("WAFFO_API_KEY"))
                  .privateKey(System.getenv("WAFFO_PRIVATE_KEY"))
                  .waffoPublicKey(System.getenv("WAFFO_PUBLIC_KEY"))
                  .merchantId(System.getenv("WAFFO_MERCHANT_ID"))
                  .environment(com.waffo.types.config.Environment.SANDBOX)
                  .build();
  ```

  ```java 環境変数 theme={null}
  com.waffo.types.config.WaffoConfig cfg =
          com.waffo.types.config.WaffoConfig.fromEnv();
  ```

  ```java プロパティ theme={null}
  // springEnvironment は注入済みの org.springframework.core.env.Environment
  com.waffo.types.config.WaffoConfig cfg =
          com.waffo.types.config.WaffoConfig.fromProperties(springEnvironment::getProperty);
  ```
</CodeGroup>

### クライアント生成と Stripe 認証情報

| 生成方法                                 | パススルーとフォールバックで使う Stripe 認証情報                                      |
| ------------------------------------ | ----------------------------------------------------------------- |
| `client(WaffoConfig)`                | グローバルな `Stripe.apiKey` またはリクエスト単位のキー                              |
| `client(WaffoConfig, StripeClient)`  | 既存クライアントのキー、HTTP クライアント、タイムアウト、プロキシ                               |
| `client(WaffoConfig, String apiKey)` | 渡したキーをクライアント単位の認証情報として使う（上の行に `new StripeClient(apiKey)` を渡すのと同じ） |

優先順位は、**リクエスト単位の `RequestOptions` キー > クライアント単位 > グローバル**です。2 番目のオーバーロードが現在の `stripe-java` バージョンからクライアント単位の認証情報を読み取れない場合、クライアントを初期化できません。対応バージョンへ更新するか、API キーを明示的に渡すオーバーロードを使用してください。

### リクエスト単位のパラメーター

| パラメーター                  | 指定場所                  | 必須                | 説明                                                                          |
| ----------------------- | --------------------- | ----------------- | --------------------------------------------------------------------------- |
| `metadata.source=waffo` | `SessionCreateParams` | Waffo へルーティングする場合 | ルーティングタグ。ないリクエストは Stripe へパススルーする                                           |
| `idempotencyKey`        | `RequestOptions`      | 連携要件              | 32 文字以内。呼び出し前に生成して永続化し、再試行でも同じキーを使う。業務注文 ID が長い場合は、別の安定した関連付けキーを作成して対応を保存する |

## 未対応の Stripe 利用方法

初期リリースはサブスクリプション Checkout の一部だけを対象にします。以下は未対応の利用方法の一覧です。

「Stripe にフォールバック」と記載された行は決済に影響しません。作成リクエストは Stripe へ転送され、ユーザーは通常どおり支払えます。理由コードは戻り値の `metadata.waffo_fallback_reason` に、`waffo_routing=stripe_fallback` とともに記録されます。この 2 つと `waffo_fallback_code` はアダプターの予約フィールドなので、業務コードでは使わないでください。それ以外はエラーになるか動作しなくなるため、コード変更が必要です。

| Stripe の利用方法                                                                          | 結果と対応                                                                                                                         | 結果                           |
| ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| サブスクリプションではなく 1 回払いの商品を販売している                                                         | Stripe にフォールバック。1 回払いにタグは不要なので `source=waffo` を外す                                                                             | `not_subscription_mode`      |
| Cashier を自社ページに埋め込み、リダイレクトしない                                                         | Stripe にフォールバック。Waffo はリダイレクト型 Cashier が必要。`uiMode` の設定を外す（Stripe の既定はリダイレクト型）か、Stripe を使用する                                  | `non_hosted_ui`              |
| 1 つのサブスクリプションに複数の商品やプランを含める                                                           | Stripe にフォールバック。Waffo のサブスクリプションは単一金額なので、複数のサブスクリプションに分ける                                                                     | `multi_item`                 |
| 価格情報が不足している                                                                           | Stripe にフォールバック。`price` と `price_data` の少なくとも一方を設定する                                                                          | `missing_price`              |
| クーポンまたはプロモーションコードを使う                                                                  | Stripe にフォールバック。Waffo のサブスクリプションにクーポンの入力口はないので、割引を価格に反映する                                                                     | `has_discount`               |
| トライアルがあり、トライアル中にカードを収集しない                                                             | Stripe にフォールバック。Waffo は先にカードが必要なので、`payment_method_collection` を `always` にする                                                 | `trial_without_upfront_card` |
| 従量課金（使った分だけ支払う）                                                                       | Stripe にフォールバック。初期リリースの対象外                                                                                                    | `metered`                    |
| 段階価格（買うほど単価が下がる）                                                                      | Stripe にフォールバック。初期リリースの対象外                                                                                                    | `tiered`                     |
| サブスクリプションが複数フェーズに分かれる（例：最初の 3 期は別価格）                                                  | Stripe にフォールバック。固定の Waffo 期間として表現できない                                                                                         | `has_schedule`               |
| 決済通貨が Waffo の契約に含まれていない                                                               | Stripe にフォールバック。`paymethodconfig/inquiry` で契約が対応する通貨を確認する                                                                     | `currency_mismatch`          |
| ユーザーが選んだ支払い方法が Waffo で継続課金できない                                                        | Stripe にフォールバック。Waffo はカードと Alipay、WeChat Pay、GrabPay、KakaoPay、NaverPay、PIX に対応。カード系と個別ウォレットの併用や、Waffo が非対応の方法を含む場合もフォールバックする | `unsupported_payment_method` |
| 継続価格ではない、金額がユーザー入力、または価格を取得できない                                                       | Stripe にフォールバック。価格オブジェクトを確認する——アダプターはフォールバックを選び、誤ったリクエストを Waffo へ送ることはしない                                                     | `mapping_error`              |
| タグを付けたが Waffo の認証情報を設定していない                                                           | Stripe にフォールバック。`waffoConfig(...)` に `null` を渡していないか確認する                                                                      | `waffo_not_configured`       |
| Waffo がこの作成を明確に拒否した                                                                   | Stripe にフォールバック。`metadata.waffo_fallback_code` で調査する。最も多い原因はサブスクリプション業務が未開通                                                   | `waffo_rejected`             |
| サブスクリプションの変更：`Subscription.update("wsub_…")`                                          | アダプターでは非対応                                                                                                                    | 非対応                          |
| `invoice_now=true`、`prorate=true` など追加の請求セマンティクスを指定した解約                               | 移行ツールが対応するのは既定の即時解約のみ                                                                                                         | 非対応                          |
| 期間末での解約：`cancel_at_period_end` / `cancel_at`                                          | Waffo に対等な機能はありません。この機能に依存するサブスクリプションフローは Stripe に残してください                                                                     | 非対応                          |
| 期間、proration、一時停止、トライアル期間の変更                                                          | Waffo のサブスクリプション更新は金額（`amount`、`trialPeriodAmount`、`scheduledAmounts`）だけを変更でき、これらの項目は変更できない                                   | 意味が一致しない                     |
| Stripe の in-place proration によるアップグレード / ダウングレード                                      | Waffo のプラン変更は再作成型——新しい商品を伴い、Cashier を再度通る場合があるため、Stripe と同じ結果にはならない                                                           | 意味が一致しない                     |
| Waffo サブスクリプションの item を操作：`subscriptionItems.create/list(subscription="wsub_…")`      | Waffo は `si_…` のような item id を生成しないため、対応するオブジェクトが存在しない                                                                         | `InvalidRequestException`    |
| Waffo サブスクリプションからスケジュールを作成：`subscriptionSchedules.create(from_subscription="wsub_…")` | Stripe が Waffo サブスクリプションの課金周期を管理することになるため拒否される                                                                                | `InvalidRequestException`    |
| `checkout.session.completed` で提供処理を行う                                                 | 移行後はこのイベントが生成されず、提供処理が何も起こらないまま終わる。`customer.subscription.created` と `invoice.paid` に移す（[イベント対応表](#イベント対応表)を参照）               | 例外なし、処理が実行されない               |

既存の `si_…`、`sub_sched_…`、`sub_…` は Stripe 所有を示すため、関連操作はそのまま Stripe へ送られます。

<Tip>
  移行中は `onUnsupported=FAIL_LOUD` を維持してください。「Stripe にフォールバック」のケースも例外として表面化するため、1 件ずつ確認できます。すべて確認した後で、本番環境の安全策として `FALLBACK` に切り替えます（[設定リファレンス](#設定リファレンス)を参照）。
</Tip>

### 該当箇所を事前にスキャン

[AI 移行スキル](/docs/ja/developer-docs/tools-and-references/references/stripe-adapter#ai-移行スキルを使用する)のスキャナーは Stripe 呼び出しを次のように分類します。

| スキャン結果                             | 意味                               | 対応                 |
| ---------------------------------- | -------------------------------- | ------------------ |
| `PASS_THROUGH`                     | Stripe に残る                       | 変更なし               |
| `ROUTED_LIKELY`                    | 明確な未対応条件は見つからないが、ルーティング成功の保証ではない | Sandbox で確認        |
| `FALLBACK`                         | 「Stripe にフォールバック」の行に該当           | 許容するかパラメーターを変更     |
| `UNSUPPORTED`                      | それ以外の行に該当                        | コードを変更             |
| `REVIEW`                           | 自動判定できない                         | 値の組み立て元まで追跡        |
| `BLOCKED_PENDING_OWNERSHIP_REVIEW` | 作成フローの所有関係が未確定                   | 所有関係を決めるまでタグを追加しない |

スキャナーが判定できず、手動で追う必要があるのは主に次の点です。

* **パラメーターが別ファイル、ファクトリーメソッド、独自ラッパーで組み立てられている**——create に実際に渡る値まで追跡し、上の表と 1 つずつ照合する。
* **変更 / 解約の対象 id の由来が不明**——業務フローを辿って `wsub_` か `sub_` かを確認する。移行ツールは `wsub_` の照会と既定の即時解約に対応しますが、変更には対応しません。`sub_` はそのまま Stripe へ転送します。
* **`metadata` やイベント名が enum や定数で組み立てられている**——静的スキャンでは列挙しきれないため、タグが実際に付いているか手動で確認する。

<Warning>
  候補となるサブスクリプション作成と `SubscriptionItem` または `SubscriptionSchedule` の呼び出しが同じプロジェクトにある場合、まず各呼び出しがどの作成フローに属するか確認してください。複数 item、proration、またはスケジュールフェーズに依存するフローは、全体を Stripe に残します。所有関係を確認できるまで、候補の作成リクエストに `source=waffo` を追加しないでください。
</Warning>

<Note>
  スキャナーは正規表現とファイルコンテキストを使い、Java AST を解析しません。**出力は棚卸しであり、受け入れの判断材料にはなりません。** `ROUTED_LIKELY` は特に注意が必要です——明確なレッドラインが見つからなかったことだけを意味します。作成時、契約設定を取得できる場合はアダプターが通貨を事前確認し、取得できない場合は Waffo の作成 API が検証します。支払い方法はマッピングによる事前チェックのみで、契約に含まれるかどうかは同じく Waffo の作成 API が検証します。動的に組み立てられるパラメーターも実行時にしか分かりません。最終的な判断は [Sandbox 検証](#sandbox-検証) で行ってください。
</Note>

## ユーザーの二重支払いを防ぐためフォールバックしないケース

`onUnsupported` の設定にかかわらず、次のケースは Stripe へフォールバックしません。

**冪等競合またはネットワーク上の不明状態。** Waffo がすでにサブスクリプションを作成している可能性があるため、同じ冪等キーで照会します。既存サブスクリプションが見つかれば成功として返します。確認できない場合は自動フォールバックに対応しないため、照会して最終状態を確認してください。

**Waffo の明確な拒否。** 同じキーで照会し、サブスクリプションが存在しないことを確認できた場合だけ理由コードに従ってフォールバックします。不明な結果では自動フォールバックに対応しません。

目的は、ユーザーへの二重請求を防ぐことです。

## バージョン互換性とリリース検証

`stripe-java` は provided 依存関係で、バージョンはプロジェクト側が決めます。

|        | バージョン                   |
| ------ | ----------------------- |
| サポート下限 | 24.11.0。これより前のバージョンは非対応 |
| 検証済み上限 | 33.x                    |

各 `waffo-java-stripe` リリース前に、固定した 14 個の `stripe-java` 安定版で決定論的テストと Sandbox 回帰を実行します。変更履歴は [CHANGELOG](https://github.com/waffo-com/waffo-stripe/blob/main/CHANGELOG.md) を参照してください。

## 関連リソース

* [Stripe 移行ツール](/docs/ja/developer-docs/tools-and-references/references/stripe-adapter) — 機能と適用判断
* [Webhook 署名検証](/docs/ja/developer-docs/webhook/signature-verification) — Waffo 通知の署名方式
* [冪等性](/docs/ja/developer-docs/core-concepts/idempotency) — Waffo の冪等キー設計
* [エラーコード](/docs/ja/developer-docs/tools-and-references/developer-tools/error-codes) — Waffo エラーコードの意味を確認
* [GitHub リポジトリ](https://github.com/waffo-com/waffo-stripe) — ソースコードと変更履歴
