Next.js 스타터 템플릿
인증, 요금제 페이지, ChainPay 결제가 사전 연동된 완전한 Next.js 앱입니다. 클론하고 5분 만에 실행하세요.
빠른 시작
세 단계로 암호화폐 결제를 받으세요 — 블록체인 지식이 필요 없습니다.
결제 주문을 생성합니다
const response = await fetch("https://chainpay.pro/api/v1/orders", {
method: "POST",
headers: {
"Authorization": "Bearer sk_live_YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
amount: 29.99,
currency: "USDT",
chain: "trc20",
externalId: "order_abc123", // your internal order ID
metadata: { userId: "user_456" } // arbitrary JSON, returned in webhook
})
});
const order = await response.json();
// order.checkoutUrl → redirect user here
// order.payAddress → or show the address directly사용자를 결제 페이지로 리디렉션합니다
// Server-side redirect
res.redirect(order.checkoutUrl);
// Or client-side
window.location.href = order.checkoutUrl;결제가 확인되면 Webhook을 수신합니다
{
"event": "payment.completed",
"orderId": "ord_a1b2c3d4e5f6",
"externalId": "order_abc123",
"amount": "29.99",
"netAmount": "29.75", // after 0.8% fee
"currency": "USDT",
"chain": "trc20",
"txHash": "e3b0c44298fc1c149afb...",
"completedAt": "2026-03-22T08:15:00Z",
"metadata": { "userId": "user_456" }
}처리하기 전에 X-ChainPay-Signature 헤더를 검증하세요. 검증 코드는 섹션을 참고하세요.
인증
모든 API 요청에는 API 키를 Bearer 토큰으로 포함해야 합니다.
Authorization: Bearer sk_live_YOUR_API_KEYcurl -X POST https://chainpay.pro/api/v1/orders \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 10, "currency": "USDT", "chain": "trc20"}'| 키 접두사 | 환경 | 블록체인 |
|---|---|---|
| sk_live_… | 프로덕션 | 실제 거래 |
| sk_test_… | 샌드박스 | 실제 자금 이동 없음 |
주문 생성
https://chainpay.pro/api/v1/orders요청 매개변수
| 매개변수 | 타입 | 필수 여부 | 설명 |
|---|---|---|---|
| amount | number | 필수 | USD 기준 결제 금액(예: 29.99). 이에 상응하는 암호화폐 금액은 현재 시세로 계산됩니다. |
| currency | string | 필수 | 받을 암호화폐. 다음 중 하나: "USDT", "ETH", "BTC", "SOL". |
| chain | string | 필수 | 블록체인 네트워크. 다음 중 하나: "trc20", "erc20", "btc", "sol". 선택한 통화와 호환되어야 합니다. |
| externalId | string | 선택 | 내부 주문 식별자. Webhook에서 변경 없이 반환됩니다. 가맹점별로 고유해야 합니다. |
| metadata | object | 선택 | 임의의 JSON 객체(최대 2 KB). 이 주문의 모든 Webhook에서 반환됩니다 — 사용자 ID, 장바구니 참조 등을 전달하는 데 유용합니다. |
지원되는 통화 + 체인 조합
| currency | chain | 네트워크 | 평균 확인 시간 |
|---|---|---|---|
| USDT | trc20 | TRON (TRC-20) | 약 1분 |
| USDT | erc20 | Ethereum (ERC-20) | 약 2분 |
| ETH | erc20 | Ethereum | 약 2분 |
| BTC | btc | Bitcoin | 약 30분 |
| SOL | sol | Solana | 5초 미만 |
응답
{
"orderId": "ord_a1b2c3d4e5f6g7h8",
"payAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"cryptoAmount": "29.99",
"currency": "USDT",
"chain": "trc20",
"checkoutUrl": "https://chainpay.pro/pay/ord_a1b2c3d4",
"expiresAt": "2026-03-22T09:00:00Z",
"createdAt": "2026-03-22T08:30:00Z"
}결제 흐름
호스팅형 결제 페이지의 작동 방식입니다.
- 1리디렉션 — 서버는 주문 생성 시 반환된 checkoutUrl로 사용자를 리디렉션합니다.
- 2결제 페이지 — 호스팅형 페이지에는 정확한 암호화폐 금액이 미리 입력된 QR 코드와 지갑 주소가 표시됩니다. 사용자는 QR을 스캔하거나 주소를 지갑 앱에 복사합니다.
- 3확인 — ChainPay가 블록체인을 모니터링합니다. 필요한 확인 횟수에 도달하면 주문 상태가 confirming으로, 이어서 completed로 변경됩니다.
- 4돌아가기 리디렉션 — 결제 페이지에 성공 화면이 표시됩니다. 대시보드에 successUrl을 설정해 두었다면 사용자가 자동으로 그곳으로 리디렉션됩니다.
- 5Webhook — 서버가 payment.completed Webhook을 수신합니다. Webhook 서명을 검증한 후에만 주문을 이행하세요.
expired로 변경되고 payment.expired Webhook이 전송됩니다. 다시 시도하려면 새 주문을 생성하세요.Webhook
ChainPay는 주문 상태가 변경되면 서명된 HTTP POST 요청을 Webhook URL로 전송합니다.
설정
대시보드 → 앱 → 새 앱으로 이동합니다
Webhook URL을 입력합니다(예: https://yourapp.com/api/webhooks/chainpay)
생성된 Webhook Secret을 복사합니다 — 서명 검증에 사용합니다
Webhook 이벤트
현재 지원되는 이벤트:
| 이벤트 | 발생 시점 |
|---|---|
| payment.completed | 주문이 결제되고 온체인에서 확인됨 |
| payment.expired | 주문이 결제 없이 만료됨 |
페이로드 형식
{
"event": "payment.completed",
"orderId": "ord_xxxxxxxx",
"amount": "9.99",
"currency": "USDT",
"chain": "trc20",
"txHash": "abc123...",
"netAmount": "9.92",
"completedAt": "2026-01-01T00:00:00.000Z",
"metadata": {
"userId": "your-user-id",
"plan": "monthly"
}
}서명 검증
모든 Webhook 요청에는 X-ChainPay-Signature 헤더가 포함됩니다. 앱의 Webhook Secret으로 HMAC-SHA256을 사용해 검증하세요:
import crypto from 'crypto'
function verifyWebhook(rawBody: string, signature: string, secret: string): boolean {
const computed = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(computed),
Buffer.from(signature)
)
}
// In your webhook handler:
export async function POST(req: Request) {
const rawBody = await req.text()
const signature = req.headers.get('x-chainpay-signature') ?? ''
if (!verifyWebhook(rawBody, signature, process.env.CHAINPAY_WEBHOOK_SECRET!)) {
return new Response('Unauthorized', { status: 401 })
}
const event = JSON.parse(rawBody)
if (event.event === 'payment.completed') {
const { userId, plan } = event.metadata
// activate user's plan
}
return new Response('OK')
}import hmac, hashlib
def verify_webhook(raw_body: bytes, signature: str, secret: str) -> bool:
computed = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(computed, signature)Webhook 테스트
대시보드 → 앱으로 이동하여 앱을 선택합니다
"테스트 이벤트 전송"을 클릭합니다
서버가 요청을 수신했는지 확인합니다(HTTP 200 예상)
재시도 정책
전송에 실패하면 지수 백오프로 재시도합니다: 1분 → 5분 → 30분 → 2시간 → 24시간(총 5회 시도).
5회 시도가 모두 실패하면 Webhook은 실패로 표시됩니다. 전송 실패 내역은 대시보드 → 알림에서 확인하세요.
모범 사례
- 처리하기 전에 항상 서명을 검증하세요
- HTTP 200을 빠르게 반환하세요 — 무거운 처리는 비동기로 수행하세요
- 이벤트 중복 제거에
orderId를 사용하세요(Webhook은 두 번 이상 전송될 수 있습니다) - 정확한 서명 검증을 위해 파싱하기 전에 원본 본문을 저장하세요
주문 조회
https://chainpay.pro/api/v1/orders/:id{
"id": "ord_a1b2c3d4e5f6g7h8",
"amount": "29.99",
"cryptoAmount":"29.99",
"netAmount": "29.75",
"currency": "USDT",
"chain": "trc20",
"status": "completed",
"payAddress": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"txHash": "e3b0c44298fc1c149afbf4c8996fb92...",
"externalId": "order_abc123",
"metadata": { "userId": "user_456" },
"createdAt": "2026-03-22T08:30:00Z",
"expiresAt": "2026-03-22T09:00:00Z",
"completedAt": "2026-03-22T08:45:00Z"
}주문 상태 값
| 상태 | 설명 |
|---|---|
| pending | 주문이 생성되었으며, 사용자가 결제를 보내기를 기다리는 중입니다. |
| confirming | 온체인에서 거래가 감지되었으며, 충분한 블록 확인을 기다리는 중입니다. |
| completed | 결제가 완전히 확인되었습니다. 주문을 이행해도 안전합니다. |
| expired | 결제가 감지되지 않은 채 30분이 경과했습니다. |
| failed | 예상치 못한 오류가 발생했습니다. orderId와 함께 지원팀에 문의하세요. |
환율 API
현재 USD 환율을 가져옵니다. API 키가 필요 없습니다.
https://chainpay.pro/api/v1/rates{
"USDT": 1.0,
"ETH": 3450.00,
"BTC": 87500.00,
"SOL": 145.00,
"updatedAt": "2026-03-22T08:00:00Z"
}환율은 집계된 시장 데이터로부터 60초마다 업데이트됩니다. 주문 생성 엔드포인트에 전달하는 amount는 항상 USD로 해석되며, API는 주문 생성 시점의 환율로 암호화폐로 변환합니다.
샌드박스 모드
실제 자금을 이동하지 않고 전체 연동을 테스트하세요.
?mode=test로 가입하면 sk_test_ API 키를 받게 됩니다. 모든 표준 엔드포인트는 샌드박스 모드에서 동일하게 작동합니다 — 블록체인 폴링은 생략되며 확인은 수동으로 트리거합니다.
1단계 — 샌드박스 API 키 받기
curl -X POST "https://chainpay.pro/api/auth/register?mode=test" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "yourpassword"}'
# Response includes:
# { "merchant": { "apiKey": "sk_test_xxxxxxxxxxxxxxxx" } }2단계 — 테스트 키로 주문 생성하기
curl -X POST https://chainpay.pro/api/v1/orders \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 10, "currency": "USDT", "chain": "trc20", "externalId": "test_order_1"}'3단계 — 결제 시뮬레이션하기
curl -X POST https://chainpay.pro/api/v1/sandbox/simulate-payment \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"orderId": "ord_abc123"}'
# Response:
{
"success": true,
"orderId": "ord_abc123",
"txHash": "sandbox_ord_abc123_1711000000000",
"netAmount": "9.92000000"
}
# The order status is now "completed" and your webhook is fired.sandbox_ 접두사가 붙습니다. simulate-payment 엔드포인트는 sk_test_ 키에서만 사용할 수 있습니다. 라이브 키로 시도하면 403을 반환합니다.오류 코드
모든 오류 응답은 일관된 JSON 형식을 사용합니다:
{
"error": "INVALID_CHAIN",
"message": "chain 'foo' is not supported for currency USDT",
"status": 400
}| HTTP 상태 | 일반적인 오류 코드 | 원인 및 해결 |
|---|---|---|
| 400 | INVALID_AMOUNT, INVALID_CURRENCY, INVALID_CHAIN, MISSING_FIELD | 요청 본문이 잘못되었거나 필수 필드가 누락되었습니다. 위의 요청 매개변수 표를 확인하세요. |
| 401 | UNAUTHORIZED, INVALID_API_KEY | Authorization 헤더가 누락되었거나 유효하지 않습니다. Bearer <key>를 전송하고 있는지 확인하세요. |
| 403 | FORBIDDEN, SANDBOX_ONLY | API 키에 이 작업에 대한 권한이 없습니다(예: 라이브 키로 simulate-payment 사용). |
| 404 | ORDER_NOT_FOUND | 주어진 ID의 주문이 존재하지 않거나, 귀하의 가맹점 계정에 속하지 않습니다. |
| 429 | RATE_LIMITED | 요청이 너무 많습니다. 기본 한도는 API 키당 60 req/min입니다. 속도를 줄이고 Retry-After 헤더 값만큼 기다린 후 재시도하세요. |
| 500 | INTERNAL_ERROR | 예상치 못한 서버 측 오류입니다. 지수 백오프로 재시도하세요. 지속되면 요청 ID와 함께 지원팀에 문의하세요. |
정산
ChainPay가 수집한 자금을 지갑으로 보내는 방식과 시점입니다.
정산은 완료되었으나 아직 정산되지 않은 모든 금액을 대시보드의 설정 → 정산 지갑에서 설정한 지갑 주소로 보냅니다. 통화별로 별도의 지급 주소를 설정할 수 있습니다.
netAmount는 이미 수수료가 차감된 금액을 반영합니다. 정산은 해당일의 모든 netAmount 값의 합계를 전송합니다.연동할 준비가 되셨나요?
계정을 만들고 몇 초 만에 API 키를 받으세요.