- 홈
- 개발자
몇 분 만에 암호화폐 결제 연동하기
REST API는 NOWPayments v1과 호환되며 엔드포인트, 필드, 상태 값, 통화 코드가 동일합니다. x-api-key로 인증하고, 서명된 IPN으로 상태 업데이트를 받습니다.
https://omniwallet.ewin888.com/api/v1/https://omniwallet-dev.ewin888.com/api/v1/빠른 시작
- 01
API 키 발급
가입 후 콘솔에 로그인해 ‘개발자 → API 키’에서 키를 생성하고, ‘IPN’ 메뉴에서 IPN 시크릿을 설정합니다. 먼저 테스트 환경에서 연동을 완료하는 것을 권장합니다.
- 02
결제 생성
POST /v1/payment엔드포인트를 호출하면서 금액, 가격 기준 통화, 결제받을 코인을 전달합니다. 응답의pay_address에는 해당 결제의 전용 주소가,pay_amount에는 결제해야 할 금액이 담겨 있습니다.POST /v1/paymentcurlcurl -X POST "https://omniwallet.ewin888.com/api/v1/payment" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-A-1024" \ -d '{ "price_amount": 120, "price_currency": "usd", "pay_currency": "usdttrc20", "order_id": "A-1024", "order_description": "Pro plan, 12 months", "ipn_callback_url": "https://example.com/ipn" }'응답JSON{ "payment_id": 355446374401130, "payment_status": "waiting", "pay_address": "TJ8nq3M5yVfQ9C2dWkPz7aH4sLrE6uXbGt", "price_amount": 120, "price_currency": "usd", "pay_amount": 120.02, "pay_currency": "usdttrc20", "order_id": "A-1024", "network": "TRC20", "expiration_estimate_date": "2026-10-02T09:15:00.000Z", "omni_payment_url": "https://omniwallet.ewin888.com/payment/4fKq9ZtR2mXcL8vBnW1pYs" } - 03
고객 결제 안내
주소와 금액(또는 QR 코드)을 고객에게 보여 주세요.
POST /v1/invoice로 호스팅 인보이스 페이지를 만든 뒤, 응답의invoice_url로 고객을 안내할 수도 있습니다.POST /v1/invoicecurlcurl -X POST "https://omniwallet.ewin888.com/api/v1/invoice" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "price_amount": 49.9, "price_currency": "usd", "order_id": "A-1025", "order_description": "Starter plan", "ipn_callback_url": "https://example.com/ipn", "success_url": "https://example.com/thanks", "cancel_url": "https://example.com/cart" }'응답JSON{ "id": "355447603200201", "order_id": "A-1025", "price_amount": "49.9", "price_currency": "usd", "pay_currency": null, "invoice_url": "https://omniwallet.ewin888.com/invoice/8hTz2QpLm4XwN7cVb1RkYe", "success_url": "https://example.com/thanks", "cancel_url": "https://example.com/cart", "created_at": "2026-10-01T09:20:00.000Z" } - 04
IPN 수신
결제 상태가 바뀔 때마다
IPN 검증ipn_callback_url로 POST 요청을 보내 드립니다. 먼저 서명을 검증한 뒤payment_status에 따라 주문 상태를 업데이트하세요. - 05
운영 환경으로 전환
사업자 인증이 완료되면 운영 환경에서 새 API 키와 IPN 시크릿을 생성하고, 기본 URL을 운영 환경 주소로 바꿉니다.
IPN 서명 검증
IPN 시크릿으로 ‘키를 알파벳순으로 정렬한 JSON’에 대해 HMAC-SHA512를 계산하고, 그 결과를 16진수 문자열로 x-omni-sig 헤더에 담아 보냅니다. 전송되는 요청 본문이 바로 이 정렬된 JSON이므로, 수신한 원본 본문을 그대로 검증하면 됩니다.
- 서명은 상수 시간 비교로 검증하세요
- IPN은 중복되거나 순서가 뒤바뀌어 도착할 수 있습니다.
payment_id와 상태를 기준으로 중복을 제거하고, 필요하면GET /v1/payment/{id}를 호출해 최신 상태를 확인하세요 - 2xx 응답은 수신 완료로 간주되며, 그 외의 응답이나 타임아웃은 자동으로 재시도됩니다
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const IPN_SECRET = process.env.OMNI_IPN_SECRET;
// Keep the raw body: the signature covers the exact bytes we send.
app.post('/ipn', express.raw({ type: 'application/json' }), (req, res) => {
const received = String(req.get('x-omni-sig') || '');
const expected = crypto
.createHmac('sha512', IPN_SECRET)
.update(req.body)
.digest('hex');
const valid = received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return res.status(401).send('invalid signature');
const payment = JSON.parse(req.body.toString('utf8'));
// Idempotent update: the same status can be delivered more than once.
// await orders.applyStatus(payment.order_id, payment.payment_status);
res.sendStatus(200);
});
app.listen(3000);<?php
$secret = getenv('OMNI_IPN_SECRET');
$raw = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_OMNI_SIG'] ?? '';
// The signature covers the exact bytes we send.
$expected = hash_hmac('sha512', $raw, $secret);
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit('invalid signature');
}
$payment = json_decode($raw, true);
// Idempotent update: the same status can be delivered more than once.
// apply_status($payment['order_id'], $payment['payment_status']);
http_response_code(200);import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
app = Flask(__name__)
IPN_SECRET = os.environ["OMNI_IPN_SECRET"].encode()
@app.post("/ipn")
def ipn():
raw = request.get_data() # the exact bytes we signed
expected = hmac.new(IPN_SECRET, raw, hashlib.sha512).hexdigest()
received = request.headers.get("x-omni-sig", "")
if not hmac.compare_digest(expected, received):
abort(401)
payment = json.loads(raw)
# Idempotent update: the same status can be delivered more than once.
# apply_status(payment["order_id"], payment["payment_status"])
return "", 200{
"actually_paid": 120.02,
"actually_paid_at_fiat": 120,
"invoice_id": null,
"order_description": "Pro plan, 12 months",
"order_id": "A-1024",
"outcome_amount": 119.42,
"outcome_currency": "usdttrc20",
"parent_payment_id": null,
"pay_address": "TJ8nq3M5yVfQ9C2dWkPz7aH4sLrE6uXbGt",
"pay_amount": 120.02,
"pay_currency": "usdttrc20",
"payment_id": 355446374401130,
"payment_status": "finished",
"price_amount": 120,
"price_currency": "usd",
"purchase_id": null,
"updated_at": "2026-10-01T09:17:42.000Z"
}결제 상태
상태 값은 NOWPayments와 동일합니다.
| 상태 | 설명 |
|---|---|
waiting결제 대기 | 고객의 결제 대기 중 |
confirming확인 중 | 트랜잭션 감지됨, 블록 컨펌 대기 중 |
confirmed확인 완료 | 입금 확정됨 |
sending정산 중 | 지갑으로 정산 진행 중 |
partially_paid부분 결제 | 결제 금액 부족(고객이 만료 전까지 부족분 추가 결제 가능) |
finished완료 | 결제 완료 |
failed실패 | 결제 실패(예: 제재 목록 해당) |
refunded환불 완료 | 결제자에게 환불 완료 |
expired만료 | 기한 내 미결제 |
NOWPayments에서 마이그레이션
API가 호환되므로 대부분의 코드는 수정할 필요가 없습니다.
- 기본 URL 변경
https://api.nowpayments.io/v1/주소를 아래의 OmniWallet URL로 바꿉니다. - OmniWallet API 키로 교체헤더 이름은 그대로
x-api-key입니다. - IPN 검증 업데이트OmniWallet의 IPN 시크릿과
x-omni-sig헤더를 사용하도록 변경합니다. 기존 코드를 전혀 수정하지 않으려면 콘솔에서x-nowpayments-sig호환 헤더를 활성화할 수 있습니다. - 지원 코인 확인통화 코드는 동일하지만(예:
usdttrc20,usdcsol), 당사가 지원하는 코인만 사용할 수 있습니다.GET /v1/merchant/coins또는 지원 코인 페이지에서 확인하세요. - 출금 설정 확인출금은 기존과 마찬가지로
POST /v1/auth로 5분간 유효한 JWT를 발급받아 진행합니다. 또한 출금 권한이 있는 API 키에는 IP 화이트리스트를 설정해야 하며, 출금 대상 주소도 먼저 화이트리스트에 등록해야 합니다.
- https://api.nowpayments.io/v1/
+ https://omniwallet.ewin888.com/api/v1/그대로 유지되는 항목
- 엔드포인트 경로와 HTTP 메서드
- 요청 및 응답 필드 이름
- 결제 및 출금 상태 값
- 통화 코드
- IPN 서명 알고리즘
주의해야 할 차이점
- IPN 서명 헤더는
x-omni-sig입니다(x-nowpayments-sig도 함께 보내도록 선택 가능) - OmniWallet 확장 필드는
omni_로 시작합니다 - 법정화폐 출금은 지원하지 않습니다(HTTP 501 응답)
- 정산은 항상 콘솔에 등록된 지갑으로 지급됩니다
- 테스트 환경은 실제 블록체인 테스트넷을 사용합니다
테스트 환경(샌드박스)
테스트 환경은 별도로 배포된 독립 환경으로, 각 체인의 테스트넷에 연결되며 API는 운영 환경과 완전히 동일합니다.
- 계정, API 키, 데이터가 운영 환경과 분리되어 있어 별도로 가입해야 하며, 사업자 인증은 자동으로 승인됩니다
- 각 테스트넷의 테스트 코인으로 결제하며, 트랜잭션은 해당 테스트넷의 블록 탐색기에서 확인할 수 있습니다
- 테스트 코인은 실제 가치가 없습니다. 메인넷 자산을 테스트 환경 주소로 보내지 마세요
| 네트워크 | 테스트넷 | 사용 가능한 통화 코드 |
|---|---|---|
| Ethereum | Ethereum Sepolia | eth usdc |
| BNB Smart Chain | BNB Smart Chain Testnet | bnbbsc usdtbsc |
| Polygon | Polygon Amoy | maticmainnet usdcmatic |
| Avalanche C-Chain | Avalanche Fuji | avax usdcavax |
| Arbitrum | Arbitrum Sepolia | etharb usdcarb |
| Base | Base Sepolia | ethbase usdcbase |
| Optimism | OP Sepolia | ethop usdcop |
| TRON | TRON Nile | trx usdttrc20 |
| Solana | Solana Devnet | sol usdcsol |
| Bitcoin | Bitcoin Testnet4 | btc |
멱등성과 재시도
리소스를 생성하는 모든 POST 요청에 Idempotency-Key 헤더를 포함할 수 있습니다. 네트워크 타임아웃이 발생하면 같은 키로 다시 요청하세요. 결제나 출금이 중복 생성되지 않습니다.
- 같은 키, 같은 내용: 첫 요청의 결과를 반환
- 같은 키, 다른 내용: 422
IDEMPOTENCY_KEY_REUSED응답 - 첫 요청이 아직 처리 중: 409
IDEMPOTENCY_IN_FLIGHT응답(잠시 후 다시 시도하세요)
POST /api/v1/payout HTTP/1.1
Host: omniwallet.ewin888.com
x-api-key: YOUR_API_KEY
Authorization: Bearer YOUR_JWT
Idempotency-Key: 6c0a8e6e-3f5b-4f7a-9a52-1f0c2d9b7e41
Content-Type: application/json