Stripe PHP SDK 顧客にデフォルトの支払い方法をアタッチする際の手順で詰まった

## 概要

Stripeの顧客にStripe.jsを使って追加したクレカ情報のPaymentMethodIdを紐づけてデフォルトの支払い方法に設定しようとしていた。一部でめっちゃ詰まったので簡単にまとめておく。

## 前提

本記事で紹介しているコードは「コピペすれば動く」というものではない。

## エラーになるコード

下記はイケてないコードである。

```php
/**
 * 顧客に紐づくデフォルトの支払い方法を更新
 *
 * @param UserObject $userObject
 * @param string $paymentMethodId
 * @return void
 */
private function updateDefaultPaymentMethod(UserObject $userObject, string $paymentMethodId): void
{
    $this->makeStripeClient();
    try {
        // NOTE: 顧客と支払い方法を紐づけ
        $this->attachPaymentMethodToCustomer(paymentMethodId: $paymentMethodId, userObject: $userObject);
        // NOTE: 紐づけた支払い方法をデフォルトに設定
        $this->setDefaultPaymentMethodToCustomer(paymentMethodId: $paymentMethodId, userObject: $userObject);
    } catch (ApiErrorException $e) {
        throw $e;
    }
    return;
}

/**
 * 顧客に支払い方法を紐付ける
 *
 * @param string $paymentMethodId
 * @param UserObject $userObject
 * @return PaymentMethod
 */
private function attachPaymentMethodToCustomer(string $paymentMethodId, UserObject $userObject): PaymentMethod
{
    $this->makeStripeClient();
    try {
        $this->stripeClient->paymentMethods->attach(
            $paymentMethodId,
            [
                'customer' => $userObject->getStripeCustomerId(),
            ]
        );
    } catch (ApiErrorException $e) {
        throw $e;
    }
}

/**
 * 顧客にデフォルトの支払い方法を設定する
 *
 * @param string $paymentMethodId
 * @param UserObject $userObject
 * @return void
 */
private function setDefaultPaymentMethodToCustomer(string $paymentMethodId, UserObject $userObject): void
{
    try {
        $this->updateCustomer(
            userObject: $userObject,
            options: [
                'invoice_settings' => [
                    'default_payment_method' => $paymentMethodId,
                ]
            ]
        );
    } catch (ApiErrorException $e) {
        throw $e;
    }
}
```

これを実行すると`The customer does not have a payment method with the ID pm_XXXXXXXXXXXXXX. The payment method must be attached to the customer.`のようなエラーが出てしまう。
実は原因は単純で、`setDefaultPaymentMethodToCustomer()`の`$this->updateCustomer()`で`default_payment_method`に紐づいて渡すPaymentMethodIdは`attachPaymentMethodToCustomer()`の`$this->stripeClient->paymentMethods->attach()`が返すPaymentMethodのidである必要がある。↑のコードはリクエストでもたらされたPaymentMethodIdを`default_payment_method`に紐づけて渡してしまっている。ここでエラーになる模様、、！

## 修正後のコード

下記だと正常に動作した。

```php
/**
 * 顧客に紐づくデフォルトの支払い方法を更新
 *
 * @param UserObject $userObject
 * @param string $paymentMethodId
 * @return void
 */
private function updateDefaultPaymentMethod(UserObject $userObject, string $paymentMethodId): void
{
    $this->makeStripeClient();
    try {
        // NOTE: 顧客と支払い方法を紐づけ
        $paymentMethod = $this->attachPaymentMethodToCustomer(paymentMethodId: $paymentMethodId, userObject: $userObject);
        // NOTE: 紐づけた支払い方法をデフォルトに設定
        $this->setDefaultPaymentMethodToCustomer(paymentMethod: $paymentMethod, userObject: $userObject);
    } catch (ApiErrorException $e) {
        throw $e;
    }
    return;
}

/**
 * 顧客に支払い方法を紐付ける
 *
 * @param string $paymentMethodId
 * @param UserObject $userObject
 * @return PaymentMethod
 */
private function attachPaymentMethodToCustomer(string $paymentMethodId, UserObject $userObject): PaymentMethod
{
    $this->makeStripeClient();
    try {
        $paymentMethod = $this->stripeClient->paymentMethods->attach(
            $paymentMethodId,
            [
                'customer' => $userObject->getStripeCustomerId(),
            ]
        );
    } catch (ApiErrorException $e) {
        throw $e;
    }
    return $paymentMethod;
}

/**
 * 顧客にデフォルトの支払い方法を設定する
 *
 * @param PaymentMethod $paymentMethod
 * @param UserObject $userObject
 * @return void
 */
private function setDefaultPaymentMethodToCustomer(PaymentMethod $paymentMethod, UserObject $userObject): void
{
    try {
        $this->updateCustomer(
            userObject: $userObject,
            options: [
                'invoice_settings' => [
                    'default_payment_method' => $paymentMethod->id,
                ]
            ]
        );
    } catch (ApiErrorException $e) {
        throw $e;
    }
}
```