Elixir で決済サービスをつくってみた

8f085184e37717b9b8fbdd923c0f8d18?s=47 enerick
November 30, 2019

Elixir で決済サービスをつくってみた

Developer Boost 2019 の発表資料です

8f085184e37717b9b8fbdd923c0f8d18?s=128

enerick

November 30, 2019
Tweet

Transcript

  1. 3.

    ⾃⼰紹介 橋本広⼤(@enerick) 株式会社ミクシィ ID・ペイメント事業部 2016年4⽉⼊社
 → XFLAG ID の開発・運⽤
 →

    幻のモバイル事業 → 新規の決済系サービス「6gram」や
  社内決済基盤の開発・運⽤を⾏っている
  2. 5.
  3. 9.

    決済サービス基礎概念 決済システムについて ⼊⾦⽅向でやること ‣ ユーザーが登録するクレジットカードや銀⾏⼝座情報の管理 ‣ 決済リクエストの送信 ‣ 加盟店として、カード会社や銀⾏への売上計上 出⾦⽅向でやること

    ‣ 発⾏したプリペイドカードの管理 ‣ ユーザーの与信管理 ‣ 決済リクエストの受信、許可/拒否応答(オーソリゼーション) ‣ 利⽤された各加盟店から計上される売上に基づいて精算
  4. 10.

    決済サービス基礎概念 決済システムについて ⼊⾦⽅向でやること ‣ ユーザーが登録するクレジットカードや銀⾏⼝座情報の管理 ‣ 決済リクエストの送信 ‣ 加盟店として、カード会社や銀⾏への売上計上 出⾦⽅向でやること

    ‣ 発⾏したプリペイドカードの管理 ‣ ユーザーの与信管理 ‣ 決済リクエストの受信、許可/拒否応答(オーソリゼーション) ‣ 利⽤された各加盟店から計上される売上に基づいて精算 6gramでは⼊⾦・出⾦の両⽅向で
 決済ネットワークとの接続システムから
 プリペイドカードのオーソリゼーション・精算システムまで
 すべて内製しました
  5. 12.

    なんで内製? 決済システムについて 「そもそもパッケージ製品って何をしてくれるんだろう…?
   接続仕様書はどのみち必要らしいし、作ってみたらわかるのでは? 」 ある程度想像はしていたが、⼤変なことも多かった ‣ ⽂字通りの仕様書の⼭(紙のみ・複写禁⽌) ‣ 開発利⽤できる試験環境が無い

    ‣ パッケージを使わないため⼯程マシマシの試験、など ちなみにパッケージを利⽤してもこれらの全てから逃れられるわけではない しかし予定スケジュール通り⼊⾦・出⾦両⽅向の接続が完了 サーバーレス PCI DSS 環境も保たれ結果的に後悔はない…!
  6. 13.

    6gram では Elixir をふんだんに利⽤しています 決済システムについて Account アカウントや属性、認証情報の管理 Payment 決済に関連する情報の管理 Gateway

    決済ネットワークとの接続・通信の管理 その他 決済基盤ダッシュボード、6gram 運⽤のための管理ツール
  7. 15.

    Elixir ってどんな⾔語? Erlang VM 上で動作する Erlang/OTP との互換性がある どことなく Ruby inspired

    な⽂法 マクロによる拡張性や Protocol による多相性などを加えている 
 builderscon 2019 での ABEJA の⽯川さんの発表がオススメ ‣ builderscon tokyo 2019 - Elixir: Under the Hood - Qiita
 https://qiita.com/ishikawa@github/items/3d054f3d29920107687f Elixir での Web 開発について
  8. 16.

    Elixir の良いところ・悪いところ Elixir での Web 開発について Developer Summit 2019 での

    gumi の幾⽥さんの発表がオススメ ‣ 私が愛する Elixir/Erlang の楽しさと⾟さ - Speaker Deck
 https://speakerdeck.com/cooldaemon/erlang-falsele-sisatoxin-sa わかりみ深い… がせっかくなので⾃分の感覚でもいくつか取り上げてみる
  9. 17.

    Elixir の良いところ Elixir での Web 開発について ‣ Erlang VM の安定性、耐障害性

    ‣ Erlang/OTP のライブラリや資産がそのままつかえる ‣ コア周辺の開発・コミュニティが活発 ‣ 公式ドキュメントがちゃんとしている ‣ パターンマッチ便利 ‣ ⾮同期・並列処理が簡単にかける ‣ 細かい便利機能が⾊々備わっている
  10. 18.

    便利なパターンマッチ Elixir での Web 開発について 基本的なパターンマッチ iex> {:ok, result} =

    {:ok, 13} {:ok, 13} iex> result 13 iex> {:ok, result} = {:error, :bad_request} ** (MatchError) no match of right hand side value: {:error, :bad_request} (stdlib) erl_eval.erl:450: :erl_eval.expr/5 (iex) lib/iex/evaluator.ex:257: IEx.Evaluator.handle_eval/5 (iex) lib/iex/evaluator.ex:237: IEx.Evaluator.do_eval/3 (iex) lib/iex/evaluator.ex:215: IEx.Evaluator.eval/3 (iex) lib/iex/evaluator.ex:103: IEx.Evaluator.loop/1 (iex) lib/iex/evaluator.ex:27: IEx.Evaluator.init/4
  11. 19.

    便利なパターンマッチ Elixir での Web 開発について 強いパターンマッチ 先頭 2bytes に値の⻑さ、その後ろにその⻑さ bytes

    分の値を持ち、
 その後ろにはまた同じ構造を持つようなバイナリ列をパースしたい 8 "Content1" 4 "Hoge" 2bytes 8bytes
  12. 20.

    便利なパターンマッチ Elixir での Web 開発について 強いパターンマッチ 先頭 2bytes に値の⻑さ、その後ろにその⻑さ bytes

    分の値を持ち、
 その後ろにはまた同じ構造を持つようなバイナリ列をパースしたい iex> bin = <<0, 8, "content1", 0, 4, "hoge">> iex> <<len::16, val::binary-size(len), rest::binary>> = bin <<0, 8, 99, 111, 110, 116, 101, 110, 116, 49, 0, 4, 104, 111, 103, 101>> iex> val "content1" iex> rest <<0, 4, 104, 111, 103, 101>>
  13. 21.

    便利なパターンマッチ Elixir での Web 開発について 強いパターンマッチ 先頭 2bytes に値の⻑さ、その後ろにその⻑さ bytes

    分の値を持ち、
 その後ろにはまた同じ構造を持つようなバイナリ列をパースしたい iex> bin = <<0, 8, "content1", 0, 4, "hoge">> iex> <<len::16, val::binary-size(len), rest::binary>> = bin <<0, 8, 99, 111, 110, 116, 101, 110, 116, 49, 0, 4, 104, 111, 103, 101>> iex> val "content1" iex> rest <<0, 4, 104, 111, 103, 101>> パターン中で束縛した値をパターン中で使える。つよい
  14. 22.

    便利なパターンマッチ Elixir での Web 開発について ちなみに 同様のパターンで「ある値と同じ値」も簡単なパターンで表現できる iex> {n, n}

    = {:ok, :ok} {:ok, :ok} iex> {n, n} = {:ok, 100} ** (MatchError) no match of right hand side value: {:ok, 100} (stdlib) erl_eval.erl:450: :erl_eval.expr/5 (iex) lib/iex/evaluator.ex:257: IEx.Evaluator.handle_eval/5 (iex) lib/iex/evaluator.ex:237: IEx.Evaluator.do_eval/3 (iex) lib/iex/evaluator.ex:215: IEx.Evaluator.eval/3 (iex) lib/iex/evaluator.ex:103: IEx.Evaluator.loop/1 (iex) lib/iex/evaluator.ex:27: IEx.Evaluator.init/4
  15. 23.

    細かい便利機能たち Elixir での Web 開発について self-contained なリリースファイル⽣成 mix release コマンドでランタイムコミコミのリリースを作成してくれる

    2019年6⽉ にリリースされた v1.9 から標準搭載 ‣ アプリケーションサーバーに Erlang や Elixir のランタイムのインストールの必要 なし リリースファイルのコマンドオプションでスクリプト実⾏も出来るので、バッチ処理 も1バイナリでまかなえる ‣ 6gram の精算処理はこれを利⽤してアプリケーションサーバーと同じイメージを 使って精算バッチを実⾏している
  16. 24.

    細かい便利機能たち Elixir での Web 開発について ETS (Erlang Term Storage) で

    in-memory データ保存 ノード内に閉じたデータの保持をしたい場合に外部システムを要さずに
 ETS という OTP 組み込みのデータストレージが利⽤できる ‣ 直接 ETS をつかわなくても Elixir の Registry などからつかうこともできる ‣ 6gram では起動時にデータ読み込みが必要なモジュールで読み込み結果の保持な どにも気軽に利⽤している
  17. 25.

    細かい便利機能たち Elixir での Web 開発について 簡単に⾮同期処理、並列処理が書ける Task で気軽に関数の⾮同期実⾏ができる ‣ 結果を気にしないタイプの⾮同期処理でも

    Task.Supervisor を使ってプロセスに紐 付けるのを推奨 ‣ 紐付けておくと、アプリケーションの exit シグナルをトラップして実⾏が中断さ れないようにしたりもできる Flow で気軽にリストなどに対して並列 map などができる ‣ ノード内での並列度のコントロールや処理の待ち合わせなどもできる
  18. 26.

    Elixir の悪いところ Elixir での Web 開発について ‣ 動的型付けなので動かすと壊れてた・バグってたケースがままある ‣ よく⾔われているが

    Erlang VM の速度⾃体は速くはない ‣ Binary の操作はいろいろ整っている⼀⽅ Bitstring の操作がわりと貧弱 ‣ 公式の SDK やライブラリは無い場合が多く、コミュニティ頼み or ⾃作 ‣ ⼈気ライブラリでも放置気味なものも…
  19. 27.

    Elixir でよくある動的型付け⾔語っぽいミス Elixir での Web 開発について だいたい Map や Struct

    アクセス時 ‣ Map アクセス時の key の型違い(string/atom) ‣ Struct アクセス時の typo(コンパイルフェーズで気づけない) ‣ nil に dot access、など ほとんどはユニットテストで検出可能 しかし、テスト⽤のモック実装と現実の実装の間で返り値に差があったりすると… ‣ それでも staging 環境で気付ける ‣ 常⽤開発環境がない外部システムなら接続試験で…
  20. 28.

    Elixir でよくある動的型付け⾔語っぽいミス Elixir での Web 開発について Map アクセス時の key の型違い(string/atom)

    iex> map = %{hoge: 100} iex> map.hoge 100 iex> map = map |> Jason.encode! |> Jason.decode!
 iex> map.hoge ** (KeyError) key :hoge not found in: %{"hoge" => 100} JSON を介したりするとついやってしまいがち atom は GC されないので、string → atom は雑にできない
  21. 30.

    bitstring の操作⽀援が貧弱 Elixir での Web 開発について binary 向けオペレータは基本つかえない… が頑張ればなんとかなる #

    ݁߹ͷྫ iex> <<1, 2>> <> <<3>> <<1, 2, 3>> iex> <<1::1, 2::1>> <> <<3::1>> ** (ArgumentError) argument error (stdlib) eval_bits.erl:101: :eval_bits.eval_exp_field1/6 (stdlib) eval_bits.erl:92: :eval_bits.eval_field/3 (stdlib) eval_bits.erl:68: :eval_bits.expr_grp/4 (stdlib) erl_eval.erl:481: :erl_eval.expr/5 (iex) lib/iex/evaluator.ex:257: IEx.Evaluator.handle_eval/5 (iex) lib/iex/evaluator.ex:237: IEx.Evaluator.do_eval/3 # ΦϖϨʔλΛ͔ͭΘͣʹ݁߹ iex> << <<1::1, 2::1>>, <<3::1>> >> <<5::size(3)>>
  22. 32.

    決済システム開発で便利だったこと Elixir での Web 開発について Erlang のエコシステムに乗っかれるのはやはり便利 6gram における ISO8583(クレジットカード取引のプロトコル)処理は

    Erlang ⽤ラ イブラリをベースに実装している ‣ Bitmap の処理や中間表現のみを利⽤し、フィールドの定義や変換処理は⾃前実装 ‣ こういう拡張性のある実装のライブラリは Elixir には存在しなかった
  23. 34.

    決済システム開発で便利だったこと Elixir での Web 開発について ログやスタックトレースの表⽰をコントロールできる PCI DSS 的にカード番号や CVC

    などがログにでるのはインシデント ‣ 標準の Logger が拡張性あるので JSON ログするついでにフィルター関数も呼ばれ るように拡張 ‣ v1.8 で導⼊された Custom Inspect Optionを利⽤するとより事故を回避しやすく ‣ CVC は3桁程度のため、カード番号のように正規表現で⼀括フィルタしづらく、
 どうやってエラー時などにもログに出さないようにするか悩ましかった ‣ Inspect Option 付の Struct に⼊れて引き回すことで不意の内容展開時も安⼼
  24. 35.
  25. 37.
  26. 38.

    なんで Elixir? Appendix すでに弊社の中では採⽤実績も複数ある選択肢だった 弊社で最初に Elixir を採⽤したのはリアルタイム通信を必要とするゲームのバックエ ンド 発⾜時点のメンバーが全員 Elixir

    経験者 個⼈的にも、⼊社以来サーバーサイドはほとんど Elixir を扱っていた ‣ 未経験者でもそれほど初期の学習コストが⾼くないことも知っていた 前プロジェクトの資産を活かせる 前プロジェクトも Elixir で、その資産も活かせるため⾃然な流れで採⽤