【iOS,GCP】Walletに追加したPassをリモートからアップデートしたい。
サーバー側:GCP(Cloud Functions)
モバイル側:Flutter
・アプリのおおまかな作り
アプリで自分のお店のPassを作成 → PassデータがFirebase Storageに保存される → お客さんにFirebase StorageのURLを共有し、Passを取得してもらう
・やりたいこと
Pass情報を編集した時にWalletにAPNを送信してPassをアップデートしたい
12/1 9:00
これに沿って実装する。
まず、GCPのCloud FunctionsからWalletにプッシュ通知を送りたい。
これに沿って自前コードを書いてみたけど
export const onCardUpdated = functionFirestore
.document("passes/{uid}")
.onUpdate(async (snapshot, context) => {
var passData = snapshot.after.data()
var deviceSnap = await admin.firestore().collection('devices').where('serialNumber','==',passData.serialNumber).get()
var devices = deviceSnap.docs
console.log('devices', devices.length)
for (var i in devices) {
var pushToken = devices[i].data().pushToken
console.log('send to ', pushToken)
var path = "/3/device/"+pushToken
var url = "https://api.push.apple.com:443"+path
const response = await fetch(url, {
method: 'POST',
body: null,
headers: {
'apns-topic': passId,
'apns-push-type': 'background',
'connection': 'keep-alive'
}
});
if (!response.ok) {
console.log('エラー!',response.json())
}
if (response.body !== null) {
var json = response.json()
console.log('json',json)
}
}
})
内容のないエラーしか返ってこない。
TypeError: fetch failed
ダメもとでFlutterでもやってみたけど、これも中身のないエラーが返ってきた。
ClientException: Invalid request method
Appleさんに質問してみたけどサポート外ということだった。
Appleのプラットフォームにも半ギレでポストしてみたが、今日(2023/12/1)時点では反応なし。
https://developer.apple.com/forums/thread/742343
Axios使ってやってみても意味不明なエラーが出て、Githubのissuesに挙げてみたが3日間無反応。Axios開発者からしたら「こっちの問題じゃねーよ」って感じだろう。
URLリクエストは単純なPOSTくらいしかしたことないんだけど、そこが間違ってそう。APNs用の証明書を送る工程が必要なんじゃないか?わからない。
10:04
Cloud Messagingを使って送れるかを試す。
トピックをPass Type IDにすると送信できるよー。的なことを言っている人がいた。
解釈が正しいか定かではないので、まずCloud Messagingのコンソールから送ってみた。
届かない。
そもそもトピックにサブスクライブしたユーザーに通知送る時は、アプリ側からサブスクライブする必要なかったっけ?
今回はPassをWalletに追加した人に通知を送りたいので、アプリをインストールしていない場合が多い。したがってアプリからサブスクライブというのができない。
どうしたらいいんだ。。
10:50
トピックから送る方法は諦めて、Certificateを使おうと思った。
The notification uses the same certificate and private key that the creator of the pass used to sign the original
とドキュメントには書いてあるが、そもそもcertificateをどこで使えばいいのかわからない。
アプリをインストールしてない人にでもAPNを送れるのか、ここからCode-Level Supportでお問い合わせしてみた。
https://developer.apple.com/contact/technical/#!/request/form
返信を待つ。
12/7 13:55
1週間経ったがAppleから返信が来ない。
Apple Developer Forumにも投稿してみたけど誰も返信してくれない。
質問内容を変えて、必死な感じでもう1つTSI送ってみた。
次の年 01/11
何も進展してない。
Code Level Supportから返信が来て、アプリをインストールしてない人にでもAPNは送れるらしい。
そしてCode Level Supportはバックエンドはサポートできないらしいので、やはりStackoverFlowとかに頼るしかない。
同じような質問はApple Develper Forumにいくつかあるのに、誰も答えていない。いったいみんなどうやって実装してるんだ。
1/13
地道に調べて実装できた!!!
var path = "/3/device/"+pushToken
var url = "https://api.push.apple.com:443"
console.log(url)
var storage = admin.storage().bucket()
var signerCert = (await getRawBody(storage.file('signerCert.pem').createReadStream())).toString();
var signerKey = (await getRawBody(storage.file('signerKey.pem').createReadStream())).toString();
const clientSessionOptions: http2.ClientSessionOptions = {};
const secureClientSessionOptions: http2.SecureClientSessionOptions = Object.assign({}, clientSessionOptions);
secureClientSessionOptions.cert = signerCert;
secureClientSessionOptions.key = signerKey;
secureClientSessionOptions.passphrase = 'test';
const client = http2.connect(url, secureClientSessionOptions);
client.on('socketError', (err) => console.error('ソケットエラー!',err));
client.on('error', (err) => console.error('エラー!',err));
const payloadString = JSON.stringify({});
const req = client.request({
':method': 'POST',
':path': path,
'apns-push-type': 'background',
'content-type': 'application/json',
'content-length': Buffer.byteLength(payloadString, 'utf-8').toString(),
});
req.on('response', (headers, flags) => {
for (const name in headers) {
console.log(`レスポンス ${name}: ${headers[name]}`);
}
});
req.write(payloadString);
req.setEncoding('utf8');
let data = '';
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => {
console.log(`\n終了 ${data}`);
client.destroy();
});
console.log('Http2.終了する')
req.end();
ヤッターーーーーーーーー!!!!!!!
この記事が気に入ったらサポートをしてみませんか?