1. 홈
  2. 개발자
개발자

몇 분 만에 암호화폐 결제 연동하기

REST API는 NOWPayments v1과 호환되며 엔드포인트, 필드, 상태 값, 통화 코드가 동일합니다. x-api-key로 인증하고, 서명된 IPN으로 상태 업데이트를 받습니다.

API 기본 URLhttps://omniwallet.ewin888.com/api/v1/
테스트 환경https://omniwallet-dev.ewin888.com/api/v1/

빠른 시작

  1. 01

    API 키 발급

    가입 후 콘솔에 로그인해 ‘개발자 → API 키’에서 키를 생성하고, ‘IPN’ 메뉴에서 IPN 시크릿을 설정합니다. 먼저 테스트 환경에서 연동을 완료하는 것을 권장합니다.

  2. 02

    결제 생성

    POST /v1/payment 엔드포인트를 호출하면서 금액, 가격 기준 통화, 결제받을 코인을 전달합니다. 응답의 pay_address에는 해당 결제의 전용 주소가, pay_amount에는 결제해야 할 금액이 담겨 있습니다.

    POST /v1/paymentcurl
    curl -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"
    }
  3. 03

    고객 결제 안내

    주소와 금액(또는 QR 코드)을 고객에게 보여 주세요. POST /v1/invoice로 호스팅 인보이스 페이지를 만든 뒤, 응답의 invoice_url로 고객을 안내할 수도 있습니다.

    POST /v1/invoicecurl
    curl -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"
    }
  4. 04

    IPN 수신

    결제 상태가 바뀔 때마다 ipn_callback_url로 POST 요청을 보내 드립니다. 먼저 서명을 검증한 뒤 payment_status에 따라 주문 상태를 업데이트하세요.

    IPN 검증
  5. 05

    운영 환경으로 전환

    사업자 인증이 완료되면 운영 환경에서 새 API 키와 IPN 시크릿을 생성하고, 기본 URL을 운영 환경 주소로 바꿉니다.

IPN 서명 검증

IPN 시크릿으로 ‘키를 알파벳순으로 정렬한 JSON’에 대해 HMAC-SHA512를 계산하고, 그 결과를 16진수 문자열로 x-omni-sig 헤더에 담아 보냅니다. 전송되는 요청 본문이 바로 이 정렬된 JSON이므로, 수신한 원본 본문을 그대로 검증하면 됩니다.

  • 서명은 상수 시간 비교로 검증하세요
  • IPN은 중복되거나 순서가 뒤바뀌어 도착할 수 있습니다. payment_id와 상태를 기준으로 중복을 제거하고, 필요하면 GET /v1/payment/{id}를 호출해 최신 상태를 확인하세요
  • 2xx 응답은 수신 완료로 간주되며, 그 외의 응답이나 타임아웃은 자동으로 재시도됩니다
ExpressNode.js
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);
PHPPHP
<?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);
FlaskPython
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
IPN 본문 예시JSON
{
  "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가 호환되므로 대부분의 코드는 수정할 필요가 없습니다.

  1. 기본 URL 변경https://api.nowpayments.io/v1/ 주소를 아래의 OmniWallet URL로 바꿉니다.
  2. OmniWallet API 키로 교체헤더 이름은 그대로 x-api-key입니다.
  3. IPN 검증 업데이트OmniWallet의 IPN 시크릿과 x-omni-sig 헤더를 사용하도록 변경합니다. 기존 코드를 전혀 수정하지 않으려면 콘솔에서 x-nowpayments-sig 호환 헤더를 활성화할 수 있습니다.
  4. 지원 코인 확인통화 코드는 동일하지만(예: usdttrc20, usdcsol), 당사가 지원하는 코인만 사용할 수 있습니다. GET /v1/merchant/coins 또는 지원 코인 페이지에서 확인하세요.
  5. 출금 설정 확인출금은 기존과 마찬가지로 POST /v1/auth로 5분간 유효한 JWT를 발급받아 진행합니다. 또한 출금 권한이 있는 API 키에는 IP 화이트리스트를 설정해야 하며, 출금 대상 주소도 먼저 화이트리스트에 등록해야 합니다.
API 기본 URLdiff
- 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 키, 데이터가 운영 환경과 분리되어 있어 별도로 가입해야 하며, 사업자 인증은 자동으로 승인됩니다
  • 각 테스트넷의 테스트 코인으로 결제하며, 트랜잭션은 해당 테스트넷의 블록 탐색기에서 확인할 수 있습니다
  • 테스트 코인은 실제 가치가 없습니다. 메인넷 자산을 테스트 환경 주소로 보내지 마세요
네트워크테스트넷사용 가능한 통화 코드
EthereumEthereum Sepoliaeth usdc
BNB Smart ChainBNB Smart Chain Testnetbnbbsc usdtbsc
PolygonPolygon Amoymaticmainnet usdcmatic
Avalanche C-ChainAvalanche Fujiavax usdcavax
ArbitrumArbitrum Sepoliaetharb usdcarb
BaseBase Sepoliaethbase usdcbase
OptimismOP Sepoliaethop usdcop
TRONTRON Niletrx usdttrc20
SolanaSolana Devnetsol usdcsol
BitcoinBitcoin Testnet4btc

멱등성과 재시도

리소스를 생성하는 모든 POST 요청에 Idempotency-Key 헤더를 포함할 수 있습니다. 네트워크 타임아웃이 발생하면 같은 키로 다시 요청하세요. 결제나 출금이 중복 생성되지 않습니다.

  • 같은 키, 같은 내용: 첫 요청의 결과를 반환
  • 같은 키, 다른 내용: 422 IDEMPOTENCY_KEY_REUSED 응답
  • 첫 요청이 아직 처리 중: 409 IDEMPOTENCY_IN_FLIGHT 응답(잠시 후 다시 시도하세요)
HTTPHTTP
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

지금 암호화폐 결제를 시작하세요

무료로 가입해 테스트 환경에서 먼저 연동을 완료하고, 사업자 인증을 통과하면 운영 환경으로 전환할 수 있습니다.