Open6

stripe メモ

saksak

特にstripe connectを使った設計の備忘録を残しておく

saksak

入金処理について

  • 自動入金とマニュアル入金があるが(platform、connected accountどちらも)、締め概念を作りたい場合現状マニュアルで設計するしかない
    • ちなみにplatformは自動、connected accountはマニュアルという組み合わせは問題ないらしい(逆は仕組み上問題があるぽい、理由はちょっと忘れてしまった)
    • 決済手段自体を導入するのに比べてかなり工数かかるイメージなのでマニュアルで実装する場合は相応の工期を取るようにする
  • 大枠どのように設計するか
      1. stripeのbalance transactionから金額を計算するパターン
      • connected accountと紐づいてるbalance transactionを対象分取得(API or Sigmaで)して計算
      • 入金額がアカウントの残高以下であれば入金処理を実行する(パターン2の場合も同様)
      • メリット:ざっくりと(charge - platform_earning) - (refund + dispute - platform_earning_refund)とかで計算できる
      • デメリット:内部的にこの決済は含めないみたいな条件がある場合balance transactionだけだと集計できない
      1. 内部データから金額を計算するパターン
      • charge(payment intent使う場合は紐づくもの)、refund、dispute、balance transaction、application feeあたりはオンライン/バッチ処理組み合わせて取っときたい
      • 入金バッチのタイミングで対象のchargeやrefundやdisputeテーブルから入金金額を計算
        • balance transactionの中でchargeなどは比較的紐付けやすいがfeeなどをどう紐付けるか迷った、結果的にfeeは内部で決済時にカラムに入れることにしたので結局balance transactionもいらないのでは?となってきてる
      • メリデメはパターン1の逆

  • 最初から考慮しときたいこと
    • chargeだけじゃなくrefundやdispute(やtopupも使う予定あれば)が発生することを前提に金額計算の処理を作っておく
    • refundやdisputeで発生する損害をplatformとconnected accountのどちらが負担するのか規約含めて早めに決める
      • 例えばrefundの負担をどちらに持たせるかで↓のような入金額の差が発生する(¥100の商品、application fee10%、stripe fee3%、縦が時系列)
connected account platform stripe fee
¥90 ¥10 (¥3)
connected accountの損失による返金の場合↓
¥0(netで-¥10の損失) ¥0 (¥3)
platformの損失による返金の場合↓
¥0 ¥0(netで-¥3の損失) (¥3)
=> ということは入金処理をchargeとrefundの単純足し合わせで実装すると、
A. connected account側の損失の時は、(charge(ex. ¥100) - platform_earning(ex. ¥10)) - refund(ex. ¥100) 、これはconnected accountから見ると-¥10になる
※ 負の値になるので入金額全体も負になり入金処理ができない可能性もあり
B. platformの損失の時は、(charge(ex. ¥100) - platform_earning(ex. ¥10)) - (refund(ex. ¥100) - platform_earning_refund(ex. ¥10))、これはconnected accountから見ると¥0になる
  • 発生したchargeやrefundのデータは内部的にどう管理するか
    • 結果的にchargesテーブルを作ってSTIにしてcharges::stripe_chargeやcharges::stripe_refundみたいなtypeを作ることで一貫したインターフェースで操作できるような形にした(まだ運用にのせてないので今後変わるかも)
  • 売り上げフローと発生するbalance transactionを把握しておく
  • payment intentを使う場合は紐づくchargeをキャプチャ後に取得する
    • chargesと配列で取得できるが原則1つのchargeしか入ってこないとのこと(海外での決済の場合は複数
saksak
saksak

stripe webhookのコード例 (rails)

class StripeEventHandler
  def call
    payload = request.body.read
    event = Stripe::Event.construct_from(JSON.parse(payload, symbolize_names: true))
    class = 'StripeEvent::' + event.type.tr('.', '_').camelize + 'Handler'
    class.new.call(event)
  rescue JSON::ParserError => e
    render json: { status: 400, error: 'Invalid payload' }
  rescue Stripe::SignatureVerificationError => e
    render json: { status: 400, error: 'Invalid signature' }
  end
end


module StripeEvent
  class BalanceTransactionAvailableHandler
    def call(event) # メソッドはcallだけを持つ
      event&.data&.object # => Balance Transaction Objectが取得できる
    end
  end
end
saksak
  • transferのデータを取り込む理由
    • chargeに紐付けてtransferを取り込むことで、reverse_transferを意図的に発生させることが可能になる、reverse_transferをすることで例えばconnected accountから一部のお金を回収したいユースケースに対応することが簡単に実現できる
saksak

入金について追記