갤럭시아 상품권 발행 게이트웨이 — 내부 전용, 중앙 토큰 인증, relay 경유. modoo 플랫폼이 이 워커로 상품권을 발행/발행취소하고 운영 정보를 조회한다. 갤럭시아머니트리(GalaxiaMoneyTree) 표준 연동 발행 규격서 v1.8.0 기반. 범위: 발행(issue)·발행취소(cancel, **환불은 발행취소로 처리**)·메시지재전송(resend) + 정보제공(상품권/이미지/발행처/잔액·한도/이력). 유효기간연장·환불접수(잔액환불)는 미제공. 흐름: 플랫폼 →(Bearer 토큰) /api/issue(카탈로그 productId 단일 방식, 1요청=1장) → 워커가 relay(고정IP) 경유로 갤럭시아 발행 → 핀/바코드 반환. 발행 시 platform·hash(유니크) 를 함께 받아 추적(동일 hash 재요청은 중복발행 차단). 액면은 productId 가 결정(자유 액면 입력 없음): dept(백화점)는 액면별 상품 1/5/10/50만, cpn(정액)도 카탈로그 고정. 시각은 KST(+09:00).
1) 상품 매핑(안정키): productId 는 환경별로 다를 수 있다. (goodsGroupId, faceValue) 를 안정키로 저장하고 발행 직전 /api/products 로 productId 를 resolve 하라. (goodsGroupId, faceValue) 는 한 환경 내 유일하며 상품 개편·이름/이미지 변경에도 동일 상품이면 유지된다. 같은 브랜드의 액면들은 brand·goodsGroupId 동일, faceValue 만 다르다. 2) 발행 멱등 + 재발행: 발행마다 platform 과 hash(플랫폼 내 유일·불변, 주문ID/UUID 권장)를 보내라. 같은 (platform, hash) 의 활성 발행건(completed/requested)이 있으면 재발행하지 않고 duplicate=true 로 반환(이중발행·중복과금 차단). 취소/실패 건은 같은 hash 를 유지한 채 재발행 허용 → 새 orderNumber 로 갤럭시아에 새 상품권을 발행한다(취소→재사용, 실패→재시도 지원). hash 전송 시 platform 필수. 3) 실패/타임아웃 복구: 응답 미수신/실패 시 GET /api/issues/by-hash/:hash?platform=... 로 상태 확인 — completed(발행됨, barcode·pinNumber 회수)/failed(미발행)/requested(미확정 → by-hash 응답의 transactionId 로 GET /api/info 를 호출해 갤럭시아에서 실시간 확정 후 재발행 판단. transactionId 가 비어 있으면 갤럭시아에 도달하지 못한 것이므로 미발행)/404(요청기록 없음=확실히 미발행). 4) 과금: 발행취소(/api/cancel, 미사용·기간내)는 과금 없음(환원). 미발행(실패·타임아웃)은 비용 없음. 정산은 플랫폼이 고려할 필요 없음(액면가 판매; 수수료·정산은 발행처와 별도). 정합성은 운영자가 발행기록·잔액 대조로 수동 점검. 5) 환불 = 발행취소(/api/cancel): 별도 환불 API 는 쓰지 않는다. ★ **취소 대상은 transactionId(갤럭시아 거래번호)로만 지정한다 — 주문번호(orderNumber)로는 취소되지 않는다.** 거래번호는 발행 응답에 들어 있고, 잊었으면 GET /api/issues/:orderNumber · /api/issues/by-hash/:hash 로 회수한다. **발행 완료 + 유효기간 이내 + 미사용** 상태만 취소 가능하며(스펙 제한), 취소 후 같은 hash 로 재발행할 수 있다. **취소 완료 건의 복구는 불가**. 취소 요청번호(order-number)는 서버가 새로 유일하게 생성한다(원 발행번호 재사용 시 4900 중복). dept 는 원 발행 요청 금액과 같은 faceValue 를 함께 보내야 한다. dept 취소 알림 SMS 는 항상 발송(sendMsg=Y 고정, 호출측 제어 불가). **취소 가능 여부 사전 확인(권장)**: 취소 버튼을 노출하기 전에 GET /api/info 로 **isRevocable(취소가능)·isUsed(사용여부)·couponStatus(ACTIVE/CANCEL/INACTIVE)·validTo** 를 확인하라. 이미 사용/만료된 건은 갤럭시아가 취소를 거절하므로(사용된 상품권이 취소될 일은 없다), 실패 시 resCode 로 사유를 안내하면 된다 — cpn: 2206 사용된 쿠폰 · 2205 만료 · 2204 이미 취소 · 2220 취소 불가 / dept: 020016 사용불가 상태 · 030012 취소 불가 상태 · 030007·030016 유효기간 경과. **메시지 재전송(/api/resend)**: 발행 시 보낸 안내 메시지를 다시 발송한다. 대상은 **transactionId 로만** 지정하고, 재전송 요청번호는 서버가 새로 유일하게 생성한다(스펙: order-number 는 유일값, 중복 불가). **기본 3회 한도** — 초과 5701 · 재전송 원본 없음 5702 · 잔액 0원이면 불가 5703. 6) ★★ 보관 키 = transactionId (단일 식별 수단): 발행 응답의 **transactionId 를 반드시 저장하라.** 발행 이후의 모든 처리 — **정보조회(/api/info)·발행취소(/api/cancel)·메시지재전송(/api/resend)** — 는 transactionId 로만 대상을 지정한다. 주문번호(orderNumber)로는 어느 것도 처리되지 않으며, 발행 요청 시 주문번호를 보낼 수도 없다(서버가 생성). 이유: 주문번호는 호출측이 정하는 값이라 남이 발행한 상품권까지 지정될 수 있다. giftKind 도 함께 저장하고(모든 호출에 필요), platform·hash·barcode·pinNumber 도 보관 권장. transactionId 를 잃어버렸으면 GET /api/issues/by-hash/:hash?platform=… 로 회수하라. 7) PIN/바코드: barcode 항상 반환, pinNumber 상품별 optional. by-hash 또는 /api/issues/:orderNumber 로 회수. 수령자 사용기준(지류교환/번호등록 등)은 상품 usageNotice 참조. 8) 카탈로그 동기화: /api/products 의 updatedAt(KST)로 변경 감지(변경 webhook 없음). 기본 활성만 반환, ?includeInactive=true 면 단종(isActive:false)도 포함(데이터 보존). 페이징·레이트리밋 없음 — '발행 전 항상 조회'는 부하가 크니 5~10분 캐시 권장. dept 도 imageUrl 제공. 9) 오류코드(resCode): 0000=성공. 020014=발행 한도부족(재시도 가능). 010xxx=데이터/형식 오류·020001 미등록·020013 발행처-상품 매칭오류(재시도 불가, 요청 수정). 취소 실패: 이미취소/유효기간만료/취소불가 상태. 10) 기타: 시각 KST(+09:00), 금액 정수(원). 알림은 발행처가 recipientPhone 로 발송(번호 형식검증 위임 — 본인인증 완료 번호 전제). 테스트모드/샌드박스 없음(발행은 실제). dept 액면 1/5/10/50만 고정. category 필드 미제공(brand 로 분류).
Authorization: Bearer <token>
/help
공개
/help/prompt
공개
/health
공개
/api/issuer
🔒 토큰
{ "success": true, "data": {
"companyName": "갤럭시아머니트리(주)", "commissionRate": 2.2,
"bizRegNo": "120-81-60844", "ceo": "신동훈", "bizType": "모바일 상품권 및 쿠폰 발행업",
"bizCategory": "IT/통신, 핀테크, 전자결제", "phone": "1566-0123", "address": "(06349) 서울…",
"baseUrl": "https://mcoupon.mdpt.co.kr", "companyCode": "modoovoucher", "giftKind": "dept",
"aesMode": "128", "msgCallback": "15660123", "saleChannel": "mvoucher_web",
"aesConfigured": true, "updatedAt": "…" } }/api/products
🔒 토큰
?includeInactive=true (선택: 단종 상품 포함)
{ "success": true, "data": [
{ "id": 6, "brand": "대한항공", "name": "대한항공 기프트카드 5만원권",
"giftKind": "cpn", "goodsGroupId": "1000603641", "faceValue": 50000, "price": 50000,
"discountRate": 2.5, "validityDays": 1826, "isActive": true, "updatedAt": "2026-06-16T...+09:00",
"imageUrl": "https://voucher.modooapi.com/images/대한항공/...png",
"usageNotice": "대한항공 홈페이지/앱에서 기프트카드 등록 후 사용. …" },
{ "id": 27, "brand": "AK", "name": "AK모바일교환권 5만원권", "giftKind": "dept",
"goodsGroupId": "GI10002061", "faceValue": 50000, "price": 50000,
"discountRate": 2.5, "validityDays": 1826, "isActive": true,
"imageUrl": "…", "usageNotice": "[발행사] 갤럭시아머니트리. AK PLAZA 전점 …" } ] }
// 목록에도 usageNotice 가 포함된다(상세 전용 아님). price 는 현재 전 상품이 faceValue 와 동일하다.
// id 는 환경별로 다를 수 있으니 (goodsGroupId, faceValue)로 매핑하고 발행 직전 /api/products 로 id 를 resolve./api/products/:id
🔒 토큰
{ "success": true, "data": { "id": 6, "brand": "대한항공", "name": "대한항공 기프트카드 5만원권",
"giftKind": "cpn", "goodsGroupId": "1000603641", "faceValue": 50000, "price": 50000,
"discountRate": 2.5, "validityDays": 1826, "isActive": true, "updatedAt": "…",
"imageUrl": "…", "usageNotice": "…" } }/api/issue
🔒 토큰
{ "productId": 27, "recipientPhone": "01012345678", "platform": "modoogift", "hash": "a1b2c3...", "ref": "order-123" }
// 액면은 productId 가 결정한다(예: productId 27 = AK 5만원권). faceValue 를 보내도 무시된다. id 는 /api/products 로 조회.
// 선택 파라미터(위 예시에 없지만 실제로 받는다):
// buyerPhone 발송자 번호. 미입력 시 갤럭시아가 recipientPhone 과 동일하게 설정한다.
// msg 안내 메시지에 넣을 사용자 지정 문구(250Byte 제한). dept·cpn 공통.
// msgType "MMS"(기본) | "LMS" | "SMS". **cpn 전용**(dept 는 무시).
// duration 쿠폰 유효기간(일). **cpn 전용**. 미지정·0 이면 최대 유효기간.
// reserveDate 예약발송 일시 yyyyMMddHHmmss. **cpn 전용**. 값이 올바르지 않으면 즉시발송된다.
// ref 호출측 참조 문자열(발행이력에 그대로 보관).
// ★ 주문번호(orderNumber)는 받지 않는다 — 서버가 생성한다(V + KST타임스탬프 + 난수). 호출측 식별은 platform+hash·ref 로 하라.
// 발행 후 조회·취소·재전송은 **응답의 transactionId 단일 수단**으로 처리한다.{ "success": true, "data": {
"orderNumber": "V202606160930001234", "transactionId": "...", "issueNumber": "...",
"barcode": "...", "pinNumber": "...", "faceValue": "50000",
"issueDate": "20260616093001", "validTo": "20310615", "msgAdditionalInfo": null } }
// ★★ 타입 주의: 발행 응답의 faceValue 는 갤럭시아 암호화 필드를 복호화한 값이라 **문자열**이다("50000").
// transactionId·issueNumber·barcode 도 같은 이유로 문자열.
// 중복(동일 hash) — 정상 발행과 같은 키 집합을 그대로 돌려준다. barcode·pinNumber 포함이므로 추가 호출이 필요 없다:
// { "success": true, "duplicate": true, "data": {
// "orderNumber": "V...", "status": "completed", "platform": "modoogift", "hash": "a1b2c3...",
// "transactionId": "...", "issueNumber": "...", "barcode": "...", "pinNumber": "...",
// "faceValue": 50000, "issueDate": "20260616093001", "validTo": "20310615" } }
// 단 이 경로의 값은 우리 DB 에서 읽으므로 faceValue 가 **숫자**다(발행 응답은 문자열 — 타입이 다르니 숫자 변환 후 비교하라).
// status 가 "requested"(발행 진행 중 미확정)면 success:false 이고 barcode·pinNumber 는 null 일 수 있다 → /api/info 로 확정하라.
// 실패: { "success": false, "error": "발행 한도 부족", "ordNo": "...", "resCode": "020014" }/api/cancel
🔒 토큰
{ "giftKind": "cpn", "transactionId": "2063240001" }
// transactionId·giftKind 필수. 누락 시 400.
// 선택 파라미터:
// faceValue 대상 상품권 발행 액면가. **dept 는 원 발행 요청 금액과 동일하게 필수로 넣어라**(cpn 은 불필요).
// issueDay 원 발행일 YYYYMMDD.
// 백화점 예시: { "giftKind": "dept", "transactionId": "TR2608...", "faceValue": 50000, "issueDay": "20260802" }{ "success": true, "data": { "transactionId": "...", "cancelDate": "20260616100000" } }
// 실패: { "success": false, "error": "이미 사용된 쿠폰", "resCode": "2206" }/api/resend
🔒 토큰
{ "giftKind": "cpn", "transactionId": "2063240001" }{ "success": true, "data": {} }
// 실패: { "success": false, "error": "메시지 재전송 횟수 초과(기본 3회)", "resCode": "5701" }/api/info
🔒 토큰
?giftKind=cpn&transactionId=2063240001
{ "success": true, "data": { "transactionId": null, "couponStatus": "CANCEL",
"issueDate": "20260805051159", "faceValue": "10000", "balance": "10000",
"validFrom": "20260805", "validTo": "20270804", "barcode": "4530000697734295",
"isRevocable": "false", "isUsed": "false", "usedDate": null } }
// ↑ 실제 운영 응답 그대로(가공 없음). ★★ 타입 주의: faceValue·balance·isRevocable·isUsed 는 모두 **문자열**이다.
// 갤럭시아 원본 값을 그대로 전달하므로 숫자·불리언이 아니다. 판정은 String(v) === "true" 로 하라.
// (isRevocable 을 그대로 if 조건에 넣으면 "false" 도 참이 되어 이미 사용된 건이 통과한다.)
// ★ 취소 전 사전 확인용: isRevocable(취소 가능 여부)·isUsed(사용 여부)·usedDate·couponStatus(ACTIVE/CANCEL/INACTIVE)·validTo/api/limit
🔒 토큰
?giftKind=dept&goodsGroupId=GI10002061
{ "success": true, "data": { "limitPrice": 5000000 } }/api/issues/:orderNumber
🔒 토큰
{ "success": true, "data": { "orderNumber": "V...", "status": "completed",
"barcode": "...", "pinNumber": "...", "balance": 100000 } }/api/issues/by-hash/:hash
🔒 토큰
?platform=modoogift (권장: hash 범위 한정)
{ "success": true, "data": { "orderNumber": "V...", "status": "completed",
"platform": "modoogift", "hash": "a1b2c3...", "barcode": "...", "pinNumber": "...", "faceValue": 100000 } }/api/issues/:orderNumber/logs
🔒 토큰
{ "success": true, "data": [ { "direction": "request", "api_name": "issueCoupon", "payload": {...} } ] }/webhook/usage/:secret
🛡 IP제한
Content-Type: application/xml <Result><transaction><appdiv>10</appdiv><barcode>..</barcode><amount>ENC</amount> <remainprice>ENC</remainprice><orderID>..</orderID></transaction></Result>
<Result><resCode>0000</resCode><resMsg>Success</resMsg></Result>
/images/:brand/:file
공개
/console
🔑 관리자
GET https://voucher.modooapi.com/help/prompt)# modooapi-workers-voucher 연동 가이드 (AI 에이전트용)
너는 modooapi 의 "modooapi-workers-voucher" API 를 호출하는 통합 에이전트다. 아래 명세대로 정확히 요청을 구성하라.
- Base URL: https://voucher.modooapi.com
- 인증: modooapi.com/console 에서 발급한 중앙 액세스 토큰을 모든 /api/* 요청에 `Authorization: Bearer <token>` 헤더로 전송한다.
- 공통 응답: 성공 { "success": true, "data": ... }, 실패 { "success": false, "error": "<메시지>" }.
- 개요: modoo 플랫폼이 이 워커로 상품권을 발행/발행취소하고 운영 정보를 조회한다. 갤럭시아머니트리(GalaxiaMoneyTree) 표준 연동 발행 규격서 v1.8.0 기반. 범위: 발행(issue)·발행취소(cancel, **환불은 발행취소로 처리**)·메시지재전송(resend) + 정보제공(상품권/이미지/발행처/잔액·한도/이력). 유효기간연장·환불접수(잔액환불)는 미제공. 흐름: 플랫폼 →(Bearer 토큰) /api/issue(카탈로그 productId 단일 방식, 1요청=1장) → 워커가 relay(고정IP) 경유로 갤럭시아 발행 → 핀/바코드 반환. 발행 시 platform·hash(유니크) 를 함께 받아 추적(동일 hash 재요청은 중복발행 차단). 액면은 productId 가 결정(자유 액면 입력 없음): dept(백화점)는 액면별 상품 1/5/10/50만, cpn(정액)도 카탈로그 고정. 시각은 KST(+09:00).
## 연동 가이드
1) 상품 매핑(안정키): productId 는 환경별로 다를 수 있다. (goodsGroupId, faceValue) 를 안정키로 저장하고 발행 직전 /api/products 로 productId 를 resolve 하라. (goodsGroupId, faceValue) 는 한 환경 내 유일하며 상품 개편·이름/이미지 변경에도 동일 상품이면 유지된다. 같은 브랜드의 액면들은 brand·goodsGroupId 동일, faceValue 만 다르다.
2) 발행 멱등 + 재발행: 발행마다 platform 과 hash(플랫폼 내 유일·불변, 주문ID/UUID 권장)를 보내라. 같은 (platform, hash) 의 활성 발행건(completed/requested)이 있으면 재발행하지 않고 duplicate=true 로 반환(이중발행·중복과금 차단). 취소/실패 건은 같은 hash 를 유지한 채 재발행 허용 → 새 orderNumber 로 갤럭시아에 새 상품권을 발행한다(취소→재사용, 실패→재시도 지원). hash 전송 시 platform 필수.
3) 실패/타임아웃 복구: 응답 미수신/실패 시 GET /api/issues/by-hash/:hash?platform=... 로 상태 확인 — completed(발행됨, barcode·pinNumber 회수)/failed(미발행)/requested(미확정 → by-hash 응답의 transactionId 로 GET /api/info 를 호출해 갤럭시아에서 실시간 확정 후 재발행 판단. transactionId 가 비어 있으면 갤럭시아에 도달하지 못한 것이므로 미발행)/404(요청기록 없음=확실히 미발행).
4) 과금: 발행취소(/api/cancel, 미사용·기간내)는 과금 없음(환원). 미발행(실패·타임아웃)은 비용 없음. 정산은 플랫폼이 고려할 필요 없음(액면가 판매; 수수료·정산은 발행처와 별도). 정합성은 운영자가 발행기록·잔액 대조로 수동 점검.
5) 환불 = 발행취소(/api/cancel): 별도 환불 API 는 쓰지 않는다. ★ **취소 대상은 transactionId(갤럭시아 거래번호)로만 지정한다 — 주문번호(orderNumber)로는 취소되지 않는다.** 거래번호는 발행 응답에 들어 있고, 잊었으면 GET /api/issues/:orderNumber · /api/issues/by-hash/:hash 로 회수한다. **발행 완료 + 유효기간 이내 + 미사용** 상태만 취소 가능하며(스펙 제한), 취소 후 같은 hash 로 재발행할 수 있다. **취소 완료 건의 복구는 불가**. 취소 요청번호(order-number)는 서버가 새로 유일하게 생성한다(원 발행번호 재사용 시 4900 중복). dept 는 원 발행 요청 금액과 같은 faceValue 를 함께 보내야 한다. dept 취소 알림 SMS 는 항상 발송(sendMsg=Y 고정, 호출측 제어 불가).
**취소 가능 여부 사전 확인(권장)**: 취소 버튼을 노출하기 전에 GET /api/info 로 **isRevocable(취소가능)·isUsed(사용여부)·couponStatus(ACTIVE/CANCEL/INACTIVE)·validTo** 를 확인하라. 이미 사용/만료된 건은 갤럭시아가 취소를 거절하므로(사용된 상품권이 취소될 일은 없다), 실패 시 resCode 로 사유를 안내하면 된다 — cpn: 2206 사용된 쿠폰 · 2205 만료 · 2204 이미 취소 · 2220 취소 불가 / dept: 020016 사용불가 상태 · 030012 취소 불가 상태 · 030007·030016 유효기간 경과.
**메시지 재전송(/api/resend)**: 발행 시 보낸 안내 메시지를 다시 발송한다. 대상은 **transactionId 로만** 지정하고, 재전송 요청번호는 서버가 새로 유일하게 생성한다(스펙: order-number 는 유일값, 중복 불가). **기본 3회 한도** — 초과 5701 · 재전송 원본 없음 5702 · 잔액 0원이면 불가 5703.
6) ★★ 보관 키 = transactionId (단일 식별 수단): 발행 응답의 **transactionId 를 반드시 저장하라.** 발행 이후의 모든 처리 — **정보조회(/api/info)·발행취소(/api/cancel)·메시지재전송(/api/resend)** — 는 transactionId 로만 대상을 지정한다. 주문번호(orderNumber)로는 어느 것도 처리되지 않으며, 발행 요청 시 주문번호를 보낼 수도 없다(서버가 생성). 이유: 주문번호는 호출측이 정하는 값이라 남이 발행한 상품권까지 지정될 수 있다. giftKind 도 함께 저장하고(모든 호출에 필요), platform·hash·barcode·pinNumber 도 보관 권장. transactionId 를 잃어버렸으면 GET /api/issues/by-hash/:hash?platform=… 로 회수하라.
7) PIN/바코드: barcode 항상 반환, pinNumber 상품별 optional. by-hash 또는 /api/issues/:orderNumber 로 회수. 수령자 사용기준(지류교환/번호등록 등)은 상품 usageNotice 참조.
8) 카탈로그 동기화: /api/products 의 updatedAt(KST)로 변경 감지(변경 webhook 없음). 기본 활성만 반환, ?includeInactive=true 면 단종(isActive:false)도 포함(데이터 보존). 페이징·레이트리밋 없음 — '발행 전 항상 조회'는 부하가 크니 5~10분 캐시 권장. dept 도 imageUrl 제공.
9) 오류코드(resCode): 0000=성공. 020014=발행 한도부족(재시도 가능). 010xxx=데이터/형식 오류·020001 미등록·020013 발행처-상품 매칭오류(재시도 불가, 요청 수정). 취소 실패: 이미취소/유효기간만료/취소불가 상태.
10) 기타: 시각 KST(+09:00), 금액 정수(원). 알림은 발행처가 recipientPhone 로 발송(번호 형식검증 위임 — 본인인증 완료 번호 전제). 테스트모드/샌드박스 없음(발행은 실제). dept 액면 1/5/10/50만 고정. category 필드 미제공(brand 로 분류).
## 엔드포인트
### GET https://voucher.modooapi.com/api/issuer [🔒 토큰]
발행처(이슈어) 정보 조회 — 활성 발행처의 회사정보·갤럭시아 연동설정(시크릿 AES KEY/IV 값 제외, 설정여부만). 미설정 시 404.
응답:
{ "success": true, "data": {
"companyName": "갤럭시아머니트리(주)", "commissionRate": 2.2,
"bizRegNo": "120-81-60844", "ceo": "신동훈", "bizType": "모바일 상품권 및 쿠폰 발행업",
"bizCategory": "IT/통신, 핀테크, 전자결제", "phone": "1566-0123", "address": "(06349) 서울…",
"baseUrl": "https://mcoupon.mdpt.co.kr", "companyCode": "modoovoucher", "giftKind": "dept",
"aesMode": "128", "msgCallback": "15660123", "saleChannel": "mvoucher_web",
"aesConfigured": true, "updatedAt": "…" } }
### GET https://voucher.modooapi.com/api/products [🔒 토큰]
상품권 카탈로그(목록·액면·할인율·유효기간·이미지·안내) — 발행 가능한 상품권 목록. 여기의 id(productId) 로 /api/issue 한다(발행은 productId 단일 방식). 액면은 상품마다 고정(faceValue) — dept(백화점)도 액면별 상품으로 제공(1/5/10/50만원). 같은 브랜드의 액면들은 brand·goodsGroupId 가 동일하고 faceValue 만 다르다. 안정 식별키=(goodsGroupId, faceValue)(환경·개편 불변, 유일). updatedAt(KST)로 변경 감지. 기본은 활성만; ?includeInactive=true 면 단종(isActive:false)도 포함. 페이징 없음(전건). dept 도 imageUrl 제공.
요청:
?includeInactive=true (선택: 단종 상품 포함)
응답:
{ "success": true, "data": [
{ "id": 6, "brand": "대한항공", "name": "대한항공 기프트카드 5만원권",
"giftKind": "cpn", "goodsGroupId": "1000603641", "faceValue": 50000, "price": 50000,
"discountRate": 2.5, "validityDays": 1826, "isActive": true, "updatedAt": "2026-06-16T...+09:00",
"imageUrl": "https://voucher.modooapi.com/images/대한항공/...png",
"usageNotice": "대한항공 홈페이지/앱에서 기프트카드 등록 후 사용. …" },
{ "id": 27, "brand": "AK", "name": "AK모바일교환권 5만원권", "giftKind": "dept",
"goodsGroupId": "GI10002061", "faceValue": 50000, "price": 50000,
"discountRate": 2.5, "validityDays": 1826, "isActive": true,
"imageUrl": "…", "usageNotice": "[발행사] 갤럭시아머니트리. AK PLAZA 전점 …" } ] }
// 목록에도 usageNotice 가 포함된다(상세 전용 아님). price 는 현재 전 상품이 faceValue 와 동일하다.
// id 는 환경별로 다를 수 있으니 (goodsGroupId, faceValue)로 매핑하고 발행 직전 /api/products 로 id 를 resolve.
### GET https://voucher.modooapi.com/api/products/:id [🔒 토큰]
상품 단건 조회 — 목록(/api/products)의 한 건과 동일한 필드를 반환한다(usageNotice 포함 — 목록에도 이미 들어 있으므로 상세를 따로 부를 필요는 없다). 없는 id 는 404.
응답:
{ "success": true, "data": { "id": 6, "brand": "대한항공", "name": "대한항공 기프트카드 5만원권",
"giftKind": "cpn", "goodsGroupId": "1000603641", "faceValue": 50000, "price": 50000,
"discountRate": 2.5, "validityDays": 1826, "isActive": true, "updatedAt": "…",
"imageUrl": "…", "usageNotice": "…" } }
### POST https://voucher.modooapi.com/api/issue [🔒 토큰]
상품권 발행(카탈로그 productId 단일 방식, 1요청=1장) — 카탈로그 productId 로만 발행. 액면은 productId 가 결정(cpn·dept 모두 카탈로그 고정) — faceValue 등 액면 입력 파라미터 없음. 따라서 '없는/비정상 액면' 요청 불가(dept 도 1/5/10/50만 액면별 상품). 수량 파라미터 없음(한 요청=한 장). recipientPhone 으로 알림 발송. 추적: 토큰(인증)·platform(요청 플랫폼/출처)·요청 상품권 정보·hash(유니크 해시코드)를 기록. hash 는 플랫폼 내 유일·불변 코드. 동일 (platform,hash) 의 활성(completed/requested) 발행건이 있으면 재발행하지 않고 duplicate=true 로 반환(이중발행·중복과금 차단). 취소/실패 건은 같은 hash 로 재발행 허용(hash 유지, 새 orderNumber·새 상품권 발행). hash 전송 시 platform 필수. 시각은 KST(+09:00).
요청:
{ "productId": 27, "recipientPhone": "01012345678", "platform": "modoogift", "hash": "a1b2c3...", "ref": "order-123" }
// 액면은 productId 가 결정한다(예: productId 27 = AK 5만원권). faceValue 를 보내도 무시된다. id 는 /api/products 로 조회.
// 선택 파라미터(위 예시에 없지만 실제로 받는다):
// buyerPhone 발송자 번호. 미입력 시 갤럭시아가 recipientPhone 과 동일하게 설정한다.
// msg 안내 메시지에 넣을 사용자 지정 문구(250Byte 제한). dept·cpn 공통.
// msgType "MMS"(기본) | "LMS" | "SMS". **cpn 전용**(dept 는 무시).
// duration 쿠폰 유효기간(일). **cpn 전용**. 미지정·0 이면 최대 유효기간.
// reserveDate 예약발송 일시 yyyyMMddHHmmss. **cpn 전용**. 값이 올바르지 않으면 즉시발송된다.
// ref 호출측 참조 문자열(발행이력에 그대로 보관).
// ★ 주문번호(orderNumber)는 받지 않는다 — 서버가 생성한다(V + KST타임스탬프 + 난수). 호출측 식별은 platform+hash·ref 로 하라.
// 발행 후 조회·취소·재전송은 **응답의 transactionId 단일 수단**으로 처리한다.
응답:
{ "success": true, "data": {
"orderNumber": "V202606160930001234", "transactionId": "...", "issueNumber": "...",
"barcode": "...", "pinNumber": "...", "faceValue": "50000",
"issueDate": "20260616093001", "validTo": "20310615", "msgAdditionalInfo": null } }
// ★★ 타입 주의: 발행 응답의 faceValue 는 갤럭시아 암호화 필드를 복호화한 값이라 **문자열**이다("50000").
// transactionId·issueNumber·barcode 도 같은 이유로 문자열.
// 중복(동일 hash) — 정상 발행과 같은 키 집합을 그대로 돌려준다. barcode·pinNumber 포함이므로 추가 호출이 필요 없다:
// { "success": true, "duplicate": true, "data": {
// "orderNumber": "V...", "status": "completed", "platform": "modoogift", "hash": "a1b2c3...",
// "transactionId": "...", "issueNumber": "...", "barcode": "...", "pinNumber": "...",
// "faceValue": 50000, "issueDate": "20260616093001", "validTo": "20310615" } }
// 단 이 경로의 값은 우리 DB 에서 읽으므로 faceValue 가 **숫자**다(발행 응답은 문자열 — 타입이 다르니 숫자 변환 후 비교하라).
// status 가 "requested"(발행 진행 중 미확정)면 success:false 이고 barcode·pinNumber 는 null 일 수 있다 → /api/info 로 확정하라.
// 실패: { "success": false, "error": "발행 한도 부족", "ordNo": "...", "resCode": "020014" }
### POST https://voucher.modooapi.com/api/cancel [🔒 토큰]
발행 취소(미사용·유효기간 내) — ★ 취소 대상은 **transactionId(갤럭시아 거래번호)로만** 지정한다. 주문번호(orderNumber)로는 취소되지 않는다 — 주문번호는 호출측이 임의로 정하는 값이라 남이 발행한 상품권까지 취소될 수 있어 대상 지정에서 제외했다. 거래번호를 모르면 GET /api/issues/:orderNumber 또는 GET /api/issues/by-hash/:hash 로 조회하라(발행 응답에도 들어 있다). 취소 요청번호는 서버가 새 유일번호로 생성하므로 호출측이 신경 쓸 필요 없다(원 발행번호 재사용 시 갤럭시아 4900 중복). **백화점(dept)은 원 발행 요청 때 보낸 금액과 같은 faceValue 를 함께 보내야 한다**(규격서 발행취소 제한사항). 취소 가부(발행완료·유효기간 이내·미사용)는 갤럭시아가 판단한다.
요청:
{ "giftKind": "cpn", "transactionId": "2063240001" }
// transactionId·giftKind 필수. 누락 시 400.
// 선택 파라미터:
// faceValue 대상 상품권 발행 액면가. **dept 는 원 발행 요청 금액과 동일하게 필수로 넣어라**(cpn 은 불필요).
// issueDay 원 발행일 YYYYMMDD.
// 백화점 예시: { "giftKind": "dept", "transactionId": "TR2608...", "faceValue": 50000, "issueDay": "20260802" }
응답:
{ "success": true, "data": { "transactionId": "...", "cancelDate": "20260616100000" } }
// 실패: { "success": false, "error": "이미 사용된 쿠폰", "resCode": "2206" }
### POST https://voucher.modooapi.com/api/resend [🔒 토큰]
알림 메시지 재전송(MMS/LMS/SMS — 기본 3회 한도) — 발행 시 수신자에게 발송된 안내 메시지를 다시 보낸다. ★ 대상은 **transactionId(갤럭시아 거래번호)로만** 지정한다(주문번호 불가). 재전송 요청번호는 서버가 새로 생성한다(원 발행번호 재사용 시 4900 중복). **벤더 제약**: 기본 3회 한도 초과 5701 · 재전송 원본 없음 5702 · 잔액 0원이면 불가 5703. 응답은 resCode·resMsg 뿐이라 **잔여 재전송 횟수는 갤럭시아가 제공하지 않는다**(규격서 메시지재전송 응답 표).
요청:
{ "giftKind": "cpn", "transactionId": "2063240001" }
응답:
{ "success": true, "data": {} }
// 실패: { "success": false, "error": "메시지 재전송 횟수 초과(기본 3회)", "resCode": "5701" }
### GET https://voucher.modooapi.com/api/info [🔒 토큰]
정보·잔액 조회 — ★ 조회 대상은 **transactionId(갤럭시아 거래번호)로만** 지정한다. 주문번호로는 조회되지 않는다. 거래번호는 발행 응답에 있고, 잊었으면 GET /api/issues/by-hash/:hash 로 회수한다.
요청:
?giftKind=cpn&transactionId=2063240001
응답:
{ "success": true, "data": { "transactionId": null, "couponStatus": "CANCEL",
"issueDate": "20260805051159", "faceValue": "10000", "balance": "10000",
"validFrom": "20260805", "validTo": "20270804", "barcode": "4530000697734295",
"isRevocable": "false", "isUsed": "false", "usedDate": null } }
// ↑ 실제 운영 응답 그대로(가공 없음). ★★ 타입 주의: faceValue·balance·isRevocable·isUsed 는 모두 **문자열**이다.
// 갤럭시아 원본 값을 그대로 전달하므로 숫자·불리언이 아니다. 판정은 String(v) === "true" 로 하라.
// (isRevocable 을 그대로 if 조건에 넣으면 "false" 도 참이 되어 이미 사용된 건이 통과한다.)
// ★ 취소 전 사전 확인용: isRevocable(취소 가능 여부)·isUsed(사용 여부)·usedDate·couponStatus(ACTIVE/CANCEL/INACTIVE)·validTo
### GET https://voucher.modooapi.com/api/limit [🔒 토큰]
한도 조회(dept=상품별 goodsGroupId / cpn=전체)
요청:
?giftKind=dept&goodsGroupId=GI10002061
응답:
{ "success": true, "data": { "limitPrice": 5000000 } }
### GET https://voucher.modooapi.com/api/issues/:orderNumber [🔒 토큰]
발행 상태·결과 조회(우리 DB)
응답:
{ "success": true, "data": { "orderNumber": "V...", "status": "completed",
"barcode": "...", "pinNumber": "...", "balance": 100000 } }
### GET https://voucher.modooapi.com/api/issues/by-hash/:hash [🔒 토큰]
유니크 해시코드로 발행건 추적 조회(우리 DB) — 발행 요청 시 받은 hash 로 어떤 토큰·플랫폼에서 무엇을 발행했는지 추후 대조. hash 는 플랫폼 내 유일이므로 ?platform 권장. barcode·pinNumber 포함(전체 회수 가능). status: completed=발행됨 / failed=실패(미발행) / requested=결과 미확정(타임아웃 등 → /api/info 로 재확인) / 404=요청기록 없음(미발행 확실).
요청:
?platform=modoogift (권장: hash 범위 한정)
응답:
{ "success": true, "data": { "orderNumber": "V...", "status": "completed",
"platform": "modoogift", "hash": "a1b2c3...", "barcode": "...", "pinNumber": "...", "faceValue": 100000 } }
### GET https://voucher.modooapi.com/api/issues/:orderNumber/logs [🔒 토큰]
거래 요청/응답 로그(마스킹)
응답:
{ "success": true, "data": [ { "direction": "request", "api_name": "issueCoupon", "payload": {...} } ] }
### POST https://voucher.modooapi.com/webhook/usage/:secret [🛡 IP제한]
갤럭시아 사용내역 즉시보고(GMT→우리, XML) — 갤럭시아가 쿠폰 사용/사용취소 시 전송. 보안: GMT 고정 IP 화이트리스트 + 비공개 시크릿 경로(:secret 가 GALAXIA_WEBHOOK_SECRET 와 일치). 중앙 토큰 미사용(발신측이 갤럭시아라 토큰 없음).
요청:
Content-Type: application/xml
<Result><transaction><appdiv>10</appdiv><barcode>..</barcode><amount>ENC</amount>
<remainprice>ENC</remainprice><orderID>..</orderID></transaction></Result>
응답:
<Result><resCode>0000</resCode><resMsg>Success</resMsg></Result>
### GET https://voucher.modooapi.com/images/:brand/:file [공개]
상품 썸네일 이미지(R2) — 카탈로그 imageUrl 이 가리키는 공개 이미지.
### GET https://voucher.modooapi.com/console [🔑 관리자]
발행처 관리 콘솔 + 한도 대시보드 + 발행 기록(admin) — 관리자(CONSOLE_SECRET) 로그인. ①그룹별 발행 한도 자동조회 대시보드(dept 그룹별 + cpn 전체) ②발행처(갤럭시아머니트리) 회사정보·연동설정 등록/수정(AES IV/KEY write-only) ③발행/취소 기록(galaxia_issues, 페이징: 시각·주문번호·종류·액면·상태·수신자(마스킹)·거래번호). 발행 연동은 발행처 레코드를 출처로 사용. 한도 JSON: GET /console/limits.
## 규칙
- 금액은 정수(원). 날짜/시각은 명세 포맷을 따른다.
- 토큰이 없거나 무효면 401. 권한/IP 오류는 403. 입력 오류는 400.
- 실패 시 error 메시지와 (있으면) resCode 를 사용자에게 그대로 전달하라.