
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の作成
ここから先は
¥ 1,000
この記事が気に入ったらチップで応援してみませんか?