충전 / 요금 API
imsend의 모든 발송 채널(SMS, 알림톡, MCP 등)은 하나의 포인트 잔액을 공유합니다.
충전은 TossPayments를 통해 처리되며, 발송 성공 시에만 포인트가 차감됩니다.
• 외부 서비스의 충전 창은 충전 세션으로 여세요. 브라우저 URL 에 API 키를 싣는 /charge?apiKey= 방식은 폐지 예정입니다(동작은 당분간 유지).
• 결제수단은 카드(간편결제 포함)입니다. 가상계좌는 입금돼도 포인트가 적립되지 않는 결함이 있어 선택지에서 제거했습니다. 가상계좌로 결제했는데 포인트가 들어오지 않았다면 info@imcorp.kr 로 가맹점 ID·입금 일시·금액을 알려 주세요.
포인트 잔액 1개 ← SMS API 차감 / 알림톡 차감 / MCP 차감
충전(TossPayments) → 포인트 증가 → 모든 채널에서 공통 사용
빌링 타입 조회
GET /api/v1/tenant/billing-type
가맹점의 빌링 타입(선불/후불)을 반환합니다. 잔액·단가 없이 가볍게 호출 가능하며, 프론트엔드가 충전 UI 표시 여부를 판단할 때 사용합니다.
GET /api/v1/tenant/billing-type
X-API-Key: your_api_key_here
응답 예시
{
"success": true,
"data": {
"tenantId": "moonpd",
"billingType": "PREPAID",
"showRechargeUI": true
}
}
billingType:"PREPAID"(선불) 또는"POSTPAID"(후불)showRechargeUI:billingType === "PREPAID"일 때true. 프론트 분기 단순화용
creditLimit) 내에서 발송하고 월말 정산하므로 충전 페이지가 불필요합니다.
잔액 조회
GET /api/v1/sms/balance
현재 포인트 잔액과 채널별 단가를 반환합니다.
GET /api/v1/sms/balance
X-API-Key: your_api_key_here
응답 예시
{
"success": true,
"data": {
"tenantId": "moonpd",
"tenantName": "문피디",
"balance": 4980,
"billingType": "PREPAID",
"creditLimit": 0,
"available": 4980,
"unit": "point",
"priceSms": 12,
"priceLms": 35,
"priceMms": 85,
"priceAlimtalk": 10,
"priceEmail": 1.2
}
}
- 응답은
data객체로 감싸집니다. 잔액 단위는"point"이며 포인트 ↔ 원(KRW) = 1:1입니다. available: 사용 가능 잔액.POSTPAID는balance + creditLimit,PREPAID는balance와 동일.
충전 세션 (권장)
POST /api/v1/charge/session — 2026-09-23 추가
연동 서비스의 서버가 API 키로 5분짜리 일회용 충전 주소를 발급받고, 브라우저는 그 주소만 팝업으로 엽니다. API 키가 브라우저·URL 에 나가지 않습니다. 결제 금액은 imsend 서버가 주문으로 저장한 값만 믿습니다.
공유 지갑 하위 가맹점(walletMode:"PARENT", 2026-09-24~)은 충전하지 않습니다 — 충전 세션 403 WALLET_PARENT_MANAGED, 기존 충전 화면도 거절. /api/v1/tenant/billing-type 의 showRechargeUI 가 false 이면 충전 버튼을 숨기세요.
1. 연동 서버 → POST /api/v1/charge/session(헤더 X-API-Key, 본문 없음) → url 받기
2. 브라우저에서 window.open(url, 'imsendCharge', 'width=480,height=720') 로 팝업 열기
3. 사용자가 금액(1만 / 3만 / 5만 / 10만원)을 고르고 카드(간편결제 포함)로 결제
4. 결과 화면이 "창을 닫아 주세요" 를 보여 주고, 닫기 버튼은 창만 닫습니다(부모 창에 메시지를 보내지 않음)
5. 연동 쪽은 팝업이 닫힌 것을 감지(popup.closed 주기 확인)해 GET /api/v1/sms/balance 로 잔액을 다시 읽습니다
응답
// 200
{
"success": true,
"data": {
"token": "q3Z0...(43자)",
"url": "https://imsend.kr/charge?token=q3Z0...",
"expiresIn": 300
}
}
// 401
{ "success": false, "code": "UNAUTHORIZED", "message": "유효하지 않은 API Key" }
// 409 — 후불(POSTPAID) 가맹점
{ "success": false, "code": "NOT_PREPAID", "message": "선불 가맹점만 충전할 수 있습니다." }
| 규칙 | 설명 |
|---|---|
| 유효 시간 | 300초. 지나면 만료 안내 화면이 뜹니다 — 새로 발급하세요. |
| 1회용 | 결제 버튼을 누르는 순간(주문 생성) 소비됩니다. 토스 결제창에서 취소했다면 같은 창에서 다시 결제할 수 없으니 창을 닫고 새 세션을 발급하세요. 주문 전까지는 새로고침해도 됩니다. |
| 대상 | 선불(PREPAID)·활성 가맹점만. 후불은 409 NOT_PREPAID. 발급 뒤 비활성·후불로 바뀌면 화면과 주문 단계에서 거부되고 토큰은 쓰이지 않습니다. 이미 카드 승인된 주문은 그대로 적립됩니다. |
| 결제수단 | 카드 전용(간편결제 포함). 가상계좌는 지원하지 않습니다. |
| 금액 | 1만 / 3만 / 5만 / 10만원 프리셋. 포인트 ↔ 원 = 1:1. |
| 주소 취급 | url·token 은 5분 동안 충전 화면을 열 수 있는 값입니다. 로그에 남기지 말고 발급받은 사용자에게만 여세요. |
(폐지 예정) API 키 URL 충전
GET /charge?apiKey={키}&returnUrl={사이트 내부 경로}
이 경로(GET /charge?apiKey=, 결제 성공 콜백)의 응답에는 폐지 예고 헤더가 붙습니다:
Deprecation: true · Link: </docs/billing#charge-session>; rel="deprecation"
옮기는 방법: 브라우저에서 키를 넣어 URL 을 만들던 부분을 → 서버에서 POST /api/v1/charge/session 을 호출해 받은 url 을 여는 것으로 바꿉니다. 결제 뒤에는 팝업 닫힘을 감지해 잔액을 다시 읽습니다(returnUrl 이동에 의존하지 않음). Java SDK 의 getChargePageUrl() 도 같은 이유로 @Deprecated 입니다.
포인트 ↔ 원(KRW) = 1:1. 충전 금액은 고정 프리셋 버튼(1만 / 3만 / 5만 / 10만원)으로만 선택합니다 — 임의 금액 직접 입력은 지원하지 않습니다.
postMessage팝업 사용을 권장합니다. iframe 안에서 열린 경우에만 충전 페이지가 부모 창에
postMessage 로 신호를 보냅니다. 대상 origin 은 고정입니다 — 충전 페이지 자신의 origin(https://imsend.kr)과 imsend 에 등록된 origin(https://호스트[:포트])에만 보내고, '*' 로는 보내지 않습니다(2026-09-23 변경). 다른 사이트에서 iframe 으로 신호를 받아야 하면 운영 문의로 origin 등록을 요청하세요. 받는 쪽도 event.origin === 'https://imsend.kr' 을 확인하세요.•
{ type:'imsend:charge:resize', height } — 카드결제 단계 등 필요한 높이(px). iframe 높이를 맞추세요(초기 권장 440×560).•
{ type:'imsend:charge:close', success } — 결제 완료/취소 시 모달을 닫고 잔액을 갱신하세요.메시지에는 키·토큰·금액이 들어가지 않습니다. 팝업(
window.open)으로 열면 결제 뒤 스스로 닫힙니다. 충전 세션 화면은 어떤 메시지도 보내지 않습니다.
URL 파라미터
| 파라미터 | 필수 | 설명 |
|---|---|---|
| apiKey | 필수 | 발급받은 API 키 |
| returnUrl | 선택 | 결제 완료 후 이동할 imsend 사이트 내부 상대경로(/ 로 시작, // 제외, URL 인코딩). 외부 절대 URL(https://…)은 오픈 리다이렉트 방지를 위해 무시되어 이동하지 않습니다 — 외부 서비스는 팝업 닫힘 감지로 처리하세요 |
예시
# 직접 URL 구성 (폐지 예정 — 새 연동은 충전 세션)
https://imsend.kr/charge?apiKey=your_key
# Java SDK (@Deprecated)
String url = client.payment().getChargePageUrl();
// 팝업으로 열기
window.open(url, '_blank', 'width=500,height=700');
충전 이력 조회
GET /api/v1/sms/point-history
| 파라미터 | 설명 |
|---|---|
| page | 페이지 번호 (기본: 1) |
| size | 페이지 크기 (기본: 50). 원장 list 와 결제 payments 가 각각 이 크기로 페이징됩니다 |
GET /api/v1/sms/point-history?page=1&size=20
X-API-Key: your_api_key_here
응답 예시
{
"success": true,
"balance": 4980,
"list": [
{
"ledger_seq": 1024,
"tx_type": "CHARGE",
"amount": 10000,
"balance_after": 14980,
"description": "TossPayments 충전",
"reg_date": "2026-05-04T14:23:00"
},
{
"ledger_seq": 1025,
"tx_type": "USE",
"amount": 12,
"balance_after": 14968,
"description": "SMS 발송 차감",
"reg_date": "2026-05-04T14:25:30"
}
],
"total": 48,
"payments": [
{
"payment_seq": 305,
"pg_type": "TOSS",
"amount": 10000,
"status": "DONE",
"order_id": "ORD-20260504-0001",
"reg_date": "2026-05-04T14:22:58"
}
]
}
- 필드명은 snake_case(
ledger_seq,tx_type,balance_after,reg_date)입니다. tx_type:CHARGE(충전) /USE(선불 차감) /CREDIT_USED(후불 차감) /REFUND(환불).total은 포인트 원장 전체 건수입니다.payments[]는im_payment의 실제 Toss 충전 레코드(DONE)를 함께 반환합니다.
월별 빌링 집계
GET /api/v1/sms/billing
연간 월별/분기별 발송 건수와 발송 비용(SUM(unit_price))을 집계하여 반환합니다.
GET /api/v1/sms/billing?year=2026
X-API-Key: your_api_key_here
응답 예시
{
"success": true,
"data": {
"tenantId": "moonpd",
"billingType": "PREPAID",
"creditLimit": 0,
"balance": 4980,
"year": "2026",
"months": [
{ "period": "2026-05", "total_count": 1100, "sms_count": 800, "lms_count": 300, "total_amount": 16520 },
{ "period": "2026-04", "total_count": 1900, "sms_count": 1500, "lms_count": 400, "total_amount": 28500 }
],
"quarters": [
{ "period": "2026-Q2", "total_count": 3000, "total_amount": 45020 }
],
"yearTotal": { "totalCount": 3000, "totalAmount": 45020 }
}
}
- 응답은
data객체로 감싸집니다. 월/분기 행의 식별자는period입니다. - 집계는 발송 비용(과금액) 합산만 제공합니다 — 충전 금액과의 분리(charge vs use) 항목은 없습니다.
total_amount는 해당 기간im_sms_log.unit_price합계,sms_count/lms_count는 유형별 건수입니다.
채널별 단가
발송 성공(수신 확인) 시에만 차감됩니다. imsend는 3중 단가 체계로 운영되며, 발송 경로·가맹점에 따라 실제 적용 단가가 달라집니다. 내 계정에 실제 적용되는 단가는 항상 잔액 조회 API로 확인하세요.
| 단가 체계 | 적용 대상 | 우선순위 |
|---|---|---|
| ① 기본 단가 | 모든 가맹점 공통 기본값 (아래 표의 파란색) | 최저 우선 |
| ② 가맹점별 협의 단가 | 계약·협의로 가맹점마다 별도 설정 — 설정 시 기본 단가에 우선 적용 | 기본보다 우선 |
| ③ MCP 단가 | AI 에이전트(MCP) 발송 전용 별도 체계 (아래 표의 보라색) | MCP 발송에만 |
📌 일반 API(X-API-Key) 발송은 ②가맹점별 협의 단가 → ①기본 단가 순으로 적용됩니다. AI 에이전트(MCP) 발송은 가맹점 단가와 무관하게 ③MCP 단가가 적용됩니다.
| 채널 | 단가 | 비고 |
|---|---|---|
| SMS (단문) | 12원/건 | 90바이트 이내 |
| LMS (장문) | 35원/건 | 2,000자 이내 |
| MMS | 85원/건 | 이미지 포함 |
| 이메일 | 1.2원/건 | HTML 지원 |
| 카카오 알림톡 | 10원/건 | 승인 템플릿 필요 |
| MCP — SMS | 17원/건 | AI 에이전트 발송 |
| MCP — LMS | 40원/건 | AI 에이전트 발송 |
| MCP — 이메일 | 1.5원/건 | AI 에이전트 발송 |
| MCP — 알림톡 | 15원/건 | AI 에이전트 발송 |
차감 정책
- 수신 확인(DELIVERED) 시 1회 차감 — 발송 시점에는 차감하지 않습니다. 결과 폴러(
SmsResultScheduler)가 GT Agent 결과를 수신해 DELIVERED로 확정될 때 단 한 번 차감합니다. - 실패 건 미차감 — FAILED 상태는 포인트 차감 없음
- TIMEOUT 자동 환불 — 결과 미수신(
TIMEOUT/NO_RESULT)으로 5분이 경과하면RecoveryScheduler가 실제 청구 단가(unit_price)를 환불(tx_type=REFUND)하고result_code='TIMEOUT_REFUNDED'로 마킹해 중복 환불을 방지합니다. - 과금 타입별 원장 기록 — 선불(PREPAID)은
tx_type=USE, 후불(POSTPAID)은tx_type=CREDIT_USED로 기록됩니다. - 잔액 부족 시 발송 차단 — 사전 잔액 확인 후 발송 요청
fixedDelay=30000)로 동작합니다. 수신 확인이 완료되는 시점에 차감되며, GT Agent 결과 도착 시점에 따라 차감 시각은 달라질 수 있습니다.
MCP 카카오페이 직접 결제
{ "tool": "charge_point", "amount": 10000 } // 5000, 10000, 30000 중 하나
// → { "authUrl": "카카오페이 결제 링크", "sessionId": "...", "expiresIn": 900 }
// 결제 완료 확인
{ "tool": "get_charge_status", "sessionId": "..." } // 또는 check_balance
자세한 사용법은 MCP 문서의 charge_point를 참고하세요.