Hibariya

Stripe の Subscription 機能を Docker だけで素早く試す

Stripe の ドキュメント によると、この機能を使っていわゆるサブスクリプション型の課金の仕組みを実装できる。公開されている 参考実装 には、Netflix のような毎月固定額の支払いに対応する例がある。ドキュメントだけでも十分に分かりやすいものの、これを実際に手元で動かすことで、何がどうやって動いているのかさらに理解が深まった。手元で動かすには Docker さえあれば大丈夫。

前提条件:

  • 手元のマシンに Docker がインストールされていること。
  • Stripe のアカウントを持っていること。
  • 参考実装で使う商品 (Product) として Basic と Premium を作成済みであること (手順)

ターミナル上でやること

全体を通して、シェル上で実行する操作は以下の通り。

# Stripe CLI のセットアップ
mkdir -p ~/.config/stripe
touch ~/.config/stripe/config.toml
docker run --rm -it \
  --volume ~/.config/stripe/config.toml:/root/.config/stripe/config.toml \
  stripe/stripe-cli login

# Stripe CLIで、飛んでくる webhook のリクエストをアプリケーションサーバに転送する
docker run --rm -it \
  --volume ~/.config/stripe/config.toml:/root/.config/stripe/config.toml \
  --name stripe-cli \
  --publish 4242:4242\
  stripe/stripe-cli listen --forward-to http://localhost:4242/stripe-webhook

git clone https://github.com/stripe-samples/subscription-use-cases.git
cd subscription-use-cases

# アプリケーションの実行に必要な環境変数を設定する
cp -p .env.example fixed-price-subscriptions/server/ruby/.env
$EDITOR fixed-price-subscriptions/server/ruby/.env

# Stripe CLI と同じネットワークを使いつつ、アプリケーションサーバを起動する
docker run --rm -it \
  --workdir=/work \
  -v $(pwd):/work \
  --network container:stripe-cli \
  ruby:2.7.1 bash -c \
    "cd fixed-price-subscriptions/server/ruby && bundle && bundle exec ruby server.rb -o 0.0.0.0"

必要なコマンドはこれだけ。今回は Ruby の実装を使うので、Ruby の実行できるコンテナを用意する。また、仕組み上 Stripe からの webhook を受け取る口も必要なので、そのために Stripe CLI の Docker イメージも使っている。ひとつひとつは少し複雑なので、それぞれ順番に見ていこう。

Webhook を受け取れるようにする

参考実装のアプリケーションサーバは手元のマシンで動くので、Stripe から飛んでくる webhook のリクエストをどうにかして受け取れるようにする必要がある。そこで Stripe のコマンドラインツール Stripe CLI の出番になる。Docker イメージが提供されているので、すぐに使える。

mkdir -p ~/.config/stripe
touch ~/.config/stripe/config.toml
docker run --rm -it \
  --volume ~/.config/stripe/config.toml:/root/.config/stripe/config.toml \
  stripe/stripe-cli login

Stripe CLI はログイン時にブラウザを開こうとするものの、ブラウザの入っていないコンテナ上で実行しているので当然うまくいかない。ただ、失敗したタイミングで URL が表示されるので、それを使って自分で開けば問題ない。ログインが完了すると、~/.config/stripe/config.toml に認証情報が書き込まれる。

出力されているURLを使ってログインする

準備ができたので、早速 webhook を待ち受ける。

docker run --rm -it \
  --volume ~/.config/stripe/config.toml:/root/.config/stripe/config.toml \
  --name stripe-cli \
  --publish 4242:4242\
  stripe/stripe-cli listen --forward-to http://localhost:4242/stripe-webhook

これによって webhook のリクエストはすべて http://localhost:4242/stripe-webhook に転送される。ここで転送先URLに localhost を使うことができるのは、次のステップでアプリケーションサーバを走らせるときにこのコンテナと同じネットワークスタックを使うためだ。また、そのためにあらかじめコンテナに名前を付けている。これによって、別のコンテナを走らせるとき --network container:stripe-cli と既存のコンテナを指定して同じネットワークスタックを使うことができる。

コンテナが起動すると、whsec_ で始まる webhook secret が出力される。これは次のステップで使う。

アプリケーションサーバを起動する

参考実装を GitHub から clone しよう。冒頭でも述べたように、ここでは Ruby の実装を使う。

git clone https://github.com/stripe-samples/subscription-use-cases.git
cd subscription-use-cases

アプリケーションサーバを起動するには、いくつかの環境変数を設定する必要がある。アプリケーションは .env から変数を読み取るので、リポジトリのルートにある .env.example.env という名前でアプリケーションサーバのディレクトリにコピーする。お好みのエディタで環境変数を設定する。

cp -p .env.example fixed-price-subscriptions/server/ruby/.env
$EDITOR fixed-price-subscriptions/server/ruby/.env

STRIPE_WEBHOOK_SECRET には前のステップで手に入れた webhook secret を設定する。その他の値は Stripe のダッシュボードで確認できる。

Keys: ダッシュボードの開発者ページ

Prices: 商品ページの詳細

最後に、ruby コンテナの中で依存ライブラリをインストールしつつアプリケーションサーバを起動する。前述の通り、--network container:stripe-cli で Docker に Stripe CLI のコンテナと同じネットワークスタックを使うように知らせる。アプリケーションをホスト側のブラウザから開くため、-o 0.0.0.0 もここでは必要になる。このオプションによって、Sinatra に全てのネットワークインターフェースを listen するように知らせている。

docker run --rm -it \
  --workdir=/work \
  -v $(pwd):/work \
  --network container:stripe-cli \
  ruby:2.7.1 bash -c \
    "cd fixed-price-subscriptions/server/ruby && bundle && bundle exec ruby server.rb -o 0.0.0.0"

これで localhost:4242 を手元のブラウザで開き、subscription の機能が動作している様子を確認できるようになったはずだ。この参考実装では、subscription の作成、変更、キャンセルなどを試すことができる。Stripe CLI の出力は特定のイベントがどのタイミングで起きるかを知るのに便利なので活用しよう。

Tokyo Rubyist Meetup で2年ぶりに発表した

4/3 の回で Docker について話した。英語の発表は2年ぶり2回目。業務で Docker を使って開発をしてきた中で、こういうとこが良くて、こういうところが面倒だった、みたいな感じの内容。

Using Docker for your Rails Development Environment - Tokyo Rubyist Meetup | Doorkeeper

Tokyo Rubyist Meetup で話すと、Paul さんの手厚いサポート (リハーサルとかスライドのレビューとか) によって自分の変な英語が次々とあぶり出されていくので勉強になる。ありがたい。

全体的に、話が伝わるかどうかを重視して準備した。最近気になっていたこととして「(第二言語話者としての) 自分のスピーチの内容は、思ったよりも相手に伝わってないんじゃないか」というのがある。テキストベースの一対一の会話と比較してみると、スピーチはちょっと難易度が上がる感じがする。話し手と聞き手の関係が一対多になって、方向はほぼ一方通行、テキストみたいに読み返したりもできない。それに加えて話し手が第二言語で話すとなると、正しく伝わる情報はさらに減りそう。Google 翻訳が一瞬で意味の通る日本語に変換してくれるその英文を、生身の人間が一回聞くだけで理解できるのかどうか、なんとなく不安になる。というわけで、RubyConf 2018 での Daniel Azuma の発表動画Saron Yitbarek の発表動画、松田さんプロダクトの README (Himl など) を参考にしつつ、今まで自分があまり意識していなかったと思われる次の点に気をつけることにした。

  1. 重要な部分は言い換えて繰り返す
  2. 新しい話題はなるべく問いかけるかたちで始める
  3. ひとつの文節をなるべく単純に、短かくする
  4. 子音をしっかり発音する

うまくやれたのかは正直よく分からない。今回は話し方をチェックするために録画してみたんだけど、前回の録画はないので比較できないのが残念。見えている範囲では楽しんでもらえたように思えるのと、フィードバックとして、普通に理解できたよ、前より発音良くなった、といった声をもらえたのは素直に喜びたい。次の機会があったら、今度は Q&A もう少しなんとかしたい。

Tokyo Rubyist Meetup / 読んだ本

Tokyo Rubyist Meetup で発表してきた

4/19 の回で GraphQL について話してきた。「GraphQL は、Web API でありがちな諸問題への解決策のひとつとして良いもので、こんな感じで使います。」という内容。

Paul さんに発表しないかとメールで誘ってもらったのがきっかけ。その後、せっかくなので会場の提供も永和ですることになった。英語が下手でも問題ないか確認したところ「英語で発表したことがない人に登壇できる場所を提供したい」から大丈夫とのこと。

準備のために大量の英文を書いた結果、思ったことをその場で文にするのがずっと楽になった。録音した自分の発音を確認する作業を繰り返したおかげか、聞き取れる程度には発音できるようになったらしい。とはいえ、まだ満足に会話できるレベルには色々足りなくて、発表後の質問タイムではいくつかの質問にうまく答えられなかった。これは次の機会までの課題になりそう。もうしばらくお勉強と実践を繰り返していくつもり。

これからの「正義」の話をしよう

これまで何となく「正しいだろう」と素通りしてきたことについて、新たな視点が得られたような。最後あたりの話はちょっと難しい。

ゴドーを待ちながら

「ゴドーを待つんだ。」「ああそうか。」

こういうの好きだ、と思いながらささっと読了。その時代の背景を知っているともっと面白く読めるんだろうかと発行年を調べたら1952年。第二次世界大戦が終わったり、太宰治が入水して数年後。

3月上旬のようす (ゆるデ部/GraphQL/How We Learn)

ゆるデ部 meetup 64回

前回で初参加して、今回が二回目。

前回 音の鳴るシェル を作るために Pure Data で SE を作ってるんですがこういうのに使えるゲームの効果音みたいな素材ないんですかね、というようなことを言ったら ザ・マッチメイカァズ のようなまさに探していた感じの素材のありかを教えてもらう。「ダンジョンの壁に当たったとき」みたいな簡単な音を作るのさえけっこう手間だったので、ここで聞けてよかった。

効果音が揃ったのでそれなりに楽しくなったものの、もう少し面白くできないかなあと思ってしばらくほったらかしにしてた状態で2回目に参加。ドラムとか使ったシンプルな BGM があって、シェルを操作したときの音をそれにうまく合わせられると気持ちいいんじゃないかと言われてなるほどと思う。音ゲー的な感じだろうか。似たコンセプトのゲームに Rez というのがあるらしい。PS4買ったらやってみようかなあ。

GraphQL

年始から少し気になっていた GraphQL がおもしろくて API の実装を試してみたりしている。何が必要かをクライアント側がクエリではっきり簡潔に記述できるのいいなあ。スキーマから最低限の API ドキュメントが生成できるのも助かる。あと GraphiQL べんり。いいとこたくさん。でもサーバ側の実装はそんなに簡単じゃないかも。

作った API の使い心地を確認するために、いくつかクライアント側の実装も試してみた。Relay とか Apollo は色々やってくれて便利な反面、既にあるものを置き換えるにはそれなりに大きな変更が必要そうに見えた。その点 Lokka は単純な GraphQL のクライアントなので当たり前だけど自由度が高く、ちょっとした用途には使いやすかった。

参考にしたサンプルアプリは webpack でビルドできるものが多くて、試しに自分でも使いはじめてみたところけっこう楽ができてる気がする。

脳が認める勉強法 (How We Learn) を読んだ

記憶力や創造性についての事実が発見されていくまでのストーリー。その過程で行なわれた数々の実験についても詳しめに書かれていて面白かった。記憶の謎を解明するために何の意味もないたくさんの文字列を家族全員で学習する話とか、レム睡眠が発見されたときのエピソードとか。

学習の環境に変化をつけると何が変わるのか。学習前にテストを行なうことにはどれほどの意味があるか。アイデアの「孵化」はどのようにして起こるか。そんな感じのことが書いてある。巻末には TL;DR 的な Q&A が載っていて、実践的な内容としてはその数ページを読めば十分かも。

ここで読んだ孵化の話は アイデアのつくり方 にもあった気がする。

シェルでコマンドの実行前後をフックする

私達の使うアプリケーションは色々な音を出します。通知やエラーを知らせる効果音、たまにジングル (短かい音楽) を鳴らすものもあります。好みや事情によって無効にしている人も少なくないと思いますが、個人的には鳴らせるときには鳴らす方が好みです。なので、毎日使うアプリケーションのひとつであるシェルからも音が出ると楽しいのではないかと思います。例えば、コマンドを実行するときに効果音を出してみたり、失敗したとき ($? -ne 0) には悲しい感じの音が出るとか。もっと発展させて、状況に応じてリアルタイムにサウンドを作り出すとか。

そんな「音の鳴るシェル」作りの一環として、今回はコマンド実行の前後で音を出す方法を考えてみます。どうすればコマンドを叩くタイミングで任意の処理を実行できるのでしょう。1年くらい前に シェルを操作する 記事を書きました。この方法ではシェルの入出力を操作できますが、シェル上で実行されたコマンドの実行結果は得られません。そこで、シェルの機能を使ってコマンドの実行をフックし、コマンドの結果などを取得する方法を調べました。

fish

fish では イベントハンドラ というかたちでコマンド実行前後の処理を実装できます。function 定義にイベントを指定しておくと、イベントが発火されたタイミングでその function が実行されます。この仕組みを利用してコマンド実行前後をフックするには、組込みの fish_preexecfish_postexec イベントが使えます。

function my_preexec --on-event fish_preexec
  echo "preexec: $argv[1]"
end

function my_postexec --on-event fish_postexec
  echo "postexec: $argv[1] ($status)"
end

実行してみましょう。

$ uname
preexec: uname
Linux
postexec: uname (0)
$ hi
preexec: hi
fish: Unknown command 'hi'
postexec: hi (127)

ちなみに function 定義には --on-variable--on-signal というオプションもあり、値の変化やシグナルの受信を監視できて便利そうです。

function my_pwd_changed --on-variable PWD
  echo "PWD: $PWD"
end

function my_term_trap --on-signal SIGUSR1
  echo "SIGUSR1 received"
end

実行結果は次のようになります。

$ cd /tmp/
PWD: /tmp
$ kill -USR1 %self
SIGUSR1 received

zsh

zsh の場合は、add-zsh-hook でフックを登録できます。fish_postexec にあたるものは無いので、プロンプト表示前に実行される precmd を、コマンド実行後のフックとして代用しました。ここには実行したコマンドが渡ってくるわけではないので、もし必要ならばもう少し工夫が要りそうです。

autoload -Uz add-zsh-hook

add-zsh-hook preexec my_preexec
add-zsh-hook precmd my_precmd

my_preexec() {
  echo "preexec: ${1}"
}

my_precmd() {
  echo "precmd (${?})"
}

実行結果です。

% uname
preexec: uname
Linux
precmd (0)
% hi
preexec: hi
zsh: command not found: hi
precmd (127)

bash

bash では bash-preexec を使うと比較的簡単に実現できました。zsh と同様、コマンド実行後のフックは precmd で代用しています。実行結果は zsh の場合とほぼ同じなので省略します。

# https://github.com/rcaloras/bash-preexec
source ./bash-preexec.sh

preexec_functions+=(my_preexec)
precmd_functions+=(my_precmd)

my_preexec() {
  echo "preexec: ${1}"
}

my_precmd() {
  echo "precmd ($?)"
}

おわりに

私がよく使うシェルを対象に、コマンド実行前後をフックする方法について調べました。別の実現方法としては ptrace(2) や DTrace、trap(1) を駆使することで似たようなことができるかもしれません (試してない)。が、私の知っている範囲だとシェルを使うのが比較的シンプルなやり方だと思いました。もしもっと良いやり方があれば教えてください。