연동 안내
시작 순서
| 방식 | 받는 곳 | 준비 |
|---|---|---|
| B·C 운영비 자체 부담 | B 0.35% · 최소 0.30 · C 0.3% · 최소 0.20 USDT | 선택 · 입금 주소 활성화·Energy 를 서명 프로그램이 모지갑 TRX 로 채움 · 서명 설정 gas.max_trx_day (하루 TRX 한도) |
| A 금액 매칭 | 주소 1개, 주문마다 소수점 금액을 다르게 | 받는 주소만 |
| B 1회용 주소 | 주문마다 새 주소 | 서명 프로그램 → xpub |
| C 고객할당 | 고객마다 고정 주소, 주문 없이 보내도 입금 처리 | 서명 프로그램 → xpub |
- B·C: 가맹점 서버에서 서명 프로그램
init→ 출력된 xpub 를 운영자에게 전달 (A 는 받는 주소) - 운영자가 가맹점 등록 → 담당자 이메일로 설정 링크 수신
- 링크에서 비밀번호·OTP 설정 → API 키·웹훅 시크릿·서명 프로그램 토큰 한 번 표시 (보관)
- 대시보드 › 웹훅 › 연동 설정: 웹훅 주소, 결제 팝업 사이트
- 서버에서 주문 만들기 → 팝업 → 웹훅으로 지급
- B·C: 서명 프로그램
run상시 실행 (집금·이동·출금 서명)
인증
Authorization: Bearer sk_... 결제 서버: https://pay.usdtgate.net
API 키는 가맹점 서버에만. 브라우저·앱에 넣지 않음. 유출 시 대시보드 › 웹훅 › API 키 재발급 (기존 키 즉시 무효).
주문
| 요청 | 내용 |
|---|---|
| POST /v1/orders | {order_id, amount, currency?, customer_ref?, ttl_min?} · 같은 order_id 재요청 = 같은 주문 |
| GET /v1/orders/<id> | 주문 상태 |
| GET /v1/rates | 1 USDT = 현지 화폐 (시세) |
| POST /v1/customers/<ref>/address | C: 고객 고정 주소 |
| 값 | 설명 |
|---|---|
| amount | currency 없으면 USDT, 있으면 그 통화 금액 (KRW·JPY·USD 등) · 최소 1 USDT |
| customer_ref | 회원번호 등. C 는 필수 (고객 주소 결정) |
| ttl_min | 입금 기한(분), 기본 30 |
요청
curl -X POST https://pay.usdtgate.net/v1/orders \
-H "Authorization: Bearer sk_..." -H "Content-Type: application/json" \
-d '{"order_id":"chg-1001","amount":"50000","currency":"KRW","customer_ref":"user-77"}'
응답
{
"id": "ord_...", "order_id": "chg-1001", "status": "pending",
"network": "TRC20", "address": "T...", "address_index": 12,
"amount": "36.24", "paid": "0", "remaining": "36.24",
"currency": "KRW", "fiat_amount": "50000", "rate": "1379.7", "credit": "0",
"result": null, "expires_at": "...Z",
"pay_url": "https://pay.usdtgate.net/pay/ord_...", "tg_url": "..."
}
| status | |
|---|---|
| pending | 입금 대기 |
| partial | 일부 입금 |
| paid | 완료 (result: exact 정상 · over 초과) |
| expired | 만료 (result: under 부족 · null 미입금) |
결제 팝업
<script src="https://pay.usdtgate.net/checkout.js"></script>
<script>
GwPay.open(order.id, {
onPaid: d => reloadBalance(),
onStatus: d => {},
onClose: d => {},
lang: 'ko',
theme: 'simple' // simple · night · card
});
</script>
대시보드에 등록한 결제 팝업 사이트에서만 열림. theme 생략 = 대시보드 › 연동 설정 › 결제 화면 값. 팝업 알림은 화면 갱신용 · 지급은 웹훅으로만.
웹훅
POST (가맹점 웹훅 주소)
X-Gw-Timestamp: 1762650000
X-Gw-Signature: hex(HMAC-SHA256(whsec_..., timestamp + "." + body))
{"id": "evt_...", "type": "order.paid", "created": "...Z", "data": { ...주문 }}
서명·시각(5분) 확인 후 200 응답. 실패 시 2n분 간격 8회 재전송, 같은 id. 대시보드 › 웹훅에서 다시 보내기.
| type | |
|---|---|
| order.partial | 일부 입금 (credit_delta) |
| order.paid | 완료 (credit_delta) |
| order.expired | 만료 |
| deposit.customer | C: 주문 없이 고객 주소로 입금 |
| deposit.late | 만료 주문 주소·금액으로 입금 → 대시보드에서 주문 연결 |
| deposit.unknown | 연결 안 된 입금 |
| deposit.other_token | USDT 아닌 스테이블코인 입금 |
| job.updated | 집금·이동·출금 상태 |
| wallet.permission_changed | 지갑 서명 권한 변경 감지 |
검증 (Python)
from gateway_sdk import verify_webhook
if not verify_webhook(WHSEC, request.headers, request.get_data()):
abort(400)
받은 만큼 지급
order.partial·order.paid 마다 credit_delta(이번 입금분, 주문 통화)를 웹훅 id 당 한 번 더함. credit = 합계.
| 예 (5만 원, 1 USDT = 1,380원) | credit_delta | credit |
|---|---|---|
| 36.24 USDT 정확 | 50,000 | 50,000 |
| 20 USDT (부족) | 27,600 | 27,600 |
| 이어서 17.24 USDT | 23,780 | 51,380 |
| 덜 받고 만료 | 없음 | 그대로 |
주소 검증
주문 응답의 주소가 내 키에서 나온 것인지 고객에게 보여주기 전에 확인. 결제 서버가 털려도 고객 돈이 다른 곳으로 가지 않음.
from gateway_sdk import check_order check_order(order, xpub=MY_XPUB) # B·C check_order(order, fixed_address=MY_ADDR) # A # 다르면 AddressMismatch → 주문 중단
업종별 흐름
| 업종 | 요청 | 웹훅에서 할 일 |
|---|---|---|
| 구매 (쇼핑몰·디지털 상품·구독 갱신) | POST /v1/orders {order_id, amount, currency} → pay_url 또는 팝업 | order.paid → 주문 완료 · order.expired result=under → 덜 받음 처리 |
| 충전 (게임머니·포인트·지갑) | C: POST /v1/customers/<회원>/address → 고정 주소 · 또는 금액 지정 주문 | 고정 주소: deposit.customer 중 order_id 없는 것 → amount × 충전 비율 · 주문: credit_delta 만큼 |
| 환전 (게임머니 → USDT·판매자 정산) | 잔액 먼저 보류 → POST /v1/payouts {to, amount, ref, sig} (otp 생략 = 자동 출금) | job.updated done → 확정 · rejected·failed·expired → 보류 돌려줌 · unknown → 보류 유지 |
모든 웹훅은 id 당 한 번만 반영. 충전 비율·게임머니 단위는 가맹점이 정함. 세 흐름 전부 들어 있는 예: server.js (Node.js), 충전 주문형: flask_shop.py.
집금·출금 (B·C)
| 요청 | 내용 |
|---|---|
| POST /v1/sweeps | {address} 입금 주소 → 모지갑 (자동 집금도 됨) |
| POST /v1/moves | {amount, otp} 모지갑 → 출금 지갑 |
| POST /v1/payouts | {to, amount, otp, ref} 출금 지갑 → 고객 · 같은 ref = 같은 작업 · 최소 5 USDT · otp 대신 sig = 자동 출금 |
| GET /v1/jobs/<id> | 작업 상태, tx_id |
otp = 서명 프로그램 OTP (대시보드 OTP 와 별개). 120초 안에 서명 프로그램이 가져가야 함.
자동 출금: 서명 설정 payout.auto_max_per_tx·auto_max_per_day 안에서만 OTP·텔레그램 승인 없이 서명 (기본 0 = 꺼짐, 넘으면 rejected). 출금 지갑 잔액은 내부 이동(OTP)으로 미리 채워 둘 것.
자동 출금 sig: HMAC-SHA256(auto_secret, ref + "|" + to + "|" + 금액×1000000) 16진수 소문자. 예: ref wd-1 · 12.5 USDT → "wd-1|T…|12500000". auto_secret 은 서명 프로그램 설정 payout.auto_secret 과 가맹점 서버에만 둠 (게이트웨이에 보내지 않음, agent.py init 이 만들어 줌). 서명 불일치·같은 ref 재사용은 rejected. API 키나 게이트웨이가 털려도 sig 없이는 자동 출금 불가.
서명 프로그램 (B·C)
가맹점 서버에서 실행. 키는 가맹점 서버에만 있고 결제 서버는 서명할 수 없음. 나가는 연결만 사용.
unzip signer.zip && cd signer pip install -r requirements.txt cp signer_config.example.json signer_config.json python agent.py init signer_config.json # 키 파일·OTP·xpub 출력 → xpub 운영자에게 # signer_config.json: gateway_url = https://pay.usdtgate.net, signer_token = st_... python agent.py run signer_config.json # 상시 실행 python selftest_signer.py
| 설정 | 값 |
|---|---|
| gateway_url | https://pay.usdtgate.net |
| tron_api | https://nile.trongrid.io |
| usdt_contract | TXYZopYRdj2D9XRtbG411XZZ3kM5VkAeBf |
| payout | 1건·하루 한도, confirm_over 넘으면 텔레그램 승인 |
| fee_address | 대시보드 › 웹훅 › 수수료 받는 주소 |
| fee.max_per_day | 수수료 송금 하루 한도 (기본 500 USDT) |
| telegram_token | 서명 전용 봇 새로 만들기 (다른 프로그램 봇 같이 쓰지 않음) |
수수료
| 방식 | 요율 | 받는 방법 |
|---|---|---|
| B 1회용 주소 | 0.5% · 건당 최소 1.49 USDT | 모지갑 → 수수료 주소 자동 송금 · 미수 100 USDT 이상일 때 · 서명 프로그램이 fee_address·하루 한도 확인 후 서명 |
| C 고객할당 | 0.4% · 건당 최소 1.03 USDT | |
| B·C 출금 | 완료 1건당 실제 소각 TRX × 시세 × 1.3 · 최소 0.30 USDT (지금 약 1.03) | 위와 같이 합산 · 운영비 자체 부담이면 없음 |
| A 금액 매칭 | 월 50 USDT | 선납 · 기간 끝나고 3일 뒤 새 주문 402 |
대상: 완료 주문, 주문 없는 고객 입금, 만료 뒤 입금, 완료 출금. 내역은 대시보드 › 주문 › 수수료 내역 (CSV). 송금 기록은 작업 목록 kind=fee, 웹훅 job.updated.
오류
| 코드 | |
|---|---|
| 400 | {"error": "..."} 값 오류 |
| 401 | API 키·토큰 틀림, 또는 사용 중지된 가맹점 |
| 402 | A: 이용료 기간 만료 |
| 404 | 없는 주문·작업 |
| 429 | A: 같은 금액 대기 주문이 너무 많음, 잠시 후 다시 |
| 503 | 환율 일시 불가 (현지 화폐 주문) |