Webhook 通知
使用 Webhook 在您的應用程式中接收 QR Code 交付與續約結果。
簡介
使用 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 路由:
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 等工具建立連至本機伺服器的通道。

3. 測試您的 Webhook 端點是否正常運作
將一組測試兌換碼轉換為 QR Code,或直接購買一組 QR Code,確認您的 Webhook 端點有收到事件。
事件類型
支援的事件類型及其內容清單。
傳送
所有事件的傳送方式相同:向每個訂閱該事件的端點發送帶有 JSON 內容的 POST 請求。任何 2xx 回應皆視為送達;否則會重試,最多共嘗試 5 次(第一次立即傳送,之後分別於 30 秒、2 分鐘、3 分鐘與 5 分鐘後重試)。每個請求皆攜帶以下標頭:
請參閱「驗證 Webhook 請求」,了解如何檢查簽章。
esim.qrcode
兌換或購買 eSIM QR Code 後傳回的 eSIM QR Code 相關資訊。
請求內容範例
{
"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"
}esim.renewal.success
續約成功時傳送:上游供應商傳回續約訂單的下載資訊後,系統會為續約兌換碼產生 QR Code,其 status 變為 2(已兌換)。
請求標頭
esim.renewal.success與所有事件相同,請求也會攜帶 X-Webhook-Delivery、X-Webhook-Timestamp、X-Webhook-Signature 與 X-Webhook-Signature-V2(請參閱「事件類型」)。
請求內容範例
{
"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"
}欄位
esim.renewal.failed
續約失敗時傳送:上游供應商對續約訂單回應了 0000 以外的代碼,或對供應商的請求失敗。
(注意:上游供應商的回呼遺失或延遲不會觸發此事件。請設定逾時並改為查詢兌換碼,詳見「處理續約事件」。)
請求標頭
esim.renewal.failed與所有事件相同,請求也會攜帶 X-Webhook-Delivery、X-Webhook-Timestamp、X-Webhook-Signature 與 X-Webhook-Signature-V2(請參閱「事件類型」)。
請求內容範例
{
"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"
}欄位
續約與 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。
事件訂閱
新端點預設會訂閱全部三種事件。現有端點會自動接收 esim.renewal.success 與 esim.renewal.failed,無需任何變更。
處理續約事件
- 讓您的處理程式具備冪等性。重試可能使同一事件送達多次,因此請依
X-Webhook-Delivery或event_type+data.code去除重複,並忽略已處理過的事件。 - 使用逾時備援機制。若在合理時間內未收到
esim.renewal.success或esim.renewal.failed,請使用查詢 API(例如「查詢 QR Code」)查詢兌換碼,而非直接認定續約失敗。 - 請勿以
esim.renewal.failed判斷是否退款。此事件僅表示上游訂單失敗;是否及何時退還點數會另行決定,因此請勿依此事件為您的客戶退款。
驗證 Webhook 請求
驗證 Webhook 請求上的 iRoamly 簽章。
如何驗證 iRoamly 簽章?
iRoamly 簽章會放在 Webhook 請求的 X-Webhook-Signature 標頭中,格式為 sha256= 後接十六進位編碼的簽章。簽章以 HMAC-SHA256 演算法產生,以 Webhook 密鑰為金鑰,並以原始請求內容(與收到時完全相同,未經任何 JSON 解析)為訊息。
Webhook 標頭範例
{
'X-Webhook-Signature': 'sha256=dd7bdd2cf1f6bac6e171c6c508c157b7cd3cc1fd196394277fb59ba0bdd9b87b'
}您可以在管理後台的「開發者 > Webhook 通知」中找到 Webhook 密鑰。

您可以使用以下程式碼片段產生簽章:
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>):只要任一值相符即可接受該請求。