見出し画像

Twitter広告コンバージョンAPI最終編~ssGTM + Cloud Functions~

TwitterのコンバージョンAPIの話の最終編です。

前編後編で終わりじゃないのかよ!という感じもありますが、後編を書いている途中で『この選択肢もいいかも?』と思い、記載しております。

後編では
ssGTM 85%、Python(+FastAPI)15%で実装
ssGTM 40%、Google Apps Script(GAS) 60%で 実装
の説明をしていきました。

しかし、ssGTMが稼働しているということは、基本的にGCP(Google Cloud Platform)を使用していると思います。

ならば、GCP内で完結出来る方がよくないか?と考えたわけです。

というわけで、
ssGTM 85%、Python(+FastAPI)15%
のうち、Python(+FastAPI)の部分をGCPのCloud Functionsでやろう!!
という話になります。

nonceの計算とsignatureの生成をCloud Functionsでやります。

まずは認証掛けずに設定する方法を説明し、その後に認証をかける方式を紹介してみます。

Cloud Functionsでnonce生成の関数を作成

さっそく、Cloud Functionsを見ていきましょう。
GCPにログインして、メニューからCloud Functionsをクリック。

関数の作成をクリック

すると「構成」画面に遷移します。
環境は「第2世代」にし、関数名は適当に名付けます。

リージョンもどこでもいいですが、一応「asia-northeast1」が東京になります。「asia-northeast2」が大阪です。
(どこでもと書きましたが、リージョンによって微妙に料金が異なります)

トリガーの「HTTPS認証」は、今回は一旦「未認証」を選択します。

ここまで設定したら、「次へ」

実際にコードを記載する部分です。
ランタイムは何でもいいですが、ここでは「Python 3.10」で説明します。

functions_frameworkはデフォルトでインポートされています。
これに加えて、secrets をインポートします。

標準ライブラリなので、「requirements.txt」に追加で記載する必要はありません。

実際のコードはこちら。Pythonで書いたときとほぼ同じですね。

import functions_framework
import secrets


@functions_framework.http
def fn_nonce(request):
    method = request.method
    if method == 'POST':
        _nonce = secrets.token_urlsafe(32)
        nonce = ''.join(char for char in _nonce if char.isalnum())
        return {'nonce': nonce}

上部の「エントリポイント」をコード内の関数名と同じに編集します。
(名前自体はなんでもOK)

requirements.txtはデフォルトでOK(とくにいじりません)

ここまで書けたら、「デプロイ」です!

するとページが遷移します。
デプロイ中は「ビルド」と「サービス」が「Creating build」や「保留中」となっていると思いますが、気長に待ちましょう。

すべて完了すると、関数名に「緑マーク」がつきます。
と、同時に「エンドポイントURL」も吐き出されます。

ではこのエンドポイントへPOSTリクエスト送ってみましょう。

Curlコマンドでも何でも大丈夫ですが、せっかくなのでssGTMを使ってみます。テンプレートから「新規」をクリック。

コードタブにて以下のように記載。

const sendHttpRequest = require('sendHttpRequest');
const log = require('logToConsole');

const URL = "https://function-nonce-cp2pfbr3cq-an.a.run.app";

sendHttpRequest(URL, {
  method: 'POST'
}).then((res) => {
  log(res);
  log(res.body);
});


data.gtmOnSuccess();

権限タブで、『HTTPリクエストの送信』を「すべてのHTTPS URL」にしておきます。本番運用するときは指定するべきですが、一旦テストするだけなのでOKです。

そしたら、「コードを実行」をクリック

すると、コンソールにレスポンス結果が表示されます。
(もしかすると、Time Out的なメッセージが出るときもありますが、焦らず、再度「コードを実行」をクリックしてみましょう。)

res.bodyのlogで
{"nonce": "略"}
というjsonが返ってきていれば問題なしです。
(「略」とした部分は毎回変わるので、添付と同じ文字列になっている必要はないです)

では次、signatureを生成する関数を作ってみましょう。

Cloud Functionsで signature生成の関数を作成

コードの部分までは「nonce」のときと同様なのでさくっと書きます。
「関数の作成」⇒環境を「第2世代」にして、関数名を自由につける。

トリガーの認証はデフォルトでは「認証が必要」なので、
ここでは一旦「未認証の呼び出しを許可」で進めます。

「次へ」をクリックし、コード編集画面へ。

ここではランタイムを「Python 3.10」にします。

base64, hmac, hashlib, urllib をインポートします。
すべて標準ライブラリなので、requirements.txtに追記する必要はここでもないです。

コードはこちらもほぼPythonのときと同じです。

import functions_framework
import base64
import hmac
import hashlib
import urllib


@functions_framework.http
def fn_sigsig(request):
    method = request.method
    if method == 'POST':
        data = request.get_json()
        signkey = bytes(data.get('key'), 'utf-8')
        base_string = bytes(data.get('value'), 'utf-8')
        m = hmac.new(signkey, base_string, digestmod=hashlib.sha1).digest()
        m_base = base64.b64encode(m)
        result = urllib.parse.quote(m_base, safe="")

        return {'signature': result}

そして、エントリポイントを関数名に変更します。

終わり。「デプロイ」をクリック。しばらく待つ。

するとエンドポイントURLが吐き出されますので、
またssGTMでPOSTリクエスト送ってみましょう。

コードは以下。

const sendHttpRequest = require('sendHttpRequest');
const JSON = require('JSON');
const log = require('logToConsole');

const URL = "https://function-sigsig-cp2pfbr3cq-an.a.run.app";

const headers = {
  "Content-Type": "application/json"
};

const key = "abc";
const value = "あいうえお";

const payload = {
  key: key,
  value: value
};



sendHttpRequest(URL, {
  headers: headers,
  method: 'POST'
}, JSON.stringify(payload)).then((res) => {
  log(res);
  log(res.body);
});


data.gtmOnSuccess();

ここで、先程と同様に権限の「HTTPリクエストの送信」をすべてにして、コードを実行します。

コンソールに次のように表示されればOKです。

keyとvlaueを同じものに設定していれば、
今回は全く同じsignatureが返ってくるはずです。
(LOfx8wtO6hBsBOHt71h5abPOpG8%3D)

これでCloud Functionsで関数が作れました。
エンドポイントURLにPOSTリクエスト送れば、関数が実行されて、
結果がレスポンスされる、というわけです。

しかし、このままではオープン状態で、
URLさえわかれば誰でもリクエスト送ることが出来る状態です。

まぁ見られて困る情報を送っているわけではないですが、
制限はかけておきたいところです。

次はそこを見ていきましょう。

認証をかけよう!

関数を作るときに普通に「認証が必要」を選ぶと
ssGTMからトリガーできないんですよね。。

Cloud Functionsを認証する方法はいくつかあります。

簡単に3パターン説明しますね。
今回以下の方法は使わないので、本当に簡単なのでよく分からない部分もあるかもですが、スルーしていただいて大丈夫です。
詳しくは上記のヘルプなど御覧ください。

1: 環境変数を入れる認証

GOOGLE_APPLICATION_CREDENTIALS変数に認証サービスアカウントのキーのjsonを渡す方法。

これは、結局ライブラリをインポートしなくてはいけないのですが、ssGTMで使える関数じゃないんですよね。
(ssGTMはGTM側で用意している独自関数しかインポートできない)

2: メタデータサーバーの使用

この方法もssGTMからはアクセスできませんでした。

3: JWTを使って認証

これについても、JWTの生成に暗号化処理が走るので、結局ssGTMでは無理です。

そもそもhmacの処理がssGTM単体でできないので、色々試行錯誤しているわけですが、ここではhmacよりもっと厄介な処理が走るため余計にできません。。

と、いうわけで「もう無理かーー」と思うところですが、API Gatewayをかませることで、認証しつつ、ssGTMからリクエストを送れます。

API Gatewayを噛ませて認証

さっそくはっていきたいところですが、その前に
これまで作った未認証の関数を「認証が必要」に変更しておきましょう。

Cloud Functionsの画面からはできないので、Cloud Runのページにいきます。

先程作った関数をクリックします。

トリガータブから「認証が必要」に変更して、保存します。

これで認証が必要になりましたので、
先程のようにリクエストを送ろうとしても「403」が返ってきます。

ということで、認証が掛かってるのを確認できましたので、次の話にいきましょう! API Gateway のtopページへ行・・・く前に、サービスアカウントを作成しておきましょう。

サービスアカウントの作成

API Gatewayを作成する途中で、サービスアカウントを選択するところがあります。デフォルトの「Compute Engine」のサービスアカウントでも大丈夫なのですが、ここではCloud FunctionsのAPI Gateway用に別途作成することにします。

ナビゲーションから、「APIとサービス」から「認証情報」へ。

「認証情報を作成」から「サービスアカウント」をクリック。

サービスアカウント名やサービスアカウントIDは何でもOKです。
(サービスアカウントIDはサービスアカウント名を入力すると自動で入力されます。)

次にロールを選択します。3つあります。
① Service Accounts > サービスアカウント ユーザー
② Cloud Run 起動元
③ Cloud Functions 起動元

まとめるとこんな感じです。

ここまでできたら、「完了」しましょう。

サービスアカウントに表示されていればOK!

API Gatewayの作成

ここから先は

5,143字 / 31画像

¥ 1,000

この記事が気に入ったらサポートをしてみませんか?