Laravel:NextEngineAPIの初手【access_token】を取得するまでのGETやらPOSTやら

Laravel:NextEngineAPIの初手【access_token】を取得するまで

頂いたシステム系のお仕事が諸々片付いてきたので自分用の開発再開です。

社内の販売管理システムとMySQLの連携はすでに構築したので、この在庫数をNextEngineに持っていくパートの造作となります。

NextEngineAPI連携の面倒なところ

NextEngineさんから認証ステップの画像頂戴してきました。
この認証系が面倒なんです。

認証系のポイントまとめ

  1. APIサーバーとのやり取りでは 【access_token】が必要
  2. access_token取得には【uid】【state】が必要
  3. uidとstateの取得にはNextEngineへのブラウザログインが必要
  4. access_token取得にもNextEngineへのブラウザログインしている必要がある

ザクっと書いてみるとこんな感じです。
つまりはスタート地点がブラウザログインであると。

access_token取得までのScript

まずは実際に書いたスクリプトをどうぞ。

namespace app\Library\NextEngine;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Weidner\Goutte\GoutteFacade;
use Goutte\Client;
use HeadlessChromium\BrowserFactory;
use Carbon\Carbon;

class NextEngineAPI {
    
    public static function ConnectNE_First($client_id, $client_secret, $redirect_uri)
    {
        ////////////////////////////////////////
        // NEログイン
        // ログインURL:https://base.next-engine.org/
        ////////////////////////////////////////
        $loginURL = 'https://base.next-engine.org/';
        #アクセス後ログインページに遷移されるかを確認
        #Goutte 実行
        $client = new Client();
        #情報取得
        $crawler = $client->request('GET',$loginURL);
        $alert = $crawler->filter('div#sessions_alert')->text();
        if(strpos($alert,'ログインしてください') !== false){
            #ログインしてない時ログイン実行
            $loginForm = $crawler->selectButton('ログイン')->form();
            $loginForm['user[login_code]'] = 'ログインID';
            $loginForm['user[password]'] = 'ログインPASS';     
            $crawler = $client-> click($loginForm);
        }
        
        ////////////////////////////////////////
        // エンドポイントアクセスで uid・state取得
        // HOST_ADD:https://base.next-engine.org
        // ENDPOINT:/users/sign_in
        ////////////////////////////////////////
        #GETでアクセス=>RedirectURLに値が格納されて返却
        $Endpoint = 'https://base.next-engine.org/users/sign_in'.'?';
        #情報取得
        $crawler = $client->request('GET', $Endpoint.'client_id='.$c_id.'&redirect_uri='.$redirect_uri);
        
        #パラメーター取出
        $getURL = $crawler->getUri();//繊維ページのURL取得
        $getURL = str_replace($redirect_uri.'?', '', $getURL);
        $pieces = explode("&", $getURL);

        #変数初期化
        $uid = $state = NULL;
        foreach($pieces as $piece){
            if(strpos( $piece, 'uid=' ) === 0){
                #uidで始まる場合は値を変数に格納
                $uid = str_replace('uid=', '', $piece);
            }elseif(strpos( $piece, 'state=' ) === 0){
                #stateで始まる場合は値を変数に格納
                $state = str_replace('state=', '', $piece);
            }
        }

        ////////////////////////////////////////
        // POSTして access_token取得
        // ENDPOINT:https://api.next-engine.org/api_neauth/
        ////////////////////////////////////////
        if(!empty($uid)&&!empty($state)){
            #$uidを取得できている場合のみ実行
            $Endpoint = 'https://api.next-engine.org/api_neauth/';
            $client->request('POST', $Endpoint,['uid'=>$uid,'state'=>$state,'client_id'=>$c_id,'client_secret'=>$c_secret]);         
            #帰ってきた値を取得
            $crawler = $client->getInternalResponse();

            #JSONだけどGoutte がJSON取得面倒なので文字列として扱う
            #{}で囲まれた値の取得 ※これでJson部分のみを取得できる
            $pattern = '/\{.+?\}/';
            preg_match_all($pattern, $crawler, $match);
            $token_list = (array)json_decode($match[0][0]);
log::info($token_list);
        }
    }

▼実行結果(ログに出力※IDやらtokenやらは文字列シャッフルしてます)

[2021-01-15 14:42:03] production.INFO: array (
  'access_token' => 'b1874c432d3ceec370ed32bbe41b0ceb6f5a086cba2bce278f3a62ca3f886802d8ed7686cb6c375982067d38e479345d9245f15edc791783805edf7dc689c40c',
  'company_app_header' => '社名',
  'company_ne_id' => '159ce54f534fca06b4e274067ef0c7fb40693818e600689443078b85cfe04adcbd69e4e692e659ab3c02dcb90d310b943b456c971977e1eab',
  'company_name' => '社名',
  'company_kana' => 'しゃめい',
  'uid' => 'eecf8e10d590c5c6954a64aa8db2b104379199f77bcb6cd9af847aff5083f5c746131f534924be1cf3550a8fbe5611739df4e82caabdd476368c7fe158a9824e',
  'pic_ne_id' => '61f77be55b7cf1dfba371498ecb5feba9e5c04be676056c0989ff8fd27bac2b2f9a76b7d9730eff1661c04022ef552d2f2a936404d8f448cc5504f0b9229343',
  'pic_name' => 'ユーザー名',
  'pic_kana' => 'ゆーざーめいかな',
  'pic_mail_address' => 'メールアドレス',
  'refresh_token' => 'e079404e63acb88c979704e04a9dce78d0efd6f24b8987fe1e3a96da794377b8081850626a4e69ef49c0c51a8bd48b47b478a360489673eb456c828e01bd4975',
  'result' => 'success',
)

NextEngineAPI ::ConnectNE_First はSchedulerかなんかで呼び出せば時限式実行が出来ます。

記載したコードは1つのFunction内で記載してますが【ベース/ブラウザログイン/UID取得/token取得】といったように小分けすれば使いまわしやすくなりそうです。

スクリプトをチョット解説

19行目~32行目

ネクストエンジンへのブラウザログイン実行パートです。
22行目でGoutte/Clientを新規作成し、24行目でログインURLにアクセス。
25行目では吐き出されたHTMLから『<div id="sessions_alert"></div>』に格納されているテキストを取り出しています。

26行目はログイン動作を行うか否かの分岐。
ネクストエンジンでは、未ログインの場合アラートを鳴らしてログイン画面に遷移してくれるのでこれを利用しています。

28行目~31行目がブラウザログインのパーツ。
ログインボタンが<input class="btn">で作成されていたため、submitで送ることはやめてClickアクションにしています。

40行目~59行目

アクセストークン取得に使うUIDを取得するパートです。
クライアントIDとリダイレクトURLを入れてアクセスするとGET値を加えて返してくれます。
42行目がGETを加えてのURLアクセス。
45行目はリダイレクトされたページのURLを取得。
46行目でリダイレクトURLを取り除いて、47行目で配列化しています。
51行目~59行目は配列にしたGETの値を読み込んで uid= / state= で始まる文字列の場合に変数代入し次の工程で利用しやすい姿に加工しています。

注意点:clientをnewしてしまうと勝手にログオフしてしまい、またブラウザログインのページに遷移してしまいました。$clientはそのまま活用した方がよさそうです。

65行目~78行目

アクセストークンを取得するためのパートです。
このパーツではPOSTして値を返してもらうことになります。
65行目は前工程でuidの取得に成功している場合のみ実行するための場合分けです。
68行目はPOSTの実行。
70行目はPOSTして帰ってきた文字列の取得。下のような形で返ってきます。

Date: Fri, 15 Jan 2021 04:56:02 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 1246
Connection: keep-alive
Server: Apache
Cache-Control: no-cache, must-revalidate
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Max-Age: 3628800
Access-Control-Allow-Origin: リダイレクト登録URL
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type
Access-Control-Expose-Headers: Authorization
Access-Control-Request-Headers: content-type, Authorization, X-Authorization

{
"access_token":"b1874c432d3ceec370ed32bbe41b0ceb6f5a086cba2bce278f3a62ca3f886802d8ed7686cb6c375982067d38e479345d9245f15edc791783805edf7dc689c40c",
"company_app_header":"社名",
"company_ne_id":"159ce54f534fca06b4e274067ef0c7fb40693818e600689443078b85cfe04adcbd69e4e692e659ab3c02dcb90d310b943b456c971977e1eab",
"company_name":"社名",
"company_kana":"しゃめい",
"uid":"eecf8e10d590c5c6954a64aa8db2b104379199f77bcb6cd9af847aff5083f5c746131f534924be1cf3550a8fbe5611739df4e82caabdd476368c7fe158a9824e",
"pic_ne_id":"61f77be55b7cf1dfba371498ecb5feba9e5c04be676056c0989ff8fd27bac2b2f9a76b7d9730eff1661c04022ef552d2f2a936404d8f448cc5504f0b9229343",
"pic_name":"ユーザー名",
"pic_kana":"ゆーざーめいかな",
"pic_mail_address":"メールアドレス",
"refresh_token":"e079404e63acb88c979704e04a9dce78d0efd6f24b8987fe1e3a96da794377b8081850626a4e69ef49c0c51a8bd48b47b478a360489673eb456c828e01bd4975",
"result":"success"
}

この中で欲しいのはJSONの箇所のみなのですが 『->JSON()』とかで取り出せないので74行目~75行目で苦肉の策。
{}で囲まれた文字列の抽出です。

76行目は取り出したJSONを配列に格納です。

後回しにしていたNextEngineとの連携パーツを作り出した理由

社内システムとLaravelとの連携はこんな段取りで構築しています。

  1. VBScriptでPC操作し【商品×在庫数】のデータをCSVでPCに保存
  2. 保存したCSVデータをコマンドプロントからSFTPでWEBサーバーの所定の場所に所定の名前で保存
  3. LaravelのSchedulerで所定のディレクトリを定期的に監視。ファイルがあったら取込処理開始
  4. 読込んだCSVとMySQLの在庫数が異なっていたら変更、商品が存在しなかったら新規作成

これが完成したからというのも大きな理由ですが、もう一つの理由があります。

最近の楽天って同じものが連続して売れる確率が高くなってませんか?

私はそう感じてるんですよね。3年前くらいから『売れているものをより露出する』という方向で動いてきていますが、ここ1年は「なぜ?」という購買が目立つようになってきました。

今まで1回も販売が成立していなかった商品が1日に10件の受注。
翌日からはまたパッタリと受注がなくなる。

こんな例が何回もあったんです。

楽天市場内検索で優位に立って急に売れるようになったのであれば翌日に蹴落とされることはないでしょう。
なので『購入された時点』で新たな露出導線が作られて、これが広いユーザーにアナウンスされているのだと思っています。

 社内販売管理とLaravel の連携はできているがNextEngineには最新の在庫数が自動で入らない

現在はこんな状況なのですが、先程の事例のお陰でこれが意外と深刻なネタになってきています。

  • Laravel側で在庫×2の商品を在庫数2としてモールに登録(更新)。
  • NextEngine側の在庫数が100となっている(最新情報に更新していない)
  • 注文が入ると[ 100-1 = 99 ]という在庫数でNextEngineが在庫調整を実行
  • 実際には1しかない商品の在庫数が99で登録される

お陰で在庫なしキャンセルが集中的に発生する事例が増えてしまいました。

NextEngineが提供してくれているSDKを使わない理由

開発フレームワークが一番の理由です。

NextEngineで用意してくれているSDKはFuelPHPで作成しており、Laravelで開発している私としては採用するのに躊躇いがありました。で「ゼロベースで書くとなると時間かかるな」と後回しにしていたのですが、まぁいずれやらなきゃだからね。

まとめ

まずは【access_token】【refresh_token】の取得までの実装が完了しました。

あとは項目で分けてAPIアクセスするスクリプト書けばいろいろ遊べるかな。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください