『図解 カードビジネスの実務 第2版』を読んだ

2019年の1Qに読んだ本シリーズ:

クレジットのすべてがわかる! 図解 カードビジネスの実務<第2版>

クレジットのすべてがわかる! 図解 カードビジネスの実務<第2版>

概要

全体的に浅く広く、クレジットカードやそれに類するデビットカードなどについて説明されています。

  • クレジットカードの成り立ちと仕組みについて
  • クレジットカードの事業を運営する企業での業務について
  • オーソリゼーションシステムなど対外系システムやセキュリティについて

二つ目は本のタイトルのとおりで、分量を割いて説明されています。Webサービス開発者としては三つ目が気になるところです。

なぜ読んだか

EC系Webサービスを開発/運用していると、クレジットカード決済機能を実装したり、クレジットカードについての問い合わせ調査したりします。断片的には知識がありますが体系立った知識がない気がしたので、広く浅く知識を得るために読みました。

メモ

いくつかおもしろポイントがあったので書いておきます。

クレジットカードのビジネスモデル

クレジットカードによる購入(割賦販売法的には包括信用購入あっせん)は

  • 「期限の利益」を消費者に、
  • 「販売促進」を加盟店に、

それぞれ提供することで利息や手数料を得るビジネスモデルということが明記されていました。ここで、期限の利益とは支払いの期限を遅らせることで利用者が得られる利益のことです。また、販売促進については、キャッシュレス決済の手軽さや各種ポイント制度、売掛が確実に回収できることから効果があるということになります。

日本のクレジットカード

日本のクレジットカードは1枚にさまざまな支払い方法を備えていますが、これは「ジャパニーズペイメントオプション」と呼ばれるもので、海外カードは支払い方法ごとにカードが分かれているのが普通らしいです。また、分割払いなどは信用販売に由来しています。

スキーム

スキームはブランドホルダー(VISA/MasterCardなど)、イシュア、アクワイアラからなるインターチェンジ取引が世界的には標準ですが、従来の国内ブランドはアクワイアラが存在せずイシュアが直接加盟店を開拓するケースがあります(オンアス取引)。加盟店管理のコストからスケールはしないようです。

対外系システム

オーソリシステムは基幹系システムとは独立しています。理由はオーソリシステムは24時間365日稼働のオンラインシステムである一方、基幹系はメンテやバッチで止まることがあることによります。

その他

  • 仕向け
    • カード番号からカード発行会社を特定し、決済電文を送ること
  • デュアルメッセージ
    • オーソリ電文と売上電文を別々に送ること
    • 対面決済でクレジットカードを利用する際のプロセスに由来
  • シングルメッセージ
    • オーソリ電文と売上電文を同時に送ること
    • デビットカードなどで普及
  • デポジットオーソリ
    • ホテルなどでチェックアウトまで金額が確定しないので利用枠すべてをオーソリすること
    • 確実に売上か取消をおこなわないとカードが使えなくなる

まとめ

クレジットカードの従来の広まり方や使われ方に起因して、いくつかの仕組みが存在していることがわかりました。この点ではWeb経由での利用だと存在意義がよくわからなかったので参考になりました。さらっと読み通しておくと便利かと思います。

『WEB+DB PRESS Vol.108』でスキーマ駆動Web API開発についての特集記事を執筆しました

このたび、2018年12月22日に発売された『WEB+DB PRESS Vol.108』の特集1「効率急上昇!スキーマ駆動Web API開発」の企画と執筆に携わりました。id:june29さん、id:ackintoshさん両名との共著です。

WEB+DB PRESS Vol.108

WEB+DB PRESS Vol.108

  • 作者: 中野暁人,山本浩平,大和田純,曽根壮大,ZOZOTOWNリプレースチーム,権守健嗣,茨木暢仁,松井菜穂子,新多真琴,laiso,豊田啓介,藤原俊一郎,牧大輔,向井咲人,大島一将,上川慶,末永恭正,久保田祐史,星北斗,池田拓司,竹馬光太郎,粕谷大輔,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/12/22
  • メディア: 単行本
  • この商品を含むブログを見る

Web APIの仕様を表現するフォーマット(スキーマ)を用いたWeb API開発について、基礎的なところから紹介しています。本号はこの特集の他にもPostgreSQLやZOZOのシステムリプレイスなど、非常に興味深い内容が盛りだくさんなので、ご興味のある方はぜひ手にとっていただければと思います。

以上、宣伝でした。


以下、裏話的なことを書きます。

なぜ書いたのか

そもそも現在業務として携わっているWebサービスの開発でOpenAPI(いわゆるSwagger)を利用しています。ある程度わかっている人にOpenAPIについて知ってもらうにはWeb上にある次のようなリソースが役立ちます。

一方、新しくチームに入った人やそこまでWeb API開発に詳しくない人に対して、OpenAPIについて「読んどいて!」と言える文献があると、自分自身はもちろん、同じ境遇の他の人にも便利なものになるのではと感じていました。そして、REST APIのスキーマに関して最も有力な選択肢はOpenAPIなので、その説明を含めた文献があるとよさそうだと考えました。

また、これは執筆を始めたあとの話ですが、june29さんと「2018年時点でのWeb API開発のスナップショットみたいな記事だね」という話をして、それを意識して書くようにもしていました。

そういうわけで無事に企画が通り、今回寄稿することになったので、社内外で今後OpenAPIについて知ってもらいたいときは積極的にこの特集を勧めていこうと思っています。

補足

今回、OpenAPIに関しては最新版であるバージョン3を前提として、記法や活用方法を解説しました。OpenAPI 3がリリースされたのは2018年6月とまだ新しく、各種ツールのサポートも、Swagger公式やOpenAPI Generator以外の場合は特にですが、まちまちというのが現状です。そういう現状もあって、3章のサーバサイド開発のレスポンスバリデーションに関する部分は、仕組みの解説としてライブラリを使わない素朴な実装方法について書きました。

ここに関しては、今後いろいろなツールでOpenAPI 3がサポートされていくだろうと思うので、とくに主戦場であるRuby/Railsまわりなどは私としても情報発信は継続していきます。また、特集内のコラムで紹介したCommitteeに関しては、@ota42yさんが中心となって対応を進めていただいているので、私自身も試しつつ貢献できる部分はしていきたいと思っています。

また、スキーマの活用という点で、Protocol Buffersからのサーバ/クライアント生成が特徴的なgRPCをなぜ取り上げていないのか、という点は指摘としてあるかなと思います。ここに関しては、今回の執筆陣がgRPCに関する知見をそれほど持っておらず書かなかったというのが正直なところです。最近はgRPC-WebなどブラウザからgRPCのサービスをコールできる技術も出てきているようなので、詳しい知見を持つ方々にぜひ執筆してほしいという気持ちです…!

おわりに

よろしくお願いします!

Stripe Sources APIにおける決済の抽象化

この記事はGMOペパボ Advent Calendar 2018の23日目の記事です。

GMOペパボのAdvent Calendarと言っておきながら、Stripeが提供しているWeb APIの話をします。

Stripeの決済用Web APIでは、sourceという概念を通じて多様な決済方法を抽象化して表現しています。このsourceの概念がEC系Webサービス*1を開発している人間としては興味深いものだったので紹介します。

sourceとは

Stripe公式のリソースは次のあたりのものです。

stripe.com

stripe.com

sourceは多様な決済方法を取り扱うために導入されている概念です。sourceの状態がchargeable(課金可能)になると、買い手に対してcharge(課金)することができます。

もう少し詳しく説明すると、sourceは買い手が決済するために使う「モノ」を抽象化した概念であると言えます。その具象としては、クレジットカードやACH(銀行振込のようなもの)があります。1回きりの決済としてsourceに対して直接課金することもできますし、買い手に対して複数回課金するために買い手にsourceを関連付けておくこともできます。

Stripeのドキュメントとcurlがあれば、sourceを用いたWeb API経由での決済実行をすぐに体験できるので、興味がある人はぜひ触ってみてください(以下リンク先の"Try Now")。

stripe.com

たとえばクレジットカードであれば、JS経由でStripeへ直接カード情報を送信後にsourceのIDを得たあと、次のようにAPIを叩くと買い手へのsourceの関連付けと課金を実行できます。

$ curl https://api.stripe.com/v1/customers -u <key> -d email=<customer email> -d source=<source ID>
$ curl https://api.stripe.com/v1/charges -u <key> -d amount=100 -d currency=usd -d cutomer=<customer ID> -d source=<source ID>

sourceを特徴づける四つの性質

Stripeはsourceに対して次のような四つの性質を定義しています。これらの性質を使ってさまざまなsourceを分類しています。

pull/push

決済時の資金の移動方法です。

  • pull
    • 買い手の同意を得たあとに売り手側が資金を引き落とせる
  • push
    • 買い手側が明示的に資金を送る操作が必要
    • 送金後にsourceがchargeableになる

flow

買い手が決済を承認するための行動フローです。これを通過するとsourceがchargeableになります。

  • none
    • なにもしなくてもOK
  • redirect
    • リダイレクト先の銀行のページなどに遷移して承認する
    • sourceがリダイレクト先URLを持つ
  • code verification
    • 銀行口座など送金元口座の所有者であることを検証する
  • receiver
    • 買い手が送金する
    • sourceに送金先情報を含む

usage

sourceが何回使えるかを表します。

  • reusable
    • 一度flowを通ったあとは追加でchargeできるsource
    • sourceをcustomerに関連付けて、customerに対して課金することで実現
  • single_use
    • 決済するたびに買い手の明示的な同意が必要なsource
    • sourceに対して直接課金する

synchronous/asynchronous

課金の結果がいつ得られるかを表します。

  • synchronous
    • success/failureが即時わかる
  • asynchronous
    • 結果が数日後にわかる
    • sourceの状態はpendingからsuccess/failureに遷移する

決済方法の分類

上述した性質を用いると、sourceの具象である決済方法は次のように分類できます。

  • クレジットカード
    • pull/none/reusable/synchronous
    • 買い手に紐づけておけば、売り手が何度も課金できる
  • ACH(銀行振込のようなもの)
    • push/receiver/reusable/asynchronous
    • 買い手の入金操作が必要、かつ数日かかる(らしい)

その他の決済方法についても、Stripeのドキュメントで表がまとめてあります

sourceの利点

このあたりから私見が入ります。Stripeのsourceの利点は次のようなところだと思います。

  • さまざまな決済方法を同じような扱いかたで扱えるようになる
  • 決済ごとの特徴を決めるための性質を定義することで、新決済に対応しやすくなっている

一つ目については、Stripeのドキュメントを見るとわかるとおり、いろいろな決済方法について利用方法が説明されているのですが、どの決済方法でも概ねsource作成 → sourceへの課金 → Webhookによる結果の確認、という流れになっています。決済が複数あることで複雑化しそうな部分を適切な抽象化で吸収し、統一的なインタフェースとして提供できていることがわかります。

二つ目については、このsourceを用いた決済の抽象化方法を使うと、新しく導入する決済をsourceのインタフェースに載せやすくなります。例えば、Amazon Payや楽天PayのようないわゆるID決済や日本でよく使われる*2後払いについて考えてみます。これらの決済をsourceの性質で分類すると、おおよそ次のようになると思います。

  • ID決済
    • pull/redirect/reusable/synchronous な決済
    • 買い手と外部サービスIDを関連付け、外部サービスに遷移して認証し、あとで追加課金でき(ることが多い)、すぐに決済結果がわかる
  • 後払い
    • push/receiver/single_use/asynchronous な決済
    • 買い手がコンビニなどから明示的に送金し、決済ごとに買い手の承認が必要で、与信に人手を介する場合は結果が出るのに時間がかかる

このように複数の性質を用いて特徴を決定することで、新しい具象に対しても、類似する性質の既存決済と同じフローに載せやすくなっています。

まとめ

StripeのWeb APIにおいて多様な決済をうまく扱うための概念であるsourceについて紹介しました。今回はちょっと間に合わなかったのですが、このようなWeb APIをサーバサイドで実装する際にデータモデリングがどうなるかを考えると、なかなかおもしろいのではと思っています。

なお、id:takatoshionoさんに教えてもらったのがきっかけで、私はこのStripeのWeb APIについて知りました。ありがとうございました!

"The Modular Monolith: Rails Architecture"を読んだ

Modular MonolithというアーキテクチャをRailsアプリケーションへ適用する記事を読みました。

medium.com

モノリスアーキテクチャとマイクロサービスアーキテクチャの中間に位置する、一つのモノリシックなアプリケーション内でドメインごとにモジュールに分解しつつ運用するためのアーキテクチャを、Railsでどのように実装するか、という内容です。

Modular Monolithとは

記事から引用します。

Rather than extracting microservices, we decided to first focus on making our app modular. Our goal was to identify good architectural boundaries before we extracted code out into independent services. This would set us up to be able to migrate to microservices in the future, by having the code structured in a way to make a smooth transition.

  • モノリスから複数のマイクロサービスを抽出するより、まずアプリ内をモジュラーにしていく
  • 独立したサービスとして抽出する前にアーキテクチャ上のよい境界を見つける。スムーズに移行しやすいようにコードを構成しておく

というものです。Railsで実現するための具体的な方法として次の項目が挙げられています。

  • app ディレクトリを持たず、コードはすべて gemsengines の下に置く
  • gems の下に置くコードははRailsに依存しないRubyのコードとなる
    • すべてステートレスであり、ストレージを使わない
    • Active Supportだけは使う
  • engines の下に置くコードはRailsに依存するRubyのコードとなる
    • mountable engineにする
    • Active Recordによってデータを永続化する
    • Action PackによってAPIやWebインタフェースを公開する

なぜModular Monolithを使うのか

rails new してRailsアプリケーションをふつうに作っていくとモノリスアーキテクチャになります。ユーザー管理、商品管理、決済など、さまざまなドメインの機能をまとめて app 配下などで管理しています。一方、ドメインごとにサービスを切り出して別のアプリケーションとして運用し、サービス間はWeb APIなどを通じて連携するマイクロサービスアーキテクチャも存在します。

Martin Fowler氏は、MonolithFirstという記事で、最終的にマイクロサービスにアーキテクチャを移行したいとしても、最初はモノリスから始めるのがよいと述べています。Monolith Firstではアプリケーションを成長させながらドメインの境界を見つけていき、またその必要があるならば、徐々にマイクロサービスとして切り出していきます。

Railsであれば、小〜中規模なアプリケーションならモノリスのままRailsの利点を活かしていくのがよいでしょう。成長して大きくなってきたアプリケーションは、モジュール間の依存が複雑になって変更時の影響範囲が読めなくなってきたりします。こういう場合は、まずモノリスの中をドメインごとにモジュールとして分解していくのが効果的といえ、今回読んだ記事ではModular Monolithを使うのがよいと述べています。

RailsにおけるModular Monolithアーキテクチャの実現

ここからは、記事で説明されているModular Monolithアーキテクチャを実現するための実装方法について、かいつまんで説明します。

コードを gemsengines に配置

前述したように、コードをすべて gemsengines の下に置きます。すべてgemとして構成するので、それぞれのディレクトリにはgemspecが存在します。Gemfileでは次のようにしてロードします。

Dir.glob(File.expand_path("../engines/*", __FILE__)).each do |path|
  gem File.basename(path), :path => path
end

Dir.glob(File.expand_path("../gems/*", __FILE__)).each do |path|
  gem File.basename(path), :path => path
end

やっていることは、enginesgems 配下のディレクトリについて gem メソッドで読み込むgemを指定しています。同一リポジトリ内にgemが存在するので path オプションを使っています。これを見るとわかるように、実質monorepoとなっています。記事では、プロダクトのコアとなるコードとgemのコードを同時に更新できるので、後方互換性が問題とならず便利であることを利点として挙げています。

モジュール構成のスタート地点

記事では、Admin, API, Domain というモジュールから始めたと述べています。AdminAPIDomain にそれぞれ依存する形です。ここから、Domain モジュールを分解していくのがよいだろうと述べています。

境界の遵守

Railsでgemをロードすると、あるクラスは他のクラスに自由にアクセスできてしまいます。これを防ぐために、Railsエンジンのテスト実行時にそのエンジン自体と依存先だけをロードできるようにGemfileの記述を工夫しています。テストはRailsエンジンのディレクトリで実行します。

if ENV["ENGINE"].nil?
  if Dir.pwd.split("/")[-2] == "engines"
    ENV["ENGINE"] = Dir.pwd.split("/").last
  end
end

Dir.glob(File.expand_path("../engines/*", __FILE__)).each do |path|
  engine = File.basename(path)
  gem engine, :path => "engines/#{engine}", :require => (ENV["ENGINE"].nil? || ENV["ENGINE"] == engine)
end

ENGINE 環境変数が設定されていなければ、Railsエンジン名を ENGINE 環境変数に保存しておきます。そして、engines 配下のディレクトリ名をトラバースしながら、ENGINE 環境変数を見て該当のRailsエンジンだけを require しています。この方法によって、あるモジュールが依存先として指定していないモジュールのクラスを使ってしまっているときはテストでエラーにできます。

元記事では、このロード方法をさらに発展させて、変更したgemとRailsエンジンだけテストが実行されるようにしていました。

循環依存の検出

Bundlerのおかげでモジュールの循環依存が検出できるという話です。

ある二つのモジュール間に循環依存があると、それらのモジュールは強く結合しており、実質一つのモジュールになっているといえます。Modular MonolithアーキテクチャでgemやRailsエンジンといったモジュールに分解することで、モジュール間に循環依存があると、それらのモジュールのロード時にBundlerがエラーとして検出してくれるようになります。

疎結合化のためのObserverパターン

Modular Monolithでは、モジュール間の依存方向に気をつける必要があることがわかってきました。モジュール間の依存方向を制御したいときにはObserverパターンが使えます。

# driving_scoreエンジンにある定期ジョブ
score = ScoringService.generate_score(user)
if score.eligible?
  QuoteService.generate_quotes(user)
end

上のコードは quoting (見積)エンジンの持つ QuoteService へ依存が発生しています。driving_score エンジンから quoting エンジンへの依存を作りたくない場合、次のようにすれば、モジュール間の依存を解消して疎結合にできます。

# driving_scoreエンジンにある定期ジョブ
score = ScoringService.generate_score(user)
if score.eligible?
  DRIVING_SCORE_PUB_SUB.publish(:eligible_score, :user_id => user_id)
end

# quotingエンジンにあるイベントsubscribe用コード
DRIVING_SCORE_PUB_SUB.subscribe(:eligible_score) do |user_id|
  QuoteService.generate_quotes(user_id)
end

driving_score エンジンでは eligible_score というイベントだけを発行し、だれがそのイベントを購読しているかに関心はありません。つまり、driving_score エンジンから quoting エンジンへの依存をなくすことに成功しています。

実際には、pub/subの実現にはKafkaやメッセージキュー用のミドルウェアなどが必要になります。私見としては、このあたりはRailsだとWhisperが使えそうだと思いました。

感想

独立したアプリケーションの機能をmountable engineに切り出すと便利というのはこれまでも言われていたことですが*1app 配下を廃してすべてのコードを gemsengines 配下に置くという割り切りや、マイクロサービスを意識して徐々にドメイン境界を見つけて切り出すという点が独特だと思いました。

また、書籍『マイクロサービスアーキテクチャ』では、モノリシックなアプリケーション内にモジュールを作ることは「実世界ではプロセス境界内でのモジュール分離の約束が守られてことはほとんどありません」*2と述べられています。この問題に対しては、Modular Monolithの設計方針であるgem/Railsエンジンへの分離やBundlerによる依存関係管理を用いた境界の遵守が、解決策の候補になるのではないかと思いました。

興味があれば元記事も読んでみてください。

*1:https://speakerdeck.com/kami/mountable-engine-for-small-team など

*2:『マイクロサービスアーキテクチャ』p.12(原文ママ)

このブログをHTTPS化した

はてなブログで独自ドメインを当てているときもHTTPS化できるようにとっくになっていたので、このブログもHTTPS化しました。

手順

こちらの記事を全面的に参考にさせてもらいました。

blog.jnito.com

まずはgemをインストールします。config.yml の準備もいい感じにやっておきます。

$ gem install hateblo_mixed_contents_finder

次に、mixed contentsが存在する記事を洗い出します。

$ hateblo_mixed_contents_finder validate_all
$ cat result.txt | cut -f 1 | sort -u > invalid_entries.txt # mixed contentsを含む記事のURLリストを出力する

ツールが invalid_entries.txt を参照してくれるので、再更新をかけます。

$ hateblo_mixed_contents_finder update_all

ふたたびmixed contentsがないかを確認します。

$ hateblo_mixed_contents_finder validate_all
$ cat result.txt | cut -f 1 | sort -u
http://blog.kymmt.com/entry/201601_read_booksq
http://blog.kymmt.com/entry/201602_read_books
http://blog.kymmt.com/entry/201603-read-books

ここまでやって、mixed contentsが残った記事のURLリストを出せたので、これらの記事については手作業でmixed contentsを取り除きました。具体的には、ベタ書きされたURLのプロトコルがHTTPだったので、次のように対応しました。

  • httphttps に置き換え
  • Amazonの画像URLは http://ecx.images-amazon.comhttps://images-na.ssl-images-amazon.com に置き換え

もう一度mixed contentsがないか確認たところ、そのような記事は見つかりませんでした。

$ hateblo_mixed_contents_finder validate_all
# ...
OK💚

最後に、はてなブログの設定画面からHTTPS配信を有効にしておしまいです。