【本記事は、開発者向けの記事です】
当店は、資格試験の実施をオンラインで受け付けています。受験料の収受をオンラインで実施します。
先日、このシステムにLINE Payを実装しましたので、概略をこちらでご紹介します。
サーバはApache / PHP7 、LINE Pay APIを使用します。
LINE Payオンライン決済とは
LINE Payオンライン決済では、スマホQRコード決済の「LINE Pay」の決済機能を、対面ではなくオンラインで実行します。
スマホ / PC / タブレット等、どんなデバイスでもインターネットブラウザがあれば決済を開始できます。ただし、決済承認そのものは、当該LINEアカウントでログインされたスマートフォンで実行されるという特徴があります。
LINEアプリインストール・ログイン済み・LINEPay設定済みのスマホから決済をスタートすると、
ブラウザ→LINEアプリ→完了
パソコンなど、他のデバイスで決済をスタートすると、
PC → スマホ(LINEアプリ) → PC
という遷移をへて決済が完了します。
ですので、特徴としていえるのは、
最終的にLINEアプリを経由することから、LINEの水準でセキュリティが担保され、不正利用が非常に困難である
ということです。クレジットカードなどは番号だけで決済完了してしまうことを考えれば、非常に安全だといえます。
画面遷移が複雑なように見えますが、LINE Pay APIが自動的に遷移を制御するため、デベロッパー側ではAPIに必要なURLを設定するだけでOKです。
ただし、後述しますが、スマホでのアプリ間遷移がうまく制御できない部分もあり、デベロッパー側の実装により工夫しなければならない部分もありました。
LINE Pay 加盟店登録について
LINE Payオンライン決済を実装する前提として、加盟店登録が必要です。
検索エンジンで「LINE Pay」と検索して出る、下記のURLから、
右上にある「LINE Pay加盟店申請」をクリックして申請します。
【注意】店頭の対面決済とは別アカウントが必要です
すでに店頭でLINE Pay対面決済を行っている店舗の方は、そのまま利用できるように感じますが、そうではありませんので注意が必要です。
申し込みフォームのこちらの項目で、「オンライン」を選択して申し込んだアカウントのみが、オンライン決済を実行できます。
当店も、店頭用と、オンライン用の2つのアカウントを取得しました。
加盟しなくてもマニュアルは見られる
なお、加盟していなくてもマニュアルは「技術サポート」のリンクから見られます。
LINE Pay Developers | LINE Pay
この中の「Documentation」に、詳細なLINE Pay APIの仕様が書かれています。本記事では、その中の
LINE Pay 技術連動ガイド 日本語
を使用し、ページ数はそのページ数で記載します。(改訂などにより変わる場合があります)
LINE Pay オンライン決済の実装
それでは、本題の実装のお話をしていきます。
全体の流れ
LINE Pay APIを使用したオンライン決済のおおまかな流れは次の通りです。
決済Reserve API呼び出し
決済Reserve APIを呼び出し、LINE Pay APIサーバ内に取引情報を作成します。 (技術連動ガイド p.25)
Reserveでは、商品名・決済金額情報などを登録します。エラーなく登録が完了すると、
- 取引番号(transactionID)
- 決済承認用のURL(info.paymentUrl.web)
がResponseとして返却されますので、この決済承認用のURLにユーザーが遷移することにより、決済承認の画面に進みます。
ユーザーが決済承認用URLに移動
Reserveが成功したら、ユーザーを決済承認用URLに遷移させます。 (技術連動ガイド p.28) 通常のリダイレクトで構いません。
このURLがLINE特有のURLとなっており、LINEアプリが入っているスマートフォンで開くとLINEアプリが起動します。PCなどで開くと、LINEへのログインを促す画面になります。
遷移後、ユーザーはスマートフォン上でLINEアプリの画面を操作し、決済承認を完了させます。
決済承認の操作が完了すると、Reserveの際に加盟店側で指定したURLに遷移し、実際の決済を行います。
決済Confirm API呼び出し
ユーザーから決済承認をもらうと、LINE Pay側が加盟店が指定したURLを呼び出してくれます。このURLで、承認が下りた決済を実際に実行します。ここで実際に資金が移動します。(技術連動ガイド p.31)
Confirm URL遷移時に、URLパラメータとしてtransactionIDが付加されますので、GETで取得することができます。
実際のコード
参考サイト
以下のサイトを参考にさせていただきました。大変助かりました。ありがとうございます。
PHPでLINE Payを実装してみた(サンプルコードあり) | Qiita
サンプルコード
当店で使用している実際のコードをご紹介します。当店システムに特有の部分は、説明する文言におきかえています。
<?php $sandboxmode = false; if($_GET['ordercode']){ //当店試験管理番号 $ordercode = $_GET['ordercode']; } else { echo '通常と異なる呼び出し方法です。動作を停止します'; exit; } <試験申し込み情報をDBから取得する部分・省略> if($sandboxmode){ $baseurl = 'https://sandbox-api-pay.line.me/'; } else { $baseurl = 'https://api-pay.line.me/'; } $hostname = 'https://'.$_SERVER["HTTP_HOST"]; $returnbase = $hostname.'/form_certification/'; $header = array( 'Content-Type: application/json; charset=UTF-8', 'X-LINE-ChannelId: <当店加盟店コード(channelID)>', 'X-LINE-ChannelSecret: <当店加盟店Secret>', ); if(!$_GET['transactionId']) { // ----------------------------------- 決済reserve $postData = array( 'productName' => "受験料のお支払い {<受験日を示す変数>}分", 'productImageUrl' =>'https://curio-shiki.com/form_certification/images/linepay-iconimage.png', 'amount' => <受験料合計を求める数式>, // 'amount' => 1, <---テスト用に1円決済にするときはこちらを有効にする 'currency' => "JPY", 'confirmUrl' => "{$returnbase}<決済Confirm時に遷移するURL>?ordercode=xxxxxx", 'checkConfirmUrlBrowser' => true, 'confirmUrlType' => "SERVER", -->通常は「CLIENT」とするが、SERVERにした理由は後述 'orderId' => <一意の注文番号>, ---決済済みの注文番号と重複するとエラーになる ); $ch = curl_init($baseurl.'v2/payments/request'); echo $baseurl.'v2/payments/request'.'Reserve'; curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData)); $rs = json_decode(curl_exec($ch), true); curl_close($ch); if($rs['returnCode'] == "0000") { header("Location: {$rs['info']['paymentUrl']['web']}"); } else { echo "決済に失敗しました。エラー番号:{$rs['returnCode']}<br>くり返し失敗する場合は、お送りしたメールの返信にてご連絡をお願いいたします。"; } } else { // ----------------------------------- 決済confirm if($_GET['ordercode']){ //当店試験管理番号 $ordercode = $_GET['ordercode']; } else { echo '通常と異なる呼び出し方法です。動作を停止します'; exit; } $postData = array( 'amount' => <受験料合計を求める数式>, // 'amount' => 1, ---テスト用 1円決済するときはこちらを有効 'currency' => "JPY", ); $ch = curl_init($baseurl."v2/payments/{$_GET['transactionId']}/confirm"); echo $ch.'confirm'; curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData)); $rs = json_decode(curl_exec($ch), true); curl_close($ch); if($rs['returnCode'] == "0000") { header("Location:{$returnbase}/<支払完了処理のURL>"); } else { header("Location:{$returnbase}/<支払キャンセルのURL>"); --ここはエラーだが一律キャンセル扱いで遷移させています } } ?>
コード解説
- ReserveとConfirmを同一PHPファイル上にのせています。$_GET[‘transactionID’]が存在しなければReserve、存在すればConfirmをコールします。
- テスト用Sandboxは、channelID,channelSecretは同一で、コールするURLのみ異なります。$sandboxで容易に切り替えできるようにしています。
- ただし、sandboxでは、アプリによる決済はシミュレーションできずwebで完結するため、最終段階はどうしても本番環境でテストしなければなりませんでした。テストした決済はすぐに加盟店画面からキャンセルしましょう。
- LINE Pay APIは RESTful APIであり、sessionを使用しません。このため、加盟店側で通して引き継ぎたいデータは、このようにconfirmUrlにパラメータとして載せておくのがよいように思います。
- Reserve時のamountと、Confirm時のamountが違うとエラーになります。
- orderIdは、別に当店はなくてもいいのですが、指定しないとエラーになります。そして、決済済みのorderIdと重複するとエラーになります。(未決済のままなら重複してもエラーにならない)
- confirmUrlType は、当初CLIENTで実装しましたが、次項でのべる問題により、SERVERに変更しました。このため、クライアント側の画面を決済完了画面に遷移させるためにjavascriptを使用しています。
アプリ内ブラウザでconfirmUrlに遷移しない問題
当初
‘confirmUrlType’ => “CLIENT”
で実装していたのですが、受講生様より試験申し込みがあり、教室で実際に決済していただいたところ、
Gmailでお送りしたボタンから決済に進むと、決済承認しても決済が完了しないという問題が発生しました。
テストしてみると、confirmUrlへの遷移が行われていないのです。
アプリ内ブラウザでReserveを開始すると、決済承認されてもconfirmUrlに遷移しないことが判明しました。
このため、次の変更を実施しました。
confirmUrlはサーバー側で実行
‘confirmUrlType’ => “SERVER”
これで、クライアント側の画面で決済完了を表示できませんが、決済そのものはサーバー側でconfirmが実行され、正常に完了します。
Reserveの手前の画面で、ajaxにより支払完了を検知して遷移
Reserveに進むリンクがある(「Line Payで支払う」ボタン)画面に、Javascriptを追加しました。(JQuery必要です)
(function($){ $(function(){ timer=setInterval(check_ispaid,1000); }); function check_ispaid(){ $.ajax({ url: '/form_certification/<決済状態監視用API>.php', type: 'post', dataType: 'json', cache: false, data: { ordercode: $('#ordercode').val() } }) .done(function(response){ ispaid = response[0]; if(ispaid == 1){ clearInterval(timer); location.href = "/form_certification/<決済完了画面を表示>.php"; }; }) .fail(function(){ alert('エラーが発生しました。'); }); } })(jQuery);
<決済状態監視用API>.phpは次のようなコードです。
<?php <関数include、試験データ取得部分・省略> $ordercode = $_POST['ordercode']; <試験データ取得部分・省略> $return_data = json_encode(array(<該当ordercodeの支払い完了フラグ>)); echo $return_data; ?>
これで、決済完了画面への遷移は、LINE Pay APIによるのではなく、自サーバのフラグを監視することで自力で遷移するようになりました。
LINE Pay APIを実装してみた感想
全体的に、シンプルでよくできたAPIでした。
商品名をいくつも登録したりする機能がなかったりしますので、決済前の画面でしっかりユーザーに決済内容を説明しておくのがよいと思います。
LINEアプリを必ず通過しますので、旧来のカード決済に慣れた方にはかえって複雑に感じられる面はあると思います。しかし、セキュリティ上はこちらの方式がすぐれていると思いますので、今後、他の決済もこのような方式をとっていくのではないでしょうか。
例えば、VISA認証の仕組みがありますが、スマホアプリで認証する方がセキュアですね。
アプリ内ブラウザから正常に決済完了しなかったのは、なんとかならないものかと思っています。
PayPalも実装しましたが、こちらはアプリ内ブラウザでも正常に決済完了しています。
コメント