In-App SDK を使う
このページでは、In-App SDK を使って簡単なミニアプリを作成する方法を紹介します。 また、Service APIによる通知の送信も行います。
必要環境の用意
このページでは、Node.js 20 と npm 10 を使用して説明します。 他のバージョンを利用する場合は、オプションなどが異なる場合がありますので、適宜読み替えてください。
サービスの作成
まずは、作成するミニアプリのサービスを作成してください。サービスの作成を参照してください。
プロジェクトの作成
npm create vite@latest
コマンドでプロジェクトを作成します。
Project name
には 好きな名前を入 力してください。Select a framework
はVanilla
を選択してください。Select a variant
はTypeScript
を選択してください。
% npm create vite@latest
✔ Project name: … miniapp-example
✔ Select a framework: › Vanilla
✔ Select a variant: › TypeScript
Scaffolding project in ・・・/miniapp-example...
Done. Now run:
cd miniapp-example
npm install
npm run dev
SDK の組み込み
まず、.npmrc
ファイルを作成します。
.npmrc
という名前のファイルをプロジェクトのルートディレクトリに作成し、以下の内容を追加してください。
@pocketsign:registry=https://repo.platform.p8n.app
//repo.platform.p8n.app/:_auth=<YOUR_BASIC_AUTH_STRING>
<YOUR_BASIC_AUTH_STRING>
の部分は、以下のコマンドによって生成される値を入力してください。
echo -n "token-user:<YOUR_SDK_TOKEN>" | base64
<YOUR_SDK_TOKEN>
の部分は、SDK 取得用トークンを入力してください。SDK 取得用トークンの取得方法はSDK 取得用トークンの作成を参照してください。
実際のプロジェクトでは、.npmrc に SDK トークンを直接記載しないでください。 また、VCS にトークンが記録されないように注意してください。
保存したら、npm install @pocketsign/in-app-sdk
を実行してパッケージに SDK をインストールします。
% npm install @pocketsign/in-app-sdk
added 19 packages, and audited 20 packages in 6s
5 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
プロジェクトの package.json が以下のようになっていれば成功です。
{
"name": "miniapp-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
/* ... */
},
"devDependencies": {
/* ... */
},
"dependencies": {
"@pocketsign/in-app-sdk": "^x.y.z"
}
}
リソースを取得する
まず、デバッグユーザーの項を参照して、 デバッグユーザーの作成とサービスのサブスクリプションをしてください。その後得られるアクセストークンを保存しておいてください。
デバッグユーザーはモック環境でのみ利用可能なので、モック環境を利用します。 SDKのAPIバックエンドという機能を利用することで、通常のブラウザでの開発が可能です。
では、ユーザーの氏名のリソースを取得して表示する機能を実装します。
src ディレクトリの中に、新たに miniapp.ts を追加します。 以下のコードを追加してください。サービスIDはPlatformから確認できます。アクセストークンは先ほど保存した値です。
import {
createSDKInstance,
createApiBackend,
requestPermission,
readResourceRaw,
MergedSourceResources,
} from '@pocketsign/in-app-sdk'
const SERVICE_ID = '/* ここにサービスID */'
const ACCESS_TOKEN = '/* ここにアクセストークン */'
export async function setup(button: HTMLButtonElement, output: HTMLElement) {
const sdk = await createSDKInstance({
serviceId: SERVICE_ID,
backend: createApiBackend({ accessToken: ACCESS_TOKEN })
})
const onClick = async () => {
const expireAt = new Date()
expireAt.setHours(expireAt.getHours() + 1)
const requestResult = await requestPermission(sdk, [
{
resourceId: MergedSourceResources.fullName,
verbs: [{ verb: 'read' as const, term: 'temporary' as const, expireAt }]
}
])
if (requestResult.result === 'agree') {
const result = await readResourceRaw(sdk, { resourceId: MergedSourceResources.fullName })
if (result.result === 'success') {
const value = result.value === null ? null : JSON.parse(result.value)
output.textContent += `姓名: ${value}\n`
}
}
};
button.addEventListener('click', onClick);
}
実際のプロジェクトでは、ソースコードにアクセストークンを直接記載しないでください。 また、VCS にトークンが記録されないように注意してください。
また、 src/main.ts を以下の内容に置き換えてください。
import { setup } from './miniapp.ts';
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
<div>
<button id="read" type="button">read name</button>
<p id="output"></p>
</div>
`;
setup(
document.querySelector<HTMLButtonElement>('#read')!,
document.querySelector<HTMLElement>('#output')!
);
その後、Platformで「氏名」のリソース (ID: 50f423aa-8ea8-4385-81a1-a7d8da1c4939
) を READ 権限で要求するようにしてください。「最初に要求する」は無効で問題ありません。
リソース取得の動作確認
npm run dev
で開発サーバーを立ち上げ、立ち上がったサーバーにアクセスしてください。
表示された「read name」ボタンをタップすると、姓名がミニアプリに表示されます。
開発版ポケットサインでの動作確認
開発版ポケットサイン内で動作させるため、APP バックエンドに変更します。 src/miniapp.ts を以下のように書き換えてください。
import {
createSDKInstance,
createAppBackend,
requestPermission,
readResourceRaw,
MergedSourceResources,
} from '@pocketsign/in-app-sdk'
const SERVICE_ID = '/* ここにサービスID */'
export async function setup(button: HTMLButtonElement, output: HTMLElement) {
const sdk = await createSDKInstance({
serviceId: SERVICE_ID,
backend: createAppBackend()
})
/* ... */
}
そうしたら、npm run build
でビルドを行って、生成物をデプロイしてください。
npm run preview -- --host
コマンドの実行とポートフォワーディングを行うツール(例:ngrok)を利用しても問題ありません。
ただし、HTTPSでデプロイされたサイトにアクセスできる必要があります。
Platformで確認できる起動リンクの節にあるQRコードをポケットサインアプリで読み取ると、デプロイしたミニアプリを起動できます。 起動後、表示された「read name」ボタンをタップすると、必要に応じて権限要求画面が表示され、姓名がミニアプリに表示されます。
サーバーでのリソースの利用
今度は、リソースを表示するのではなく、サーバーへ送信する機能を実装します。 src/miniapp.ts を以下の内容に置き換えてください。
import {
createSDKInstance,
createAppBackend,
requestPermission,
readResourceWithSignature,
MergedSourceResources,
} from '@pocketsign/in-app-sdk'
const SERVICE_ID = '/* ここにサービスID */'
export async function setup(button: HTMLButtonElement, output: HTMLElement) {
const sdk = await createSDKInstance({
serviceId: SERVICE_ID,
backend: createAppBackend()
})
const onClick = async () => {
const expireAt = new Date()
expireAt.setHours(expireAt.getHours() + 1)
const requestResult = await requestPermission(sdk, [
{
resourceId: MergedSourceResources.fullName,
verbs: [{ verb: 'read' as const, term: 'temporary' as const, expireAt }]
}
])
if (requestResult.result === 'agree') {
const result = await readResourceWithSignature(sdk, { resourceId: MergedSourceResources.fullName })
if (result.result === 'success') {
await fetch('/name', { method: 'POST', body: result.value })
output.textContent += '送信しました\n'
}
}
};
button.addEventListener('click', onClick);
}
readResourceRaw
ではなくreadResourceWithSignature
を利用するように変更したことに注意してくだ さい。
サーバーへ値を送信する際は、必ずWithSignatureで終わる署名付き値を取得できるAPIを利用してください。
詳しくは署名付き値を参照してください。
では、サーバー側の実装をします。
npm install express
を実行して express をインストールします。
% npm install express jsonwebtoken jwks-rsa
added 101 packages, and audited 123 packages in 2s
18 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
api ディレクトリの中に、新たに index.js を追加します。 以下のコードを追加してください。
import express from 'express'
import jwt from 'jsonwebtoken'
import JwksClient from 'jwks-rsa'
import { MergedSourceResources } from '@pocketsign/in-app-sdk'
const SERVICE_ID = '/* ここにサービスID */'
const PORT = 3000
const jwksClient = JwksClient({
jwksUri: 'https://api.core.p8n.app/jwks'
})
const getKey = (header, callback) => {
jwksClient.getSigningKey(header.kid).then(
(key) => {
callback(null, key.getPublicKey());
},
(err) => {
callback(err)
}
)
}
const app = express()
app.use(express.text())
app.post('/name', (req, res, next) => {
// NOTE: 必ずaudienceの検証をしてください。
// 詳しくは「署名付き値」を参照してください。
jwt.verify(req.body, getKey, { audience: SERVICE_ID }, (err, decoded) => {
if (err) return next(err)
// NOTE: 必ずresource_idの検証をしてください。
// 詳しくは「署名付き値」を参照してください。
if (decoded.resource_id !== MergedSourceResources.fullName) {
return next(new Error('Different resource value received'))
}
console.log('氏名:', decoded.value !== null ? JSON.parse(decoded.value) : 'なし')
res.end()
})
})
app.use(express.static('dist'))
app.listen(PORT, () => {
console.log(`listening on port ${PORT}`)
})
通知の送信
プッシュ通知を送信する機能も実装します。 src/miniapp.ts を以下のように書き換えてください。
import {
createSDKInstance,
createAppBackend,
requestPermission,
readResourceWithSignature,
MergedSourceResources,
PushNotificationResourceId,
getSubscriptionIdWithSignature,
} from '@pocketsign/in-app-sdk'
/* ... */
export async function setup(button: HTMLButtonElement, notificationButton: HTMLButtonElement, output: HTMLElement) {
/* ... */
button.addEventListener('click', onClick);
const onNotificationButtonClick = async () => {
const requestResult = await requestPermission(sdk, [
{
resourceId: PushNotificationResourceId,
verbs: [{ verb: 'invoke' as const, term: 'always' as const }]
}
])
if (requestResult.result === 'agree') {
const result = await getSubscriptionIdWithSignature(sdk)
if (result.result === 'success') {
await fetch('/notification', { method: 'POST', body: result.subscriptionId })
output.textContent += '送信しました\n'
}
}
}
notificationButton.addEventListener('click', onNotificationButtonClick);
}
また、 src/main.ts を以下の内容に置き換えてください。
import { setup } from './mini-app.ts';
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
<div>
<button id="read" type="button">read name</button>
<button id="notification" type="button">send notification</button>
<p id="output"></p>
</div>
`;
setup(
document.querySelector<HTMLButtonElement>('#read')!,
document.querySelector<HTMLButtonElement>('#notification')!,
document.querySelector<HTMLElement>('#output')!
);
その後、Platformで「プッシュ通知を送るための権限」のリソース (ID: 3441d71b-8b11-44aa-886e-c46901f3c910
) を INVOKE 権限で要求するようにしてください。「最初に要求する」は無効で問題ありません。
では、サーバー側の実装をします。api/index.js を以下のように書き換えてください。シークレットキーはPlatformで発行できます(シークレットキー)。
/* ... */
const SERVICE_ID = '/* ここにサービスID */'
const SECRET_KEY = '/* ここにシークレットキー */'
const PORT = 3000
/* ... */
app.post('/name', (req, res) => {
/* ... */
})
app.post('/notification', (req, res) => {
// NOTE: 必ずaudienceの検証をしてください。
// 詳しくは「署名付き値」を参照してください。
jwt.verify(req.body, getKey, { audience: SERVICE_ID }, (err, decoded) => {
if (err) return next(err)
const subscriptionId = decoded.subscription_id
if (!subscriptionId) {
return next(new Error("subscription_id doesn't exist"))
}
fetch(
'https://api.core.p8n.app/pocketsign.link.v1.ServiceService/CreateNotification',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${SECRET_KEY}`
},
body: JSON.stringify({
targetSubscriptionIds: [subscriptionId],
messageTemplate: 'push notification test'
})
}
)
.then((res) => res.json())
.then((res) => {
console.log('Notification', res)
})
.finally(() => {
res.end()
})
})
})
app.use(express.static('dist'))
app.listen(PORT, () => {
/* ... */
})
実際のプロジェクトでは、ソースコードにシークレットキーを直接記載しないでください。 また、VCS にシークレットキーが記録されないように注意してください。
サーバーの動作確認
リソースを送信する機能と通知を送信する機能の動作確認をします。
先ほどと同じようにnpm run build
でビルドを行います。今度はサーバーのデプロイも必要です。
先ほどと同じく、node api/index.js
コマンドの実行とポートフォワーディングを行うツールを利用しても問題ありません。
Platformで確認できる起動リンクの節にあるQRコードをポケットサインアプリで読み取ると、デプロイしたミニアプリを起動できます。 今度はホーム画面から起動できます。 起動後、表示された「send notification」ボタンをタップすると、必要に応じて権限要求画面が表示され、サーバーへ通信送信リクエストが送られます。 サーバーではポケットサインのサーバーへプッシュ通知の送信要求を送ります。 そして、手元のポケットサインでプッシュ通知が表示されます。
本番環境へのデプロイと動作確認
本番環境のサービスをPlatformで作成してください。
モック環境のサービスと同じく「基本4情報における氏名」のリソース (ID: 50f423aa-8ea8-4385-81a1-a7d8da1c4939
) を READ 権限で要求するようにしてください。