yikegaya’s blog

仕事関連(Webエンジニア)と資産運用について書いてます

Rails製サービスにStripeで決済機能を実装してハマったこと、迷ったところ

Ruby on RailsとReactで実装、運営しているSquareEightというサービスにStripeで決済機能を実装してみて得た知見をまとめます。

https://square-eight.net/ 仕事でStripeに触れたことはあったものの他のエンジニアが実装したコードのメンテナンスをしたのみだったのですが今回0⇨1で実装してみると色々と考えるポイントが多かったです。

SquareEightの決済機能要件

  • サービス事業者向けのユーザとエンドユーザ向けのユーザが存在する
  • サービス事業者はクレジットカードを登録してSquareEightの有料プランに加入できる
  • サービス事業者は銀行口座を登録してエンドユーザ向けにクレジットカード支払いに対応したEC予約ページ、ECページを作成できる
  • エンドユーザはクレジットカードを登録してサービス事業者にクレジットカード支払いができる
  • 回数券をクレジットカードで購入して予約の決済に使用することもできる
  • 月額サブスクリプションに加入して毎月加入日に支払いを自動請求することもできる
  • 実際に実装してみて迷った / ハマったポイント
  • Standardアカウントの種別にどれを選ぶか
  • Stripe決済履歴をデータベースに記録するべきか
  • StripeのProduct APIとSubscription APIを使うべきか
  • 本人確認書類の登録バグ
  • StripeのAPIを呼び出すときにversion指定しないとバグる
  • Standardアカウントの種別にどれを選ぶか
  • Stripeを使うにはStandard、Express、Customの3種類のアカウントタイプがあるがまずここの選択で迷いました。

調べた+実装してみた感じだと

  • Expressはまず日本では対応していないので考慮不要
  • StandardはStripe側で作成された画面を使い回しエンドユーザに直接Stripeとやりとりしてもらうので開発工数が少ない
  • 自分で独自のフォームを作たり子アカウントへの振込みタイミングを制御したりとできる限りカスタマイズしたい場合はCustom エンジニアとしてまあ実装 / 運用し切れる自信はあったので今回はCustomを選択しました。

Stripeのアカウントに関して参考にした記事

https://stripe.com/docs/connect/accounts?locale=ja-JP https://qiita.com/y_toku/items/7bfa42793801dfc5415d Stripe決済履歴をデータベースに記録するべきか CustomアカウントでStripe決済を実行するにはにはPaymentIntentというAPIを呼び出す必要があるのですがこのPaymentIntentの実行記録をStripeのサーバにGetリクエスト遅れば取得できるのでデータベースに記録しなくても成立するのでは

と思って試したんですが以下の理由でキツかったのでPostgreにPaymentIntentを記録するテーブルを作って結局全て保存することにしました。

テーブルを作った理由

他のテーブルとのRelationを作るのが辛い

 例えばどのユーザの決済なのか記録するのにデータベースへの記録なしで実現しようとすると決済実行時にmetadataという形でkey value型でユーザIDを記録する方法になるのですがこれがプログラム上扱いにくかったです。 API呼び出しに時間がかかる 決済履歴を呼び出すのに数秒待たされて決済履歴に関連した画面を開くのが重くなってしまいました

取得できる決済件数に制限がある

よくよくAPI Docみると100までしか取得できないようです。これはまずい。 StripeのProduct APIとSubscription APIを使うべきか StripeにはProductという商品の名前、料金などを登録してその商品に対して決済を要求できるProduct APIという機能や月額課金、週課金など定期課金を簡単に実装できるSubscription APIがあり採用検討したのですが結果不採用にしました。

不採用にした理由

  • PaymentIntentと同じくrelationを作るのがきつい
  • Subsctionから発行されたPaymentIntentをデータベースにうまく書き込めない
  • Subsctionから発行されたPaymentIntentをデータベースにうまく書き込めない どういうことかというとPaymentIntent実行後のデータベースへの書き込み処理は

  • PaymentIntent API呼び出して決済実行。この時StripeへのRequestに{merchant_user_id: 1}など他のデータベースとrelationを作るためのmetadata登録要求も含める

  • PaymentIntent APIを呼び出すとこちら側のRailsサーバーに結果がwebhookでpostされる。そのときに金額やmetadataに含まれた情報を読み取ってデータベースに登録する という風に決済履歴は全てwebhookから登録する実装にしたのですがここがSubscriptionから発行されたPaymentIntentだとmetadataが含まれないのでうまくいかない。。

というので自前で作ったbatch処理でPaymentIntent APIを呼び出す形に落ち着きました。

本人確認書類の登録バグ

StripeのCustomアカウントを使って実装する場合サービス事業者のユーザにStripeを有効化するには本人確認書類を提出してもらう必要がありますがここでハマりどころがあり

  • iPhoneから送信した画像がHEIC形式だがStripeはjpegpngしか受け付けてくれないのでエラーになる
  • 画像のサイズが大きすぎると登録できない ここは面倒ですが一旦こちらのRailsサーバでpostされた画像を「Rmagick」というGemで最適化してからStripeにpostする実装としました。このGemを使うとHEICをPNGにしたり画像サイズを縮小できます。

https://github.com/rmagick/rmagick - StripeのAPIを呼び出すときにversion指定しないとバグる 例えば以下はReactからpostされた銀行口座情報をRubyでStripeに登録のコードですがこのようにStripeのAPIを使う際に2行目のapi_version指定がないと謎にエラーになる場合がありました。

Stripe.api_key = Rails.configuration.stripe[:secret_key]
Stripe.api_version = '2022-08-01'
external_account = Stripe::Account.create_external_account(
  current_merchant_user.account.stripe_account_id,
  {
    external_account: {
      object: 'bank_account',
      account_number: account_params[:account_number],
      routing_number: account_params[:bank_code] + account_params[:branch_code],
      account_holder_name: account_params[:account_holder_name],
      currency:'jpy',
      country:'jp',
    },
  },
)

まとめ

クレジットカード決済代行サービスにはStripeの他にPay.jp、Paygent、DNP決済も一通り実装した経験があるんですがそれらに比べるとStripeはAPIが充実していて痒いところまで手が届く印象でした。

ただ多機能な一方複雑でハマりポイントも多くドキュメントもよくよく読み込まないと理解できない点はそれなりに多いように思います。