Python USDT Payment API Integration

Create USDT TRC-20 payment links with Python, retry requests with a stable idempotency key and verify payment signatures.

Keep the integration on your server

This example uses Python 3.11+ and built-in modules, without a client SDK. Importing the module makes no API call. Never ship an API key or signing key to a browser bundle.

First configure the HTTPS webhook. Set DEDYX_API_KEY and DEDYX_WALLET in your server environment. Download dedyx_client.py and import it from your backend.

The code is an integration starting point, not a complete order system. Add your durable order storage, request body limits, receiver routing and fulfilment transaction. The examples use synthetic orders; replace the sample fields before making a real request.

Create a payment with safe retries

Python 3.11+ · downloadable client
"""Python 3.11+ server-side example; no calls or credentials at import time."""
import hashlib
import hmac
import json
import random
import re
import time
from urllib.error import HTTPError, URLError
from urllib.request import HTTPRedirectHandler, Request, build_opener

API = 'https://api.dedyx.com/api/v1'


class NoRedirect(HTTPRedirectHandler):
    def redirect_request(self, req, fp, code, msg, headers, newurl):
        return None  # Never forward an API key to a redirected host.


def create_payment(api_key, wallet, amount, description, idempotency_key):
    """Persist the key and these fields with your order BEFORE calling this function."""
    if not re.fullmatch(r'[A-Za-z0-9._:-]{8,128}', idempotency_key):
        raise ValueError('Use a persisted 8–128 character idempotency key.')
    body = json.dumps({'merchant_wallet': wallet, 'amount': amount,
                       'description': description}, separators=(',', ':')).encode()
    request = Request(API + '/payments', data=body, method='POST', headers={
        'X-API-Key': api_key, 'Idempotency-Key': idempotency_key,
        'Content-Type': 'application/json'})
    opener = build_opener(NoRedirect())
    for attempt in range(3):
        delay = 0.5 * (2 ** attempt) + random.uniform(0, 0.25)
        try:
            with opener.open(request, timeout=10) as response:
                return json.load(response)
        except HTTPError as error:
            if error.code not in (429, 500, 502, 503, 504) or attempt == 2:
                raise
            retry_after = error.headers.get('Retry-After', '')
            if retry_after.isascii() and retry_after.isdigit():
                seconds = int(retry_after)
                if seconds > 60:
                    raise  # Schedule later with the SAME stored key/body.
                delay = max(delay, seconds)
            error.close()  # nginx may return HTML; do not assume an error JSON body.
        except (URLError, TimeoutError):
            if attempt == 2:
                raise  # Outcome may be uncertain; preserve the key and reconcile.
        time.sleep(delay)

The client uses a 10-second timeout and at most three attempts. 429 and selected 5xx responses are retried with the same serialized body and idempotency key. Numeric Retry-After is respected; long waits are left for your job scheduler. nginx errors can be HTML, so the client checks the status without assuming JSON.

409 is not blindly retried: check missing webhook settings, an occupied address or a changed body for the same key. For exhausted or uncertain retries keep the stored key and reconcile; replay retention is 24 hours by default. Keys must contain 8–128 ASCII letters, digits or ._:-.

Connect the response to your order

Example call — requires your own environment and order
import os
from dedyx_client import create_payment

# These fields must come from a persisted order record.
payment = create_payment(
    api_key=os.environ["DEDYX_API_KEY"],
    wallet=os.environ["DEDYX_WALLET"],
    amount="49.00",
    description="Order #1024",
    idempotency_key="order-1024-v1",
)
# Persist payment["payment_id"] and the response before redirecting.
print(payment["payment_url"])

Save the response together with your order before redirecting the customer to payment_url. Reuse the same key and request fields for retries of that logical order. Do not put personal information in description.

Verify the original webhook body

Python 3.11+ · signature and event ID verification
import hashlib
import hmac
import json
import re
import time

def verify_webhook(raw_body, timestamp, signature, header_event_id, key_id, keys, now=None):
    """keys maps saved X-Dedyx-Key-ID values to the full whsec_... string."""
    if not re.fullmatch(r'[0-9]{1,12}', timestamp or ''):
        raise ValueError('Invalid timestamp')
    if abs((time.time() if now is None else now) - int(timestamp)) > 300:
        raise ValueError('Stale signature')
    if key_id not in keys or not re.fullmatch(r'v1=[0-9a-f]{64}', signature or ''):
        raise ValueError('Unknown signing key or signature format')
    expected = 'v1=' + hmac.new(keys[key_id].encode(), timestamp.encode() + b'.' + raw_body,
                               hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        raise ValueError('Invalid signature')
    event = json.loads(raw_body)
    if not isinstance(event, dict) or not isinstance(event.get('event_id'), str) or not event['event_id'] or event['event_id'] != header_event_id:
        raise ValueError('Event ID mismatch')
    # A valid signature is only the first check; validate your own order before fulfilment.
    return event

Pass the raw request bytes/Buffer, X-Dedyx-Timestamp, X-Dedyx-Signature, X-Dedyx-Event-ID and X-Dedyx-Key-ID. The keys mapping contains your saved key IDs and full whsec_... secrets. Do not run JSON middleware before capturing the raw bytes.

After verification, require PAID, match the event to your stored order and compare the exact amount. Persist event_id and fulfilment atomically. A duplicate returns 2xx without repeating the action. See delivery and deduplication; signature verification alone must never issue a product.

Before enabling customer payments

  • Test a valid signature, altered body, stale timestamp and unknown key.
  • Test a duplicate event and a receiver crash during fulfilment.
  • Test an API timeout after a successful creation: retry the original key.
  • Test 429 with Retry-After, nginx HTML errors and temporary 503.
  • Review expiry, canceled orders and late transfers with your support team.

Full launch checklist and atomic fulfilment example · API errors · Request beta access