マルチサイトのブログサービスで有料オプションを用意して決済にpaypal apiを利用する
テーマのカスタマイズ年間1000円、10GB容量追加年間2000円
テーマのカスタマイズやディスク容量の追加を有料オプションとして提供する無料ブログサービスをwordpressのマルチサイトで開発する場合。
無料ブログの状態を0として、各ブログサイトがどのプランを選択中であるかの判断は、wordpressシステムで退会ページと退会処理を簡単に実装でエントリーしたように、update_option(Options API)を利用して各ブログサイトのwp_optionsにプラン情報を持たせた上で判定する方法を検討している。
update_option('plan' 1);
無料ブログがプラン0だとして、上のようにしてプランを1にすることはwordpressなら難しくない。本エントリーのテーマは、paypal決済と絡めて、update_optionをどのタイミングで実行するのかという点になる。
購入ボタンの作成
wordpressの管理画面にアップグレードページを作成し、その中にpaypalの購入ボタンを設置することになる。ボタンを設置するフォーム内にhiddenフィールドで
- 商品名
- 金額
- 売主買主のメールアドレス
などのプラン構成情報やメールアドレスなどを設定することができる。また、
- return
- cancel_return
- notify_url
などの購入後のリダイレクト先やpaypal画面から[戻る]のリンク先、そして、即時支払通知のリクエスト先URLの指定もhiddenフィールドから設定することが可能。
フォーム送信後の画面遷移
購入ボタンが設置されたフォームからhiddenフィールドなどを利用してプラン情報や商品情報を送信することが可能になることを書いた。フォームを送信すると画面遷移としては、
- 購入ボタンの画面
- paypalログイン画面
- 購入内容確認画面
- 購入完了画面
と遷移する。1番目と4番目はwordpress側(こちら側)の画面で、そのほかはpaypalの画面になる。そして、3から4へはリダイレクトによる遷移になっていて、そのリダイレクト先を決めるのが、returnフィールドになっている。3から4へのリダイレクト時には購入に関する多くの情報がまとめて返されてくるので、そのデータをwordpress側で処理することができる。
厳密には必須なのだと思うが、それらのデータをこのタイミングでデータベースに格納しておくことが推奨されると判断している。その理由として、この後に受信するipn通知で、txn_idの重複判定や金額や数量が合致しているか確認するための元データとして利用するという点があげられる。
購入ボタンを押したアクションから始まるこの一連の処理をpaypalのintegrationwizardで生成することができるようになっていて、以下のURLからダウンロード可能。
https://devtools-paypal.com/integrationwizard/
ただし、ウィザードで提供されるコードをすべて利用しなければならないわけではない。実際に、
- expresscheckout.php
- paypalfunctions.php
- review.php
だけで購入完了まで実装することも可能である。ウィザードで生成されるコードを開発中のサイトに最適化していくことになるのでウィザードのコードを利用するというよりも参考にする資料と考えたほうが良いかもしれない。
上で示した画面遷移だが、別の例として以下の構成がある。
- 購入ボタンの画面
- paypalログイン画面
- 購入内容確認 paypal側
- 購入内容確認 購入決定画面 wordpress側
- 購入完了画面
購入内容の確認を2度行っている画面遷移になるのだが、こちらの構成だと購入決定画面をwordpress側で作成できるので、より一般的なのかもしれない。
PDT
購入完了画面に遷移したタイミングでPDT処理の実装を行うことを検討したい。paypalからwordpress側に返されたデータをそのままpaypal側に返してその検証結果を受け取り注文の妥当性を検証するための仕組みで結果は、
- SUCCESS
- FAIL
のどちらかが返される。PDTには弱点があり、paypal側での注文完了とwordpress側へのリダイレクトのタイミングでユーザーが操作を終了してしまった場合に注文の検証ができないという懸念点がある。反面、PDTのタイミングでSUCCESSを取得することができれば、wordpress側で即時注文を反映した構成をサイトに反映できるという点もある。
今回の例でいえば、無料ブログを利用中のユーザーが有料オプションで10GBの容量アップをpaypalで決済したとする。その場合PDTでSUCCESSがレスポンスされたタイミングで、update_optionを実行しプランのフラグをアップデートすれば、即時10GBのディスク容量アップをブログサイトに反映することが可能となる。
上の方に書いたようにPDTの処理はpaypalからのリダイレクト処理が完了することが前提となっている。ユーザーがそれ以前に操作をやめてしまったり、通信が切れたりといったトラブルが生じた場合には、wordpress側で注文のデータを更新することができない。
そのため、IPNという仕組みが存在する。
IPN
PDTは画面遷移の中で生じる同期的な処理と説明されている。それに対してIPNは非同期な処理とされている。
IPNは非同期にwordpress側のURLにアクセスを行い、paypal側での決済状態の変更を知らせる仕組みになる。paypal側の状態変更とは、
- 支払いの成立
- 支払いの取り消し
- 払い戻し
のいずれかに状態を変更するとIPNで通知が行われる。wordpress側では、その通知を受け付けるURLが必要になり、その部分の実装をIPNリスナーと呼んでいる。IPNリスナーにリクエストを送信できてしまうのは、paypalだけではなくだれでも送信することができるが、送信された内容のポストバックを受けてデータを検証できるのはpaypalだけである。
paypalからの要求にポストバックして返される結果は
- VERIFIED
- INVALID
のいずれかになる。結果に応じてwordpress側でデータベースを更新することになる。
実装するためのコードについては、paypalのサイトでもサンプルコードが公開されているので、それをwordpressと統合する作業がメインになる。IPNリスナーでおすすめなのが、
https://github.com/Quixotix/PHP-PayPal-IPN
URLから入手できるIPNリスナーのPHPクラス。
PDTで検証したデータに対する更新処理は含まれていないが、
- SSLに対応している点
- 送信結果メールが見やすく整理されている点
などで、paypalで公開されているサンプルコードよりも扱いやすくなっている。
IPNの送信先URLと書いたが、そのリクエスト先は、
notify_url
で指定することができる。この値はexpresscheckout.phpの内部で定義してもよいが、wordpressのマルチサイトで利用する場合には、各ブログサイト専用のURLを用意して、購入フォームからhiddenで指定することもできる。
また、[ipn notify_url not called]などでweb検索するとsandbox環境で注文を入れているのに、paypalからipnリクエストが来ないというトラブルが多く確認できる。そのようなトラブルが少なくないことを把握しておいたほうがよいかもしれない。
paypalのインテグレーションは、英語の理解が無いと厳しい点も最後にプラスしておこう。