본문으로 건너뛰기
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회 전송을 시도합니다: 첫 번째는 즉시, 이후 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회 시도합니다(첫 번째는 즉시, 이후 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= 뒤에 원본 요청 본문의 16진수 HMAC-SHA256이 붙습니다.String
X-Webhook-Signature-V2v1= 뒤에 "{timestamp}.{body}"의 16진수 HMAC-SHA256이 붙습니다. 시크릿 교체 기간에는 쉼표로 구분된 여러 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이번 갱신으로 생성된 새 교환 코드입니다. QR 코드 조회(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 알림

이벤트 구독

새 엔드포인트는 기본적으로 세 가지 이벤트를 모두 구독합니다. 기존 엔드포인트는 esim.renewal.success와 esim.renewal.failed를 자동으로 수신하므로 변경할 필요가 없습니다.

Webhook 알림

갱신 이벤트 처리

  • 핸들러를 멱등적으로 만드세요. 재시도로 같은 이벤트가 여러 번 전송될 수 있으므로 X-Webhook-Delivery 또는 event_type + data.code로 중복을 제거하고 이미 처리한 이벤트는 무시하세요.
  • 타임아웃 대체 처리를 사용하세요. 적절한 시간 내에 esim.renewal.success와 esim.renewal.failed 모두 도착하지 않으면 갱신이 실패했다고 가정하지 말고 조회 API(예: QR 코드 조회)로 코드를 조회하세요.
  • 환불 여부를 esim.renewal.failed로 판단하지 마세요. 이 이벤트는 업스트림 주문이 실패했다는 사실만 알립니다. 크레딧 환불 여부와 시기는 별도로 결정되므로 이 이벤트를 근거로 고객에게 환불하지 마세요.
Webhook 알림

Webhook 요청 검증

Webhook 요청의 iRoamly 서명을 검증합니다.

iRoamly 서명은 어떻게 검증하나요?

iRoamly 서명은 Webhook 요청의 X-Webhook-Signature 헤더에 sha256= 뒤에 16진수로 인코딩된 서명을 붙인 형태로 전송됩니다. 서명은 Webhook 시크릿을 키로, 원본 요청 본문(JSON 파싱 전, 받은 그대로)을 메시지로 하여 HMAC-SHA256 알고리즘으로 생성됩니다.

샘플 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에는 시크릿별 값이 쉼표로 구분되어 포함됩니다(v1=<old>,v1=<new>). 하나라도 일치하면 요청을 수락하세요.