/ 개발자
가맹점 연동 문서

결제 수납/출금 API, 몇 단계면 연동 완료

통합된 HTTP + JSON 인터페이스, SHA-256 서명 검증, 비동기 알림 및 능동 조회 기능을 제공합니다. 본 문서는 서명 규칙, 전체 API 필드, 요청 예제를 다룹니다.

개요

India Payme는 가맹점에게 두 가지 핵심 거래 기능을 제공합니다: 결제 수납(Payin) - 가맹점을 대신해 최종 사용자로부터 결제를 받습니다; 출금(Payout) - 가맹점을 대신해 지정된 계좌로 송금합니다. 모든 외부 API는 POST + JSON 방식이며, 반드시 API 서명을 포함해야 합니다. 플랫폼은 처리가 완료되면 비동기 알림으로 가맹점에 콜백하며, 능동적인 주문 조회도 지원합니다.

기능메서드경로
결제 수납 요청POST/transaction/payin/v1/apply
결제 수납 조회POST/transaction/payin/v1/query
출금 요청POST/transaction/payout/v1/apply
출금 조회POST/transaction/payout/v1/query
잔액 조회POST/transaction/payout/v1/balance

기본 주소(프로덕션 게이트웨이 도메인은 계정 개설 후 플랫폼에서 제공하는 정보를 따르세요):

https://{게이트웨이 도메인}

연동 준비

가맹점 관리자 페이지 → 애플리케이션 키에 로그인하세요. 각 애플리케이션마다 appKeyappSecret 한 쌍의 자격 증명이 발급됩니다. 이때 appKey가 요청 헤더의 appId가 됩니다. appSecret은 서명 전용 키로, 로컬에서 서명을 계산하는 데만 사용되며 전송에는 포함되지 않습니다.

모든 요청에는 다음 HTTP 헤더가 포함되어야 하며, Content-Typeapplication/json입니다:

Header필수설명
appId가맹점 애플리케이션 ID(appKey와 동일)
timestamp밀리초 단위 Unix 타임스탬프
nonce무작위 문자열, 10자리 이상, 1회용(재사용 불가)
sign아래 규칙에 따라 계산한 서명

서명 규칙

서명 알고리즘은 순수 SHA-256(16진수 소문자)이며, HMAC이 아닙니다. appSecret을 서명 대상 문자열 끝에 직접 이어붙인 뒤 SHA-256을 적용합니다. 절차:

  1. "요청 본문의 최상위 필드(비어 있지 않은 값만)"와 "서명 헤더 appId, timestamp, nonce"를 하나의 Map으로 합칩니다(sign 자체는 포함하지 않음);
  2. 파라미터 이름을 ASCII 오름차순으로 정렬해 key1=value1&key2=value2… 형식으로 연결합니다;
  3. 마지막에 구분자 없이 appSecret을 이어붙인 뒤, 전체 문자열에 SHA-256을 적용해 16진수 소문자로 변환하면 sign이 됩니다.
# 서명 대상 문자열 signString = "key1=value1&key2=value2…&keyN=valueN" + appSecret sign = SHA256_HEX(signString) // 소문자 16진수

유효 기간 및 재전송 방지:

  • 타임스탬프 유효 기간은 30초: |서버 시간 − timestamp| ≤ 30s, 초과 시 timestamp has expired가 반환됩니다. 클라이언트와 게이트웨이의 시간이 동기화(NTP)되어 있는지 확인하세요.
  • nonce 재전송 방지: 동일한 nonce는 한 번만 사용할 수 있으며, 재사용 시 nonce has been used(오류 코드 900)가 발생합니다.
  • 값이 비어 있는 필드는 서명에 포함되지 않습니다 - "서명에 사용되는 필드 집합"은 "실제로 전송되는 JSON 필드"와 완전히 일치해야 합니다.

Node.js 서명 예제:

const crypto = require('crypto'); function buildSign(appId, appSecret, timestamp, nonce, body) { const params = { ...body, appId, timestamp, nonce }; const signString = Object.keys(params) .filter(k => params[k] !== null && params[k] !== undefined) .sort() .map(k => `${k}=${params[k]}`) .join('&') + appSecret; return crypto.createHash('sha256').update(signString, 'utf8').digest('hex'); }

결제 수납 요청 POST

POST /transaction/payin/v1/apply - 결제 주문을 생성하고 결제 페이지 링크인 payUrl을 반환하여 사용자가 결제를 완료하도록 안내합니다.

필드유형필수설명
merchantOrderNostring가맹점 주문번호, 중복 불가
amountnumber주문 금액, 소수점 표기(예: 100.02)
namestring사용자 이름. 영문만 사용, 3자 이상, 공백 포함 가능, 기호나 숫자 불가
phonestring휴대폰 번호
emailstring이메일
currencystring통화: INR / PKR / USDT / BDT
productstring결제 상품 코드, 계약 조건에 따라 플랫폼에서 발급
notifyUrlstring아니오비동기 알림 주소. 전달하지 않으면 가맹점 기본 알림 주소를 사용
returnUrlstring아니오결제 성공 후 이동할 페이지
payTypestring아니오전자지갑 직접 결제 방식: JAZZCASH / EASYPAISA / BKASH / NAGAD
POST /transaction/payin/v1/apply { "merchantOrderNo": "ORD1751731200123", "amount": "600.00", "currency": "INR", "product": "{상품 코드}", "name": "John Doe", "phone": "9910099100", "email": "john@example.com", "notifyUrl": "https://shop.example/pay/notify" }
// 응답 { "code": 0, "msg": "Success", "data": { "orderNo": "P1751731200999", "merchantOrderNo": "ORD1751731200123", "amount": 600, "currency": "INR", "payUrl": "https://{수납 페이지 도메인}/cashier/xxxx", "payStatus": 0 } }

payUrl을 받은 후 사용자를 결제 페이지로 이동시키며, 최종 결과는 비동기 알림을 기준으로 합니다.

출금 요청 POST

POST /transaction/payout/v1/apply - 지정된 계좌로 송금을 요청합니다. 비동기 처리: API는 먼저 계좌 검증과 자금 동결을 수행한 뒤 비동기로 채널을 매칭해 출금을 진행하며, 응답은 일반적으로 payStatus=0(접수 성공)으로 반환되고, 최종 결과는 조회 또는 알림을 기준으로 합니다.

필드유형필수설명
merchantOrderNostring가맹점 주문번호
amountnumber송금 금액, 최소 100.00
accountNamestring수취인 이름. 영문만 사용, 3자 이상, 공백 포함 가능
accountstring수취 계좌번호
phonestring휴대폰 번호
emailstring이메일
currencystring통화: INR / PKR / USDT / BDT
bizCodestring출금 업무 유형: IMPS / UPI / BANK / JAZZCASH / EASYPAISA / BKASH / NAGAD / ECARD
payCodestring결제(출금) 수단: CARD / WALLET / NBK / ITT / VC
branchCodestring청산 코드: 인도는 IFSC, 파키스탄은 bankcode, 방글라데시는 bizCode에 따라 입력
productstring송금 상품 코드, 플랫폼에서 발급
bankNamestring아니오은행명
swiftCodestring아니오SWIFT 코드(국경 간 전신송금 ITT 사용 시)
POST /transaction/payout/v1/apply { "merchantOrderNo": "PO1751731200123", "amount": "1500.00", "currency": "INR", "product": "{상품 코드}", "accountName": "John Doe", "account": "1234567890", "bizCode": "IMPS", "payCode": "NBK", "branchCode": "HDFC0000001", "bankName": "HDFC", "phone": "9910099100", "email": "john@example.com" }

주문 조회 POST

결제 수납은 POST /transaction/payin/v1/query, 출금은 POST /transaction/payout/v1/query를 사용합니다. merchantOrderNo 또는 orderNo를 전달해 주문을 조회합니다. 초당 5회로 제한됩니다.

POST /transaction/payin/v1/query { "merchantOrderNo": "ORD1751731200123" }
// 응답 data { "orderNo": "P1751731200999", "merchantOrderNo": "ORD1751731200123", "amount": 600, "realAmount": 600, "currency": "INR", "payStatus": 1 // 0 처리 중 / 1 수납 완료 / -1 취소됨 }

잔액 조회 POST

POST /transaction/payout/v1/balance - 가맹점 계좌 잔액을 조회합니다. 초당 5회로 제한됩니다.

POST /transaction/payout/v1/balance { "currency": "INR" }
// 응답 data { "accountNo": "ACC0001", "currency": "INR", "balance": 100000.00, // 총 잔액 "balanceActive": 98500.00, // 사용 가능 잔액 "balanceFreeze": 1500.00 // 동결 잔액 }

비동기 알림

주문이 최종 상태에 도달하면 플랫폼은 POST(JSON) 방식으로 가맹점의 notifyUrl(전달하지 않았다면 가맹점 기본 주소)로 통지하며, 알림에도 동일한 서명 헤더(appId/timestamp/nonce/sign)가 함께 전달됩니다. 가맹점은 자신의 appSecret으로 동일한 규칙에 따라 서명을 다시 계산하여 비교함으로써 알림의 진위를 검증해야 합니다.

결제 수납 알림 메시지에는 merchantOrderNo, orderNo, currency, product, amount, realAmount, status(수납 성공 시 "1")가 포함됩니다. 출금 알림에는 추가로 utr(은행 거래번호)와 statusDes(상태 설명)가 포함되며, status"1"성공 / "2"실패입니다.

// 결제 수납 알림 body 예제 { "merchantOrderNo": "ORD1751731200123", "orderNo": "P1751731200999", "currency": "INR", "product": "{상품 코드}", "amount": "600.00", "realAmount": "600.00", "status": "1" }

가맹점은 순수 텍스트로 OK 또는 success(대소문자 구분 없음)를 응답해야 합니다. 그렇지 않으면 플랫폼이 재시도 큐로 전환합니다: 최대 5회, 간격은 분 단위로 점점 늘어납니다(1 / 2 / 5 / 10 / 30 / 60 / 120). 따라서 가맹점 API는 멱등성을 보장해야 합니다(동일한 orderNo에 대해 알림이 여러 번 올 수 있음).

주문 상태

가맹점 연동 시에는 아래의 외부 공개 상태를 기준으로 합니다(내부 상태 머신은 더 세분화되어 있으나 직접 노출되지 않습니다):

시나리오필드
주문 접수 응답payStatus0 생성 / 1 성공 / 2 실패 / 3 취소
결제 수납 조회payStatus0 처리 중 / 1 수납 완료 / -1 취소됨
출금 조회payStatus0 송금 대기 / 1 송금 완료 / -1 취소됨
비동기 알림status결제 수납 성공 "1"; 출금 "1" 성공 / "2" 실패

오류 코드

응답 본문의 code != 0이면 실패이며, msg는 안내 메시지입니다. 자주 발생하는 비즈니스 오류 코드:

code설명
0성공
400파라미터 오류 / 서명 검증 실패(Sign invalid)
900중복 요청 / nonce 재사용
10000 / 10001애플리케이션 없음 / 애플리케이션 사용 불가
10002지원하지 않는 통화
10003 / 10007가맹점 사용 불가 / 가맹점 계좌 사용 불가
10004상품 미개통 또는 계약 만료
10005 / 10009사용 가능한 채널 없음 / 채널 일시 사용 불가
10008계좌 잔액 부족
10010IP가 화이트리스트에 없음
10011 / 10012유효하지 않은 상품 / 유효하지 않은 결제 방식
10020가맹점 주문번호 중복
30001 / 30002주문번호 오류 / 통화 오류 또는 계좌 미활성화

전체 연동 흐름

결제 수납을 예로 들면:

  1. 비즈니스 요청 본문을 구성합니다("결제 수납 요청" 필드 참고).
  2. timestamp(밀리초)와 nonce(10자리 이상 무작위 문자열)를 생성합니다.
  3. "서명 규칙"에 따라 sign을 계산합니다.
  4. 4개의 서명 헤더 + JSON Body를 포함해 POST /transaction/payin/v1/apply를 요청합니다.
  5. code==0이면 data.payUrl을 가져와 사용자를 결제로 안내합니다.
  6. 비동기 알림을 수신 대기하며, 동시에 능동적으로 조회하여 최종 상태를 확인할 수 있습니다.
  7. 알림을 수신하면 서명을 검증하고 비즈니스를 처리한 뒤 순수 텍스트로 OK를 반환합니다.

참고: 본 문서의 API 경로, 필드, 상태 코드는 실제 프로덕션 구현을 기준으로 합니다. 프로덕션 게이트웨이 도메인, 사용 가능한 product 상품 코드, IP 화이트리스트 정책 등은 계정 개설 후 플랫폼에서 제공하는 정보를 따르세요.

키를 발급받고 바로 시작하세요

온라인으로 계정을 개설하면 테스트 자격 증명과 상품 코드를 받을 수 있으며, 샌드박스 테스트를 마치면 한 번의 클릭으로 프로덕션 전환이 가능합니다.