API 샌드박스
Unifi Pay API를 탐색하고 테스트해보세요. 요청 파라미터를 수정한 뒤 요청 전송 버튼을 클릭하면 Mock 응답을 확인할 수 있습니다.
HMAC 인증
API 요청(Server-to-Server) 시 필수 적용
1 필수 요청 헤더
X-API-Key
Unifi Pay에서 발급받은 apiKey
X-Timestamp
현재 Unix 타임스탬프 (밀리초 단위). 서버 시각과 ±5분 이상 차이 시 요청 거부
X-Authorization-Hmac
Base64로 인코딩된 HMAC-SHA256 서명값
2 서명 생성 공식
// HMAC 생성
BASE64(
HMACSHA256(
appSecret,
{HTTP_METHOD}
+ {URI}
+ {X-API-Key}
+ {X-Timestamp}
+ {REQUEST_BODY}
)
)
참고: 모든 필드를 구분자 없이 순서대로 연결합니다. GET 요청의 REQUEST_BODY는 빈 문자열("")을 사용합니다.
3 서명 생성기
생성된 서명
/api/seller/v1/payment/link
고정된 금액과 상품의 결제 링크를 발급합니다
요청 파라미터
JSON Body
필수 헤더
| 파라미터 | 타입 | 필수 |
|---|---|---|
| requestId | string | Y |
| storeId | string | Y |
| serviceName | string | Y |
| itemName | string | Y |
| itemPrice | number | Y |
| orderCurrencyCode | string | Y |
| expiresAt | string | Y |
| returnUrl | string | N |
| callbackUrl | string | N |
💡 requestId는 중복 요청을 막는 값입니다. 같은 값으로 다시 요청하면 거부됩니다. expiresAt은 미래 시점이어야 합니다.
요청 샘플
curl --request POST \
--url 'https://unifi.me/pay/api/seller/v1/payment/link' \
--header 'X-Authorization-Hmac: {YOUR_HMAC_SIGNATURE}' \
--header 'X-API-Key: {YOUR_API_KEY}' \
--header 'X-Timestamp: 1773017787000' \
--header 'Content-Type: application/json' \
--data '{
"requestId": "REQ-20260304-0001",
"storeId": "123",
"serviceName": "MyShop",
"itemName": "ITEM-GAME-001",
"itemPrice": 12.34,
"orderCurrencyCode": "USD",
"returnUrl": "https://myshop.com/payment/returnUrl",
"callbackUrl": "https://myshop.com/payment/callbackUrl",
"expiresAt": "2026-08-31T00:00:00Z"
}'
const timestamp = Date.now().toString();
const appId = 'YOUR_APP_ID';
const body = JSON.stringify({
requestId: 'REQ-20260304-0001',
storeId: '123',
serviceName: 'MyShop',
itemName: 'ITEM-GAME-001',
itemPrice: 12.34,
orderCurrencyCode: 'USD',
returnUrl: 'https://myshop.com/payment/returnUrl',
callbackUrl: 'https://myshop.com/payment/callbackUrl',
expiresAt: '2026-08-31T00:00:00Z'
});
const hmac = generateHmac(
'YOUR_APP_SECRET',
'POST' + '/api/seller/v1/payment/link' + appId + timestamp + body
);
const res = await fetch(
'https://unifi.me/pay/api/seller/v1/payment/link',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
'X-Timestamp': timestamp,
'X-Authorization-Hmac': hmac
},
body
}
);
const data = await res.json();
import requests, hmac, hashlib, base64, time, json
app_id = "YOUR_APP_ID"
app_secret = "YOUR_APP_SECRET"
timestamp = str(int(time.time() * 1000))
uri = "/api/seller/v1/payment/link"
body = json.dumps({
"requestId": "REQ-20260304-0001",
"storeId": "123",
"serviceName": "MyShop",
"itemName": "ITEM-GAME-001",
"itemPrice": 12.34,
"orderCurrencyCode": "USD",
"returnUrl": "https://myshop.com/payment/returnUrl",
"callbackUrl": "https://myshop.com/payment/callbackUrl",
"expiresAt": "2026-08-31T00:00:00Z"
}, separators=(',', ':'))
msg = "POST" + uri + app_id + timestamp + body
sig = base64.b64encode(
hmac.new(
app_secret.encode(),
msg.encode(),
hashlib.sha256
).digest()
).decode()
res = requests.post(
"https://unifi.me/pay" + uri,
headers={
"Content-Type": "application/json",
"X-API-Key": api_key,
"X-Timestamp": timestamp,
"X-Authorization-Hmac": sig
},
data=body
)
print(res.json())
응답
"linkUrl": "https://unifipay.example.com/pay/links/3f1d0a549c1e4f6a8a5b2f",
"linkId": "3f1d0a549c1e4f6a8a5b2f"
💡 linkUrl을 고객에게 공유하면 결제가 진행됩니다.
"code": "BAD_REQUEST",
"message": "requestId is required"
"code": "UNAUTHORIZED",
"message": "Invalid HMAC signature"
/api/seller/v1/payment/{transactionId}
생성된 결제 건의 현재 상태를 조회합니다
요청 파라미터
Path Parameter
필수 헤더
| 파라미터 | 위치 | 필수 |
|---|---|---|
| transactionId | path | Y |
응답 필드
요청 샘플
curl --request GET \
--url 'https://unifi.me/pay/api/seller/v1/payment/txn_abc123' \
--header 'X-API-Key: {YOUR_API_KEY}' \
--header 'X-Timestamp: 1773017787000' \
--header 'X-Authorization-Hmac: {HMAC}'
const txnId = 'txn_abc123';
const timestamp = Date.now().toString();
const appId = 'YOUR_APP_ID';
const uri = `/api/seller/v1/payment/${txnId}`;
// GET 요청은 body 없음
const hmac = generateHmac(
'YOUR_APP_SECRET',
'GET' + uri + appId + timestamp
);
const res = await fetch(
`https://unifi.me/pay${uri}`,
{
headers: {
'X-API-Key': apiKey,
'X-Timestamp': timestamp,
'X-Authorization-Hmac': hmac
}
}
);
const data = await res.json();
import requests, hmac, hashlib, base64, time
txn_id = "txn_abc123"
app_id = "YOUR_APP_ID"
app_secret = "YOUR_APP_SECRET"
timestamp = str(int(time.time() * 1000))
uri = f"/api/seller/v1/payment/{txn_id}"
# GET 요청은 REQUEST_BODY가 빈 문자열
msg = "GET" + uri + app_id + timestamp + ""
sig = base64.b64encode(
hmac.new(
app_secret.encode(),
msg.encode(),
hashlib.sha256
).digest()
).decode()
res = requests.get(
f"https://unifi.me/pay{uri}",
headers={
"X-API-Key": api_key,
"X-Timestamp": timestamp,
"X-Authorization-Hmac": sig
}
)
print(res.json())
응답
"transactionId": "txn_abc123",
"orderId": "order_12345",
"storeId": "store_001",
"serviceName": "naver",
"items": [
{ "name": "ITEM-GAME-001", "price": 50000 }
],
"status": "CONFIRMED",
"failType": null,
"countryCode": "KOR",
"payAmount": 49000,
"payCurrencyCode": "USDT",
"orderAmount": 50000,
"orderCurrencyCode": "USD",
"blockchainTxId": "0x4fce0d72ff7f4b9f4ba0cf58d30119924bea3811d85a830c762e1d87b3e1ee17",
"blockchainNetworkFee": 0.00253778
"code": "PAYMENT_TRANSACTION_NOT_FOUND",
"message": "Transaction not found"
/api/seller/v1/payment/settlement/transaction
결제, 환불 내역을 확인합니다
요청 파라미터
Query Parameter
필수 헤더
| 파라미터 | 위치 | 필수 |
|---|---|---|
| from | query | 조건부 |
| to | query | 조건부 |
| paymentType | query | N |
| transactionId | query | N |
| settlementId | query | N |
| page | query | N |
| size | query | N |
응답 필드
요청 샘플
curl --request GET \
--url 'https://unifi.me/pay/api/seller/v1/payment/settlement/transaction?from=2026-03-01T00:00:00Z&to=2026-03-31T23:59:59Z&page=0&size=20' \
--header 'X-API-Key: {YOUR_API_KEY}' \
--header 'X-Timestamp: 1773017787000' \
--header 'X-Authorization-Hmac: {HMAC}'
const from = '2026-03-01T00:00:00Z';
const to = '2026-03-31T23:59:59Z';
const timestamp = Date.now().toString();
const appId = 'YOUR_APP_ID';
const uri = `/api/seller/v1/payment/settlement/transaction?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}&page=0&size=20`;
const hmac = generateHmac(
'YOUR_APP_SECRET',
'GET' + uri + appId + timestamp
);
const res = await fetch(
`https://unifi.me/pay${uri}`,
{
headers: {
'X-API-Key': apiKey,
'X-Timestamp': timestamp,
'X-Authorization-Hmac': hmac
}
}
);
const data = await res.json();
import requests, hmac, hashlib, base64, time
from urllib.parse import urlencode
from_date = "2026-03-01T00:00:00Z"
to_date = "2026-03-31T23:59:59Z"
app_id = "YOUR_APP_ID"
app_secret = "YOUR_APP_SECRET"
timestamp = str(int(time.time() * 1000))
params = urlencode({"from": from_date, "to": to_date, "page": 0, "size": 20})
uri = f"/api/seller/v1/payment/settlement/transaction?{params}"
msg = "GET" + uri + app_id + timestamp
sig = base64.b64encode(
hmac.new(
app_secret.encode(),
msg.encode(),
hashlib.sha256
).digest()
).decode()
res = requests.get(
f"https://unifi.me/pay{uri}",
headers={
"X-API-Key": api_key,
"X-Timestamp": timestamp,
"X-Authorization-Hmac": sig
}
)
print(res.json())
응답
"content": [
{
"partnerCorpName": "ABC Corporation",
"storeId": "store_001",
"orderId": "ORD-20260301-0001",
"itemName": "Premium Subscription",
"serviceName": "ABC Service",
"orderCountryCode": "KR",
"orderCurrencyCode": "USD",
"orderAmount": 50000,
"createdAt": "2026-03-01T10:00:00Z",
"transactionId": "txn_abc123def456",
"blockchainTxId": "0xabcdef1234567890abcdef1234567890abcdef12",
"paymentType": "PURCHASE",
"originTransactionId": null,
"status": "CONFIRMED",
"finalizedAt": "2026-03-01T10:01:30Z",
"capturedAt": "2026-03-01T10:01:25Z",
"failType": null,
"payCurrencyCode": "USDT",
"payAmount": 10.87,
"paymentExchangeRate": 1.0,
"buyerWalletAddress": "0x1234567890abcdef1234567890abcdef12345678",
"variableFeeRate": 1.5,
"settlementId": "stl_xyz789",
"settlementCurrencyCode": "USDT",
"settlementExchangeRate": 1.0
}
],
"totalElements": 1,
"totalPages": 1,
"pageNumber": 0,
"first": true,
"last": true
"code": "PAYMENT_INVALID_REQUEST",
"message": "Date range must not exceed 90 days"
Webhook 안내
결제의 최종 처리 결과를 비동기로 수신합니다
동작 방식
결제의 최종 처리 결과는 결제 링크 발급 시 지정한 callbackUrl로 비동기 Webhook이 전송됩니다.
서버는 Webhook 수신 후 반드시 HTTP 200을 반환해야 합니다. 응답하지 않을 경우 재전송될 수 있습니다.
Webhook은 최소한의 정보(transactionId, status, type)만 전송합니다. 상세 정보는 결제 상태 확인 API를 호출하여 조회하세요.
결제 Webhook 상태값
| status | type | 설명 |
|---|---|---|
| CONFIRMED | PURCHASE | 결제 성공 및 확정 |
| FAILED | PURCHASE | 결제 실패 |
| CANCELED | PURCHASE | 결제 취소 (이탈 또는 30분 경과) |