【Laravel】PAYJPで決済する② (登録済みのカードも再利用)

えび

Laravelのプロジェクトでpayjpで決済する際に
以前使用したカード情報を再利用できるようにして
カード入力を省略化させてあげる

payjpの導入

以前の記事を参照

【Laravel】PAYJPで決済する① (新規決済のみ)

ユーザーがこれまで決済したカード一覧を取得する

  • PaymentController.php
public function index()
{
  $user = auth()->user();
  $cardList = [];
  
  // 既にpayjpに登録済みの場合
  if (!empty($user->payjp_customer_id)) {
    // カード一覧を取得
    \Payjp\Payjp::setApiKey(config('payjp.secret_key'));
    $cardDatas = \Payjp\Customer::retrieve($user->payjp_customer_id)->cards->data;
    foreach ($cardDatas as $cardData) {
      $cardList[] = [
        'cardNumber' =>  "**** **** **** {$cardData->last4}",
        'brand' =>  $cardData->brand,
        'exp_year' =>  $cardData->exp_year,
        'exp_month' =>  $cardData->exp_month,
        'name' =>  $cardData->name,
      ];
    }
  }
  
  return view('payment', compact('cardList'));
}

上記により、
既に顧客&カード登録済みの場合、$cardListにはこんなデータが入ってくる

array:1 [▼
  0 => array:5 [▼
    "cardNumber" => "**** **** **** 4242"
    "brand" => "Visa"
    "exp_year" => 2024
    "exp_month" => 12
    "name" => "TEST USER"
  ]
]

支払いフォームで登録済みのカード一覧を表示

  • payment.blade.php
@if (session('error-message'))
  <p>{{ session('error-message') }}</p>
@endif

<form action="{{ route('payment') }}" method="post">
  @csrf
  <script
    src="https://checkout.pay.jp/"
    class="payjp-button"
    data-key="{{ config('payjp.public_key') }}"
    data-text="カード情報を入力"
    data-submit-text="カードを登録する"
  ></script>
</form>

@if (!empty($cardList))
  <p>もしくは登録済みのカードで支払い</p>
  <form action="{{ route('payment') }}" method="post">
    @csrf
    
    @foreach ($cardList as $card)
      <div class="card-item">
        <label>
          <input type="radio" name="payjp_card_id" value="{{ $card['id'] }}" />
          <span class="brand">{{ $card['brand'] }}</span>
          <span class="number">{{ $card['number'] }}</span>
        </label>
        <div>
          <p>名義: {{ $card['name'] }}</p>
           <p>期限: {{ $card['exp_year'] }}/{{ $card['exp_month'] }}</p>
        </div>
      </div>
    @endforeach

    <button type="submit">選択したカードで決済する</button>
  </form>
@endif

cssは省略してるけど画面はこんな感じ

Controller側で登録・決済処理実装

  • PaymentController.php
public function payment(Request $request)
{
  if (empty($request->get('payjp-token')) && !$request->get('payjp_card_id')) {
    abort(404);
  }

  DB::beginTransaction();

  try {
    // ログインユーザー取得
    $user = auth()->user();
    // シークレットキーを設定
    \Payjp\Payjp::setApiKey(config('payjp.secret_key'));

    // ⭐️ 以前使用したカードを使う場合
    if (!empty($request->get('payjp_card_id'))) {
      $customer = \Payjp\Customer::retrieve($user['payjp_customer_id']);
      // 使用するカードを設定
      $customer->default_card = $request->get('payjp_card_id');
      $customer->save();
    // ⭐️ 既にpayjpに登録済みの場合
    } elseif (!empty($user['payjp_customer_id'])) {
      // カード情報を追加
      $customer = \Payjp\Customer::retrieve($user['payjp_customer_id']);
      $card = $customer->cards->create([
        'card' => $request->get('payjp-token'),
      ]);
       // 使用するカードを設定
       $customer->default_card = $card->id;
       $customer->save();
    // ⭐️ payjp未登録の場合
    } else {
       // payjpで顧客新規登録 & カード登録
       $customer = \Payjp\Customer::create([
          'card' => $request->get('payjp-token'),
       ]);
       // DBにcustomer_idを登録
       $user->payjp_customer_id = $customer->id;
       $user->save();
    }

    // ⭐️ 支払い処理
    // 新規支払い情報作成
    \Payjp\Charge::create([
         "customer" => $customer->id,
         "amount" => 100,
         "currency" => 'jpy',
    ]);

     DB::commit();

    return redirect(route('payment'))->with('message', '支払いが完了しました');

  } catch (\Exception $e) {
    Log::error($e);
    DB::rollback();

    if(strpos($e,'has already been used') !== false) {
      return redirect()->back()->with('error-message', '既に登録されているカード情報です');
    }

    return redirect()->back();
  }
}

ざっくり解説
  • 以前使用したカードIDを渡された場合
    → 自分に紐づく$customerを取得して、デフォルトカード(=支払うカード)としてそのカードIDを設定する
  • カード情報入力 & 既にpayjpには登録済みの場合
    → 自分に紐づく$customerを取得して、新しいカードを追加 & デフォルトカード(=支払うカード)としてそのカードIDを設定する
  • 新規の場合
    $customer及びカードを新規作成
  • 同じカード番号かつ同じ期限のカードはエラーが出るのでcatchして支払いフォームに表示してあげる