この記事は「Elixir Advent Calendar 2023」●日目の記事です
東京にいるけどfukuokaexのYOSUKEです。
普段は 合同会社TheWaggle で教材開発・システム開発・研修講師などマルチに活動してます。
TheWaggleでは、Elixirを学びたい人を増やすためにコンシューマ向けにエンジニアスクールを開校しようと準備中です。プログラミング教室との違いは、エンジニアとして戦力になれる人の育成を目指してます。その為、Elixirの案件を増やす為にも今年から開発事業部を作り 自社プロダクトと他社プロダクトの作成を開始しました。
今期はアジャイルの開発とウォーターフォール形式の開発をそれぞれ一件ずつ着手したのですが、来期以降は完全にアジャイル開発のみに踏襲していく予定です。
教育系システム開発と教育コンテンツ開発には自信があります。
決済サービスはお好きですか?
と言うことで、つい先日 Square決済サービスで開発して、ローンチ前日にお客様の契約したSquareから取引できないとの返答が、えー!と言う事態が起こり、急遽、Stripe決済に切り替えると言うことで短期間で2つの決済システムをゴリゴリ書いておりました。
いや、最近はこういったサービスがあるので簡単に導入できるなんて、楽勝ですねw と思ってたらStripeのWebhookでハマったのでハマった箇所と回避策を備忘録として書いておきます。
余談ですが、なぜSquareがNGだったのか?
ちなみに、既出の課題で知る人は知っている事と思いますが、Squareが取引不可となった理由は、CtoCサービスの決済を構築しようと見えてしまった事でした。実際には、BtoBtoCのビジネスサービスですが、CtoCと勘違いされてしまったようです。ただし、一度勘違いされたら申し立ても再考もされません。
クレジットの信用調査でNGになるので、どうすることもできません。
おとなしく、決済サービスを変える他ありません。と言うことでStripeに変更する事にしました。
StripeのPaymentLinkを利用した決済サービスの構築
Stripeの決済サービス構築は、stripity_stripe のモジュールがあるのでとても簡単でした。
が、しかし! Webhookのところで少しハマって色々調べ、最終的には「stripity_stripe」のモジュールの中まで読んで、それでも原因が掴めず、一旦、他の言語(Nodejs)で実装して動くことを確かめてもうまくいかないというお悩みを抱えて、結果、エンドポイントをいじって解決しました。
エンドポイントに追加
Webhookでハマったのは、 ヘッダーにstripeからの送信を保証する用途として送られてくる 「stripe-signature」 を受け取り、シークレットキーと 一緒に送られてくるBodyの要素を照らして、この送信は問題ないか確認するところで、「:OK」ルートに辿り着けない現象に悩まされてました。
最後にわかった、原因
試行錯誤でたどり着いたのは、Plugでbodyの要素が生データではなくなっている事に気がつき、解決策は、Endpoint時点でカスタムプラグを作って、生データをassignで突っ込んで渡しました。
# Stripe Webhook用 カスタムプラグ
plug :put_raw_post, only: ["/webhook/v1"]
def put_raw_post(conn, opts) do
case conn.path_info do
["webhook", "v1"] ->
{:ok, raw_body, conn} = Plug.Conn.read_body(conn, opts)
Plug.Conn.assign(conn, :raw_body, raw_body)
_ ->
conn
end
end
これで、controller側で実装した際に受け取った生データを参照してstripeからの送信だと判定してその後の処理をするということが解決できました。
気がつかなんだー、最後にそんな実装すら必要なかったというオチw
さて、最後にめでたしめでたしと思っていた所、実は上記のようなことは一切不要だったというオチがついてきます。
実は、 pipe_through [:api]
こいつが邪魔してただけ。
scope "/webhook", TestWeb do
pipe_through [:api]
post "/v1", Api.StripeWebhookController, :handle
end
ということで、以下のようにすれば、わざわざ カスタムプラグを作らなくてよかったというオチです。
scope "/webhook", TestWeb do
post "/v1", Api.StripeWebhookController, :handle
end
ただ、このおかげで、色々と理解が深まったので、良い勉強になりました。w