跳至主要內容
iRoamly Shop

Webhook 通知

使用 Webhook 在您的應用程式中接收 QR Code 交付與續約結果。

Webhook 通知

簡介

使用 Webhook 在您的應用程式中接收 QR Code 交付與續約結果。

什麼是 Webhook?

iRoamly 會針對 QR Code 交付與續約結果傳送 Webhook 通知。每個 Webhook 皆使用 HTTPS,並包含 JSON 內容。您可以透過 Webhook:

  • 在兌換碼轉換為 QR Code 或直接購買 QR Code 完成後,接收 QR Code 資訊。
  • 透過 Webhook 接收續約結果:續約 QR Code 就緒時傳送 esim.renewal.success,上游續約訂單失敗時傳送 esim.renewal.failed。

若您的端點未回應 2xx 狀態,iRoamly 最多共嘗試傳送 5 次:第一次立即傳送,之後分別於 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 端點

在 iRoamly Shop 管理後台註冊可公開存取的 HTTPS URL。

您可以使用 ngrok 等工具建立連至本機伺服器的通道。

管理後台的 Webhook 通知頁面,已開啟「新增 Webhook」面板:Webhook URL 欄位與啟用/停用狀態

3. 測試您的 Webhook 端點是否正常運作

將一組測試兌換碼轉換為 QR Code,或直接購買一組 QR Code,確認您的 Webhook 端點有收到事件。

Webhook 通知

事件類型

支援的事件類型及其內容清單。

事件類型傳送時機備註
esim.qrcode兌換碼已轉換為 QR Code,或直接購買 QR Code 已完成。未變更。isrenew 固定為 0。
esim.renewal.success續約成功,且續約兌換碼的 QR Code 已就緒。新增。續約不再傳送 esim.qrcode。
esim.renewal.failed因無法建立上游訂單而導致續約失敗。新增。僅在上游訂單失敗時傳送。

傳送

所有事件的傳送方式相同:向每個訂閱該事件的端點發送帶有 JSON 內容的 POST 請求。任何 2xx 回應皆視為送達;否則會重試,最多共嘗試 5 次(第一次立即傳送,之後分別於 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。String
X-Webhook-Signature-V2v1= 後接 "{timestamp}.{body}" 的十六進位 HMAC-SHA256。密鑰輪換期間,會包含多個以逗號分隔的 v1= 值。String

請參閱「驗證 Webhook 請求」,了解如何檢查簽章。

esim.qrcode

兌換或購買 eSIM QR Code 後傳回的 eSIM QR Code 相關資訊。

請求內容範例

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 Code,其 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此次續約產生的新兌換碼。可用於「查詢 QR Code」(qrcodeQuery)或再次續約。String
data.renewal_father_code被續約的原始(父)兌換碼。String
data.renew_order_id上游續約訂單 ID。String
data.plan_name續約方案名稱。String
data.qrcodeQR Code 圖片 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 Code。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 Code 轉換與直接購買 QR Code。與 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 通知

事件訂閱

新端點預設會訂閱全部三種事件。現有端點會自動接收 esim.renewal.success 與 esim.renewal.failed,無需任何變更。

Webhook 通知

處理續約事件

  • 讓您的處理程式具備冪等性。重試可能使同一事件送達多次,因此請依 X-Webhook-Delivery 或 event_type + data.code 去除重複,並忽略已處理過的事件。
  • 使用逾時備援機制。若在合理時間內未收到 esim.renewal.success 或 esim.renewal.failed,請使用查詢 API(例如「查詢 QR Code」)查詢兌換碼,而非直接認定續約失敗。
  • 請勿以 esim.renewal.failed 判斷是否退款。此事件僅表示上游訂單失敗;是否及何時退還點數會另行決定,因此請勿依此事件為您的客戶退款。
Webhook 通知

驗證 Webhook 請求

驗證 Webhook 請求上的 iRoamly 簽章。

如何驗證 iRoamly 簽章?

iRoamly 簽章會放在 Webhook 請求的 X-Webhook-Signature 標頭中,格式為 sha256= 後接十六進位編碼的簽章。簽章以 HMAC-SHA256 演算法產生,以 Webhook 密鑰為金鑰,並以原始請求內容(與收到時完全相同,未經任何 JSON 解析)為訊息。

Webhook 標頭範例

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

您可以在管理後台的「開發者 > Webhook 通知」中找到 Webhook 密鑰。

管理後台的 Webhook 通知頁面:Webhook 端點及其 URL、簽章密鑰(顯示)與傳送紀錄

您可以使用以下程式碼片段產生簽章:

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。

輪換 Webhook 密鑰後,舊密鑰在 24 小時內仍然有效。在此期間,X-Webhook-Signature 仍以舊密鑰簽署,而 X-Webhook-Signature-V2 會針對每個密鑰各帶一個值,並以逗號分隔(v1=<old>,v1=<new>):只要任一值相符即可接受該請求。