Buat pesanan penerimaan
Membuat pesanan pembayaran bersama dengan jumlah tetap. Responsnya meliputi nomor pesanan, alamat penerima, checkout URL, dan pembayaran status.
notifyUrl diperlukan. Panggilan balik selanjutnya untuk pesanan ini dikirimkan langsung ke URL ini.
Header
| Kolom | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| x-api-key | string | Ya | Pedagang API Key. Gunakan nilai yang diawali dengan mch_ dari halaman pengaturan API pedagang. |
| x-merchant-uid | string | Ya | Merchant UID. |
| x-timestamp | string | Ya | 13 digit milidetik timestamp. Penyimpangan jam yang diperbolehkan secara default adalah 5 menit. |
| x-nonce | string | Ya | String acak unik untuk setiap permintaan, hingga 128 karakter. Penggunaan kembali ditolak. |
| x-signature | string | Ya | Tanda tangan hex dihasilkan dengan HMAC-SHA256. Payload berisi Merchant UID, timestamp, nonce, metode, jalur, query kanonik, dan body kanonik. |
Parameter
| Kolom | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| chainCode | string | Ya | Kode rantai. Nilai yang didukung: TRON / BSC / SOL. |
| tokenSymbol | string | Ya | Simbol token, misalnya USDT. |
| merchantOrderNo | string | Ya | Nomor pesanan pedagang. Maksimal 128 karakter. |
| amount | string | Ya | Jumlah pembayaran, hingga 6 tempat desimal. |
| notifyUrl | string | Ya | Panggilan balik khusus pesanan URL. Direkomendasikan HTTPS URL publik yang dimiliki oleh pedagang. Maksimal 500 karakter; tujuan jaringan privat, loopback, dan tidak aman dilarang. |
Contoh permintaan
{
"chainCode": "TRON",
"tokenSymbol": "USDT",
"merchantOrderNo": "M202604150001",
"amount": "100.00",
"notifyUrl": "https://merchant.example.com/api/uugate/payin-notify"
}Kolom respons
| Kolom | Tipe | Wajib | Deskripsi |
|---|---|---|---|
| orderNo | string | Ya | Nomor pesanan platform. |
| merchantOrderNo | string | null | Ya | Nomor pesanan pedagang. |
| bindKey | string | null | Ya | Binding key for exclusive-wallet orders; empty for shared pay-in orders. |
| chainCode | string | null | Ya | Jaringan pesanan. |
| tokenSymbol | string | null | Ya | Token pesanan. |
| txHash | string | null | Ya | Hash transaksi yang cocok; null sebelum terdeteksi. |
| amount | string | null | Ya | Jumlah pesanan. |
| paidAmount | string | null | Ya | Jumlah yang dibayarkan. Kosong sebelum pembayaran. |
| address | string | null | Ya | Alamat penerima sebenarnya. |
| cashierUrl | string | null | Ya | URL checkout lengkap dengan token akses acak. Pertahankan seluruh fragmen #ct=. |
| paymentUri | string | null | Ya | Pembayaran dompet URI. |
| expireAt | string | null | Ya | Waktu kedaluwarsa pesanan. |
| createdAt | string | null | Ya | Waktu yang diciptakan. |
| detectedAt | string | null | Ya | Payment detection time; empty before detection. |
| confirmedAt | string | null | Ya | On-chain confirmation time; empty before confirmation. |
| completedAt | string | null | Ya | Order completion time; empty before completion. |
| status | string | Ya | Pesan status. |
Contoh respons
{
"orderNo": "PI1776193200123ABCD1234",
"merchantOrderNo": "M202604150001",
"bindKey": null,
"amount": "100.000000",
"paidAmount": null,
"address": "TXYZ3x9nJ5J4xP2gh6mQW7rT31jB8Y2pA9",
"cashierUrl": "https://open.uugate.com/SK/PI1776193200123ABCD1234#ct=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"paymentUri": "TRON:TXYZ3x9nJ5J4xP2gh6mQW7rT31jB8Y2pA9?token=USDT&amount=100.000000",
"expireAt": "2026-04-18T06:30:00.000Z",
"createdAt": "2026-04-18T06:00:00.000Z",
"detectedAt": null,
"confirmedAt": null,
"completedAt": null,
"status": "waiting_payment",
"chainCode": "TRON",
"tokenSymbol": "USDT",
"txHash": null
}Contoh kode
Menjalankan: simpan Java sebagai UUGateExample.java (UUGateCallback.java untuk callback), lalu gunakan java nama file; PHP: php example.php; Go: go run example.go; Node.js: node example.mjs. Verifikasi callback membaca isi asli dari input standar.
import { createHmac, randomBytes } from 'node:crypto';
function required(name) {
const value = process.env[name]?.trim();
if (!value) throw new Error(name + ' is required');
return value;
}
const uid = required("UUGATE_MERCHANT_UID"), key = required("UUGATE_API_KEY");
const timestamp = String(Date.now()), nonce = randomBytes(16).toString("hex");
const method = 'POST', path = '/openapi/payin/orders';
const canonicalQuery = '', canonicalBody = "{\"amount\":\"100.00\",\"chainCode\":\"TRON\",\"merchantOrderNo\":\"M202604150001\",\"notifyUrl\":\"https://merchant.example.com/api/uugate/payin-notify\",\"tokenSymbol\":\"USDT\"}";
const payload = [uid, timestamp, nonce, method, path, canonicalQuery, canonicalBody].join('\n');
const signature = createHmac('sha256', key).update(payload, 'utf8').digest('hex');
const base = (process.env.UUGATE_BASE_URL || 'https://api.uugate.com').replace(/\/+$/, '');
const response = await fetch(base + path, {
method, redirect: 'error', signal: AbortSignal.timeout(30000),
headers: { 'Content-Type': 'application/json', 'x-api-key': key, 'x-merchant-uid': uid,
'x-timestamp': timestamp, 'x-nonce': nonce, 'x-signature': signature },
...(canonicalBody ? { body: canonicalBody } : {}),
});
const raw = await response.text();
if (!response.ok) throw new Error('HTTP ' + response.status + ': ' + (raw || 'empty response body'));
JSON.parse(raw);
console.log(raw);import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.HexFormat;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class UUGateExample {
static String required(String name) {
String value = System.getenv(name);
if (value == null || value.isBlank()) throw new IllegalArgumentException(name + " is required");
return value.trim();
}
public static void main(String[] args) throws Exception {
String uid = required("UUGATE_MERCHANT_UID"), key = required("UUGATE_API_KEY");
String timestamp = Long.toString(System.currentTimeMillis());
byte[] random = new byte[16]; new SecureRandom().nextBytes(random);
String nonce = HexFormat.of().formatHex(random);
String method = "POST", path = "/openapi/payin/orders";
String canonicalQuery = "", canonicalBody = "{\"amount\":\"100.00\",\"chainCode\":\"TRON\",\"merchantOrderNo\":\"M202604150001\",\"notifyUrl\":\"https://merchant.example.com/api/uugate/payin-notify\",\"tokenSymbol\":\"USDT\"}";
String payload = String.join("\n", uid, timestamp, nonce, method, path, canonicalQuery, canonicalBody);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = HexFormat.of().formatHex(mac.doFinal(payload.getBytes(StandardCharsets.UTF_8)));
String base = System.getenv().getOrDefault("UUGATE_BASE_URL", "https://api.uugate.com").replaceAll("/+$", "");
HttpRequest request = HttpRequest.newBuilder(URI.create(base + path)).timeout(Duration.ofSeconds(30))
.header("Content-Type", "application/json").header("x-api-key", key).header("x-merchant-uid", uid)
.header("x-timestamp", timestamp).header("x-nonce", nonce).header("x-signature", signature)
.method(method, canonicalBody.isEmpty() ? HttpRequest.BodyPublishers.noBody()
: HttpRequest.BodyPublishers.ofString(canonicalBody, StandardCharsets.UTF_8)).build();
HttpClient client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.NEVER).build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
if (response.statusCode() < 200 || response.statusCode() >= 300)
throw new IllegalStateException("HTTP " + response.statusCode() + ": "
+ (response.body().isEmpty() ? "empty response body" : response.body()));
if (response.body().isBlank()) throw new IllegalStateException("Empty success response");
System.out.println(response.body());
}
}<?php
function required(string $name): string {
$value = trim((string) getenv($name));
if ($value === '') throw new RuntimeException($name . ' is required');
return $value;
}
$uid = required("UUGATE_MERCHANT_UID"); $key = required("UUGATE_API_KEY");
$timestamp = (string) (int) floor(microtime(true) * 1000);
$nonce = bin2hex(random_bytes(16));
$method = 'POST'; $path = '/openapi/payin/orders';
$canonicalQuery = ''; $canonicalBody = '{"amount":"100.00","chainCode":"TRON","merchantOrderNo":"M202604150001","notifyUrl":"https://merchant.example.com/api/uugate/payin-notify","tokenSymbol":"USDT"}';
$payload = implode("\n", [$uid, $timestamp, $nonce, $method, $path, $canonicalQuery, $canonicalBody]);
$signature = hash_hmac('sha256', $payload, $key);
$base = rtrim(getenv('UUGATE_BASE_URL') ?: 'https://api.uugate.com', '/');
$curl = curl_init($base . $path);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => $method, CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 10, CURLOPT_TIMEOUT => 30, CURLOPT_FOLLOWLOCATION => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'x-api-key: ' . $key,
'x-merchant-uid: ' . $uid, 'x-timestamp: ' . $timestamp,
'x-nonce: ' . $nonce, 'x-signature: ' . $signature],
]);
if ($canonicalBody !== '') curl_setopt($curl, CURLOPT_POSTFIELDS, $canonicalBody);
$raw = curl_exec($curl);
if ($raw === false) throw new RuntimeException('Transport error: ' . curl_error($curl));
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($status < 200 || $status >= 300)
throw new RuntimeException('HTTP ' . $status . ': ' . ($raw === '' ? 'empty response body' : $raw));
json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
echo $raw, PHP_EOL;package main
import (
"crypto/hmac"
"crypto/sha256"
"crypto/rand"
"encoding/hex"
"fmt"
"encoding/json"
"io"
"net/http"
"time"
"os"
"strings"
)
func required(name string) string {
value := strings.TrimSpace(os.Getenv(name))
if value == "" { panic(name + " is required") }
return value
}
func main() {
uid, key := required("UUGATE_MERCHANT_UID"), required("UUGATE_API_KEY")
timestamp := fmt.Sprint(time.Now().UnixMilli())
random := make([]byte, 16); if _, err := rand.Read(random); err != nil { panic(err) }
nonce := hex.EncodeToString(random)
method, path := "POST", "/openapi/payin/orders"
canonicalQuery, canonicalBody := "", "{\"amount\":\"100.00\",\"chainCode\":\"TRON\",\"merchantOrderNo\":\"M202604150001\",\"notifyUrl\":\"https://merchant.example.com/api/uugate/payin-notify\",\"tokenSymbol\":\"USDT\"}"
payload := strings.Join([]string{uid, timestamp, nonce, method, path, canonicalQuery, canonicalBody}, "\n")
mac := hmac.New(sha256.New, []byte(key)); mac.Write([]byte(payload))
signature := hex.EncodeToString(mac.Sum(nil))
base := strings.TrimRight(os.Getenv("UUGATE_BASE_URL"), "/")
if base == "" { base = "https://api.uugate.com" }
request, err := http.NewRequest(method, base + path, strings.NewReader(canonicalBody))
if err != nil { panic(err) }
for key, value := range map[string]string{"Content-Type": "application/json", "x-api-key": key,
"x-merchant-uid": uid, "x-timestamp": timestamp, "x-nonce": nonce, "x-signature": signature} {
request.Header.Set(key, value)
}
client := &http.Client{Timeout: 30 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }}
response, err := client.Do(request); if err != nil { panic(err) }
defer response.Body.Close()
raw, err := io.ReadAll(response.Body); if err != nil { panic(err) }
if response.StatusCode < 200 || response.StatusCode >= 300 {
detail := string(raw); if detail == "" { detail = "empty response body" }
panic(fmt.Sprintf("HTTP %d: %s", response.StatusCode, detail))
}
if !json.Valid(raw) { panic("Invalid JSON success response") }
fmt.Println(string(raw))
}Catatan
- Pesanan pembayaran bersama dapat memicu 40105 ketika pedagang, jaringan, token, dan jumlah yang sama masih dipesan oleh pesanan lain yang valid.
- Keempat bahasa memakai jalur, parameter, dan kontrak respons yang sama. Kredensial dibaca dari UUGATE_MERCHANT_UID dan UUGATE_API_KEY; UUGATE_BASE_URL secara default adalah https://api.uugate.com.
- canonicalBody adalah JSON ringkas dengan nama kolom terurut. Setelah mengubah parameter, urutkan key objek secara rekursif, pertahankan urutan array, dan gunakan UTF-8 tanpa escape tambahan untuk slash atau Unicode. Nominal berupa string; boolean true / false.
- Respons sukses langsung berisi objek bisnis tanpa pembungkus code / data. Periksa status HTTP sebelum isi. Error bisnis berisi code, message, data, requestId; penolakan keamanan dapat hanya mengirim HTTP 401 / 403 dengan isi kosong.
- Setelah timeout atau HTTP 5xx, periksa pesanan sebelum mengulang pembuatan atau pengiriman. Buat timestamp, nonce, dan tanda tangan baru untuk setiap permintaan.