本文へスキップ
iRoamly Shop

Webhook 通知

Webhook を使って、QR コードの配信結果と更新結果をアプリケーションで受け取ります。

Webhook 通知

はじめに

Webhook を使って、QR コードの配信結果と更新結果をアプリケーションで受け取ります。

Webhook とは?

iRoamly は、QR コードの配信結果と更新結果について Webhook 通知を送信します。各 Webhook は HTTPS を使用し、JSON ペイロードを含みます。Webhook を使って次のことができます:

  • 引き換えコードの QR コードへの変換、または QR コードの直接購入が完了した後に QR コード情報を受信する。
  • Webhook で更新結果を受信する: 更新の QR コードの準備ができると esim.renewal.success、上流の更新注文が失敗すると esim.renewal.failed。

エンドポイントが 2xx ステータスで応答しない場合、iRoamly は合計で最大 5 回配信を試みます: 1 回目は即時、その後 30 秒後、2 分後、3 分後、5 分後に再試行します。

Webhook を受信する手順

次の手順で、アプリでリアルタイムのイベントを受信できるようになります:

  • リクエストを受信するローカルエンドポイントを作成する
  • iRoamly Shop ダッシュボードの「開発者向け > Webhook 通知」で Webhook エンドポイントを登録する

1. リクエストを受信するローカルエンドポイントを作成する

ローカルアプリケーションに、POST リクエストを受け付ける新しいルートを作成します。

たとえば、Next.js に API ルートを追加できます:

typescript
import type { NextApiRequest, NextApiResponse } from 'next';

export default (req: NextApiRequest, res: NextApiResponse) => {
  if (req.method === 'POST') {
    const payload = req.body;
    console.log(payload);
    res.status(200);
  }
};

イベントを受信したら、HTTP 200 OK で応答して、イベントが正常に配信されたことを iRoamly に通知してください。

2. 開発用の Webhook エンドポイントを登録する

公開アクセス可能な HTTPS URL を iRoamly Shop ダッシュボードに登録します。

ngrok などのツールを使って、localhost サーバーへのトンネルを作成できます。

ダッシュボードの Webhook 通知ページで「新しい Webhook」パネルを開いた状態: Webhook URL 欄と有効/無効のステータス

3. Webhook エンドポイントが正しく動作することをテストする

テスト用の引き換えコードを QR コードに変換するか、QR コードを直接購入して、Webhook エンドポイントがイベントを受信することを確認します。

Webhook 通知

イベントタイプ

サポートされているイベントタイプとそのペイロードの一覧です。

イベントタイプ送信タイミング備考
esim.qrcode引き換えコードが QR コードに変換された、または QR コードの直接購入が完了したとき。変更なし。isrenew は常に 0。
esim.renewal.success更新が成功し、更新コードの QR コードの準備ができたとき。新規。更新時に esim.qrcode は送信されなくなりました。
esim.renewal.failed上流の注文を作成できず、更新が失敗したとき。新規。上流の注文が失敗した場合にのみ送信されます。

配信

すべてのイベントは同じ方法で配信されます: イベントを購読している各エンドポイントに、JSON ボディを含む POST リクエストを送信します。2xx の応答はすべて配信済みとみなされ、それ以外の場合は再試行され、合計で最大 5 回試行します(1 回目は即時、その後 30 秒後、2 分後、3 分後、5 分後に再試行)。各リクエストには次のヘッダーが含まれます:

ヘッダー説明型
X-Webhook-Eventイベントタイプ: esim.qrcode、esim.renewal.success、または esim.renewal.failed。String
X-Webhook-Delivery配信 ID。String
X-Webhook-Timestampリクエストに署名した時刻(Unix 秒)。String
X-Webhook-Signaturesha256= に続けて、生のリクエストボディの HMAC-SHA256(16 進数)。String
X-Webhook-Signature-V2v1= に続けて、"{timestamp}.{body}" の HMAC-SHA256(16 進数)。シークレットのローテーション中は、カンマ区切りの複数の v1= 値が含まれます。String

署名の確認方法については、「Webhook リクエストの検証」を参照してください。

esim.qrcode

eSIM QR コードの引き換えまたは購入後に返される、eSIM QR コードに関する情報。

リクエストボディのサンプル

json
{
	"code": 0,
	"data": {
		"activation_code": "CD112885573C49DDA42F64C905B925F4",
		"code": "UtTpoKCe769u2tt",
		"create_at": "2025-09-01 11:34:30",
		"isrenew": 0,
		"order_id": "ESIM202509021134343282",
		"plan_name": "Japan-eSIM-3GB-15 Days-Daily",
		"qrcode": "https://www.iroamly.com/qrcode/20250901/xSQfryvOWo0QUhsGcbpeTm9wWYfGu9BQ.png",
		"qrcode_content": "LPA:1$rsp1.cmlink.com$CD352885573C49DDA42F64C905B925F4",
		"sm_dp_address": "rsp1.cmlink.com"
	},
	"event_type": "esim.qrcode"
	}
Webhook 通知

esim.renewal.success

更新が成功したときに送信されます: 上流のプロバイダーが更新注文のダウンロード情報を返すと、更新コードの QR コードが生成され、その status が 2(引き換え済み)になります。

リクエストヘッダー

X-Webhook-Event:esim.renewal.success

このリクエストには、他のすべてのイベントと同様に X-Webhook-Delivery、X-Webhook-Timestamp、X-Webhook-Signature、X-Webhook-Signature-V2 も含まれます(イベントタイプを参照)。

リクエストボディのサンプル

json
{
	"code": 0,
	"data": {
		"activation_code": "CD112885573C49DDA42F64C905B925F4",
		"code": "RnB7xTq2Lm9pQaZ",
		"create_at": "2026-10-28 09:12:41",
		"download_url": "LPA:1$rsp1.cmlink.com$CD352885573C49DDA42F64C905B925F4",
		"iccid": "8944501234567890123",
		"iscouldrenewal": 1,
		"isrenew": 1,
		"plan_name": "Japan-eSIM-3GB-15 Days-Daily",
		"qrcode": "https://www.iroamly.com/qrcode/20261028/xSQfryvOWo0QUhsGcbpeTm9wWYfGu9BQ.png",
		"qrcode_content": "LPA:1$rsp1.cmlink.com$CD352885573C49DDA42F64C905B925F4",
		"renew_order_id": "TG202610280912410001",
		"renewal_father_code": "UtTpoKCe769u2tt",
		"sm_dp_address": "rsp1.cmlink.com"
	},
	"event_type": "esim.renewal.success"
}

フィールド

フィールド説明型
code常に 0。Int
event_typeesim.renewal.successString
data.codeこの更新で生成された新しい引き換えコード。Query QR Code(qrcodeQuery)で使用したり、再度更新したりできます。String
data.renewal_father_code更新された元の(親)引き換えコード。String
data.renew_order_id上流の更新注文 ID。String
data.plan_name更新プラン名。String
data.qrcodeQR コード画像の URL。String
data.qrcode_contentLPA 文字列。そのままインポートできます。String
data.sm_dp_addressSM-DP+ アドレス。String
data.activation_codeアクティベーションコード。String
data.iccidICCID。空の場合があります。String
data.download_url上流のプロバイダーが返すダウンロード文字列。String
data.create_at作成日時: サーバー時刻、YYYY-MM-DD HH:mm:ss 形式。String
data.isrenew常に 1。Int
data.iscouldrenewal新しい引き換えコードを再度更新できるかどうか: 1 - 更新可能 2 - 更新不可 3 - 期限切れInt
Webhook 通知

esim.renewal.failed

更新が失敗したときに送信されます: 上流のプロバイダーが更新注文に対して 0000 以外のコードを返した場合、またはプロバイダーへのリクエストが失敗した場合です。

(注: 上流のプロバイダーからのコールバックがない、または遅れている場合、このイベントは発生しません。タイムアウトを設定し、コードの照会にフォールバックしてください。「更新イベントの処理」を参照してください。)

リクエストヘッダー

X-Webhook-Event:esim.renewal.failed

このリクエストには、他のすべてのイベントと同様に X-Webhook-Delivery、X-Webhook-Timestamp、X-Webhook-Signature、X-Webhook-Signature-V2 も含まれます(イベントタイプを参照)。

リクエストボディのサンプル

json
{
	"code": -1,
	"data": {
		"code": "RnB7xTq2Lm9pQaZ",
		"create_at": "2026-10-28 09:12:41",
		"error_msg": "TuGe_RenewOrderEx RespCode: 9999",
		"isrenew": 1,
		"plan_name": "Japan-eSIM-3GB-15 Days-Daily",
		"renewal_father_code": "UtTpoKCe769u2tt"
	},
	"event_type": "esim.renewal.failed"
}

フィールド

フィールド説明型
code常に -1。Int
event_typeesim.renewal.failedString
data.codeこの試行で生成された更新コード。プロビジョニングされておらず、QR コードはありません。String
data.renewal_father_code更新対象だった元の(親)引き換えコード。String
data.plan_name更新プラン名。String
data.error_msg上流のプロバイダーが返したエラー(トラブルシューティング用)。String
data.create_at作成日時: サーバー時刻、YYYY-MM-DD HH:mm:ss 形式。String
data.isrenew常に 1。Int
Webhook 通知

更新と esim.qrcode

更新は esim.qrcode として配信されなくなりました。このイベントは QR コードへの変換と QR コードの直接購入で送信されます。esim.qrcode のペイロードとの違い:

  • event_type と X-Webhook-Event ヘッダーは esim.renewal.success または esim.renewal.failed で、isrenew は 1 です。
  • data.code は更新で作成された新しい引き換えコードで、更新された元のコードは data.renewal_father_code です。
  • esim.renewal.success には renew_order_id、renewal_father_code、iscouldrenewal、download_url、iccid が追加され、order_id は含まれません。
  • 更新の失敗は esim.renewal.failed で通知され、トップレベルの code は -1、上流のエラーは data.error_msg に含まれます。
Webhook 通知

イベントの購読

新しいエンドポイントは、デフォルトで 3 つのイベントすべてを購読します。既存のエンドポイントも esim.renewal.success と esim.renewal.failed を自動的に受信します。変更は不要です。

Webhook 通知

更新イベントの処理

  • ハンドラーは冪等にしてください。再試行により同じイベントが複数回配信される場合があるため、X-Webhook-Delivery または event_type + data.code で重複を排除し、処理済みのイベントは無視してください。
  • タイムアウト時のフォールバックを用意してください。妥当な時間内に esim.renewal.success も esim.renewal.failed も届かない場合は、更新が失敗したとみなさず、照会 API(Query QR Code など)でコードを照会してください。
  • esim.renewal.failed を返金の判断に使用しないでください。このイベントは上流の注文が失敗したことのみを通知します。クレジットを返金するかどうか、またその時期は別途決定されるため、このイベントに基づいてお客様に返金しないでください。
Webhook 通知

Webhook リクエストの検証

Webhook リクエストの iRoamly 署名を検証します。

iRoamly 署名を検証するには?

iRoamly 署名は、Webhook リクエストの X-Webhook-Signature ヘッダーで、sha256= に続く 16 進エンコードされた署名として送信されます。署名は HMAC-SHA256 アルゴリズムで生成され、キーには Webhook シークレット、メッセージには生のリクエストボディ(JSON を解析する前の、受信したそのままの状態)を使用します。

Webhook ヘッダーのサンプル

json
{
'X-Webhook-Signature': 'sha256=dd7bdd2cf1f6bac6e171c6c508c157b7cd3cc1fd196394277fb59ba0bdd9b87b'
}

Webhook シークレットはダッシュボードの「開発者向け > Webhook 通知」で確認できます。

ダッシュボードの Webhook 通知ページ: URL と署名シークレット(表示)を含む Webhook エンドポイント、および配信履歴

署名を生成するには、次のコードスニペットを使用できます:

typescript
import * as crypto from 'crypto';

  generateSignature(payload: string, secret: string): string {
    const computedSignature = crypto
      .createHmac('sha256', secret)
      .update(payload)
      .digest('hex');
    return 'sha256=' + computedSignature;
  }

上のコードスニペットでは、payload が生のリクエストボディ、secret が Webhook シークレットです。生成した署名(sha256= プレフィックスを含む)を X-Webhook-Signature ヘッダーの値と比較するだけで検証が完了します。

タイムスタンプ付き署名(X-Webhook-Signature-V2)

各リクエストには X-Webhook-Signature-V2 も含まれます: v1= に続けて、同じ Webhook シークレットで {timestamp}.{body}(X-Webhook-Timestamp ヘッダーの値、ドット、生のリクエストボディ)を HMAC-SHA256 で計算し、16 進エンコードした値です。

Webhook シークレットをローテーションした後も、古いシークレットは 24 時間有効です。その間、X-Webhook-Signature は引き続き古いシークレットで署名され、X-Webhook-Signature-V2 にはシークレットごとに 1 つずつ、カンマ区切りで値が含まれます(v1=<old>,v1=<new>): いずれかの値が一致すればリクエストを受け入れてください。