【AWS】bitflyer注文キャンセル

【きっかけ】

過去の記事参照
 
buffalokusojima.hatenablog.com


1. 使用するサービス

(AWS)

  • Lambda

(外部サービス)

bitflyer Lightning API


2. 概要
Lambdaを使用してbitflyerの該当の注文をキャンセルする機能をフロントエンドとバックエンド両方説明します。
API Gatewayに紐付けてブラウザ形式でAPIを叩くことが出来ます。詳細は前回記事参照
buffalokusojima.hatenablog.com

3. 実装

①バックエンド

Lambdaに以下実装。ブラウザ側から受け取った注文IDを元にbitflyerにキャンセルリクエストします。

const ssm = new (require('aws-sdk/clients/ssm'))();
const request = require('request');
const crypto = require('crypto');

exports.handler = function(event, context, callback) {

     // 特殊注文と通常注文でパスが異なる
    const PARENT_PATH = '/v1/me/cancelparentorder';
    const CHILD_PATH = '/v1/me/cancelchildorder';

    var body = JSON.parse(event.body);
    
    console.log(body)
    
    if((!body.child_id && !body.parent_id) || !body.coin_pair){
        console.log("body empty");
          callback(null, {
              statusCode: 400,
              body: JSON.stringify({message: "body empty"}),
              headers: {"Content-type": "application/json"}
            });
        return;
    }

    //受け取ったクエリの中身でパス切替 
    var path;
    if(body.child_id){
        path = CHILD_PATH;
    }else if(body.parent_id){
        path = PARENT_PATH;
    }
    
    //後はいつも通りリクエスト送信
    getParameterFromSystemManager('bitflyer-keys',callback)
    .then(function(data){
       if(body.child_id){
            body = JSON.stringify({
                product_code: body.coin_pair,
                child_order_id: body.child_id
            });
       }else if(body.parent_id){
           body = JSON.stringify({
                product_code: body.coin_pair,
                parent_order_acceptance_id: body.parent_id
            });
       }
        
        const apikey = data.split(",")[0];
        const sercretKey = data.split(",")[1];
        
        var timestamp = Date.now().toString();
        var method = 'POST';
        
        var text = timestamp + method + path + body;
        var sign = crypto.createHmac('sha256', sercretKey).update(text).digest('hex');
        
        var option = {
          url: 'https://api.bitflyer.jp' + path,
          method: method,
          headers: {
            'ACCESS-KEY': apikey,
            'ACCESS-TIMESTAMP': timestamp,
            'ACCESS-SIGN': sign,
            'Content-Type': 'application/json'
            },
          body: body
        }
        
        return sendRequest(option, callback);
    }).then(function(data){
        var message;
        if(data.response.statusCode != 200){
          console.error("Error:",data.response);
          message = data.response;
        }else message = data.body;
        
        console.log(message);
        callback(null, {
            statusCode: data.response.statusCode,
            body: JSON.stringify({message: message}),
            headers: {"Content-type": "application/json"}
          });
          return;
    });
    
    function getParameterFromSystemManager(apikey_name, callback) {
    
        return new Promise(function (resolve) {
            var apikey = process.env[apikey_name];
            
            if(!apikey || typeof apikey == undefined){
            
                // Fetches a parameter called REPO_NAME from SSM parameter store.
                // Requires a policy for SSM:GetParameter on the parameter being read.
                var params = {
                    Name: apikey_name,
                    /* required */
                    WithDecryption:true
                };
                
                ssm.getParameter(params, function(err, apikey) {
                    if (err){
                        console.error(err.stack);
                        callback(null,{
                            statusCode: 500,
                            body: JSON.stringify({message: err.toString()}),
                            headers: {"Content-type": "application/json"}
                        });
                        resolve(null);
                        return;
                    }
                    process.env[apikey_name] = apikey.Parameter.Value;
                    resolve(apikey.Parameter.Value);
                });
            }else resolve(apikey);
        });
    }
    
    function sendRequest(option, callback){
        
        return new Promise(function (resolve) {
            request(option, function(error, response, body){
                    
                    if(error){
                        console.error(error);
                        callback(null,{
                            statusCode: 500,
                            body: JSON.stringify({message: error.toString()}),
                            headers: {"Content-type": "application/json"}
                        });
                        resolve(null);
                    }
                    var data = {response, body}
                    resolve(data);
            });
            });
    }
}

②フロントエンド

前回作成した注文リスト内からキャンセル出来るようにします。
過去記事見ると既に実装している部分が見られます。
buffalokusojima.hatenablog.com

makeOrderList関数の中身を一部抜粋します。

function makeOrderList(data){
            
            data = JSON.parse(data);
            var targetDiv = document.getElementById("openOrderArea");
            if(targetDiv.firstElementChild){
                targetDiv.removeChild(targetDiv.childNodes[0]);
            }

・・・省略・・・

var input = document.createElement('input');
                input.type="button";
                input.value="cancel";
                if(d.child_order_id){
                    input.id = "child_id"+d.child_order_id;
                }else{
                    input.id = "parent_id"+d.parent_order_acceptance_id;
                }

                //イベントを追加して、ボタンクリック時にキャンセル関数が呼ばれるようにします
                input.addEventListener('click', function(){  
                    console.log("cancel:", this);
                    cancelOrder(d.product_code, this.id); 
                })
                td.appendChild(input);
                tr.appendChild(td)

・・・省略・・・

cancelOrderの中身
引数の注文受付IDをリクエスト送信するだけです。

function cancelOrder(product_code, id){

            var type = id.split("child_id");
            var body = {"coin_pair": product_code}
            if(type.length > 1){
                body.child_id = type[1];
            }else{
                type = id.split("parent_id");
                body.parent_id = type[1];
            }

            var data = {
                "method": "POST",
                "url": URL + "CancelOrder",
                "body": body
            }
   
   //キャンセル後にリストを更新
            sendRequest(data, getOrders, null);
        }


4. おわりに
以上で、bitflyerに対しての注文キャンセルが出来ます。
挙動としては、キャンセルボタンを押すと、正常に動作すればリストから該当の注文が消えます。
bitflyerのサイトからも消えていれば成功です。