> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lurufoundation.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Payment Detail

> Retrieves the detail of a payment transaction



## OpenAPI

````yaml GET /payment/detail
openapi: 3.0.0
info:
  title: Coinut Partner Ramp APIs
  description: >+
    ## API Version Differences


    ### Old Version

    1. **Field Naming Format**: Both request parameters and response parameters
    use the **snake_case** (lowercase with underscore) naming convention. Some
    interfaces may return fields in camelCase, resulting in an inconsistent
    field format.

    2. **HTTP Status Code**: All responses (both success and failure) use the
    **200 OK** HTTP status code. The success or failure of the request is
    distinguished by a custom `status` field in the response body.

    3. **Module Structure**: All interfaces were mixed in a single module
    without modular splitting.

    4. **Warning**: The old version will be deprecated in the future. Please
    migrate to the new version as soon as possible.


    ### New Version

    1. **Field Naming Format**: All request parameters and response parameters
    are **unified to camelCase** for a consistent field format.

    2. **HTTP Status Code**:
       - **Success Response**: Unified HTTP status code **200 OK**.
       - **Failure Response**: Unified HTTP status code **422 Unprocessable Entity**.
       - A custom status code is used to identify the specific failure type, and a `message` field is provided to describe the detailed failure reason.
    3. **Module Structure**: Interfaces are **modularized by business
    function**. Each module has its own route prefix (e.g., `/customer`,
    `/virtual-account`), and each module is responsible for a specific set of
    functions.


    ---


    ## Authentication (Signature)

    All requests **must** be signed using **HMAC-SHA256**, with the following
    HTTP headers:


    - `X-API-Key`: Your API key

    - `X-Timestamp`: Unix timestamp in seconds (as string)

    - `X-Nonce`: A unique random string per request

    - `X-Signature`: HMAC-SHA256 signature (lowercase hex string)


    The server will verify the signature using your **API secret** stored on
    Coinut side.


    ### Canonical string format

    <details>
      <summary><strong>Click to expand canonical string format</strong></summary>
    The canonical string is built as:


    ```text

    METHOD\n

    HOST\n

    PATH\n

    QUERY\n

    BODY_SHA256_HEX\n

    TIMESTAMP\n

    NONCE

    ```


    Where:


    - `METHOD`: Uppercase HTTP method, e.g. `GET`, `POST`

    - `HOST`: Request host, e.g. `partner-api.coinut.com`

    - `PATH`: Request path (no query string), e.g. `/balance`

    - `QUERY`: Raw query string **without** leading `?`, e.g.
    `currency=USDT&network=TRX`.
      - Parameters are **not re-ordered** or normalized; use them exactly as in the URL.
    - `BODY_SHA256_HEX`:
      - For JSON requests:
        - Serialize the body to JSON string (e.g. using your language's JSON serializer).
        - Compute SHA-256 of this string and encode as lowercase hex.
      - For requests without body (e.g. `GET`) or multipart/form-data:
        - Use an empty string.
    - `TIMESTAMP`: Same as `X-Timestamp`.

    - `NONCE`: Same as `X-Nonce`.


    The signature is:


    ```text

    X-Signature = HMAC_SHA256_HEX(apiSecret, canonicalString)

    ```

    </details>


    ---


    <details>
      <summary><strong>Click to expand JavaScript (Node.js) signing example</strong></summary>

    ```js

    import crypto from 'crypto';

    import fetch from 'node-fetch';


    const apiKey = 'YOUR_API_KEY';

    const apiSecret = 'YOUR_API_SECRET';


    async function signAndRequest() {
      const method = 'POST';
      const host = 'your-partner-api-host.com'; // e.g. partner-ramp-api.coinut.com
      const path = '/payment/estimate';
      const query = ''; // e.g. 'currency=USDT&network=TRX'

      const bodyObject = {
        // your request body here
      };
      const body = JSON.stringify(bodyObject);

      const timestamp = Math.floor(Date.now() / 1000).toString();
      const nonce = crypto.randomUUID();

      const bodyHash = crypto.createHash('sha256').update(body).digest('hex');

      const canonical = [
        method.toUpperCase(),
        host,
        path,
        query,
        bodyHash,
        timestamp,
        nonce,
      ].join('\n');

      const signature = crypto
        .createHmac('sha256', apiSecret)
        .update(canonical)
        .digest('hex');

      const url = `https://${host}${path}${query ? '?' + query : ''}`;

      const res = await fetch(url, {
        method,
        headers: {
          'Content-Type': 'application/json',
          'X-API-Key': apiKey,
          'X-Timestamp': timestamp,
          'X-Nonce': nonce,
          'X-Signature': signature,
        },
        body,
      });

      const data = await res.json();
      console.log(data);
    }


    signAndRequest().catch(console.error);

    ```


    </details>


    ---


    <details>
      <summary><strong>Click to expand Java signing example</strong></summary>

    ```java

    import javax.crypto.Mac;

    import javax.crypto.spec.SecretKeySpec;

    import java.net.URI;

    import java.net.http.HttpClient;

    import java.net.http.HttpRequest;

    import java.net.http.HttpResponse;

    import java.nio.charset.StandardCharsets;

    import java.security.MessageDigest;

    import java.time.Instant;

    import java.util.UUID;


    public class CoinutSignatureExample {

        private static String sha256Hex(String data) throws Exception {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : hash) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        }

        private static String hmacSha256Hex(String secret, String data) throws Exception {
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            mac.init(keySpec);
            byte[] result = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte b : result) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        }

        public static void main(String[] args) throws Exception {
            String apiKey = "YOUR_API_KEY";
            String apiSecret = "YOUR_API_SECRET";

            String method = "POST";
            String host = "your-partner-api-host.com"; // e.g. partner-ramp-api.coinut.com
            String path = "/payment/estimate";
            String query = ""; // e.g. "currency=USDT&network=TRX"

            String body = "{\"amount\":100}"; // your JSON body

            String timestamp = String.valueOf(Instant.now().getEpochSecond());
            String nonce = UUID.randomUUID().toString();

            String bodyHash = body.isEmpty() ? "" : sha256Hex(body);

            String canonical = String.join("\n",
                    method.toUpperCase(),
                    host,
                    path,
                    query,
                    bodyHash,
                    timestamp,
                    nonce
            );

            String signature = hmacSha256Hex(apiSecret, canonical);

            String url = "https://" + host + path + (query.isEmpty() ? "" : "?" + query);

            HttpClient client = HttpClient.newHttpClient();
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .header("Content-Type", "application/json")
                    .header("X-API-Key", apiKey)
                    .header("X-Timestamp", timestamp)
                    .header("X-Nonce", nonce)
                    .header("X-Signature", signature)
                    .method(method, HttpRequest.BodyPublishers.ofString(body))
                    .build();

            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println(response.statusCode());
            System.out.println(response.body());
        }
    }

    ```


    </details>


    <details>
      <summary><strong>Click to expand Python signing example</strong></summary>

    ```python

    import time

    import uuid

    import hmac

    import hashlib

    import json

    import requests


    API_KEY = "YOUR_API_KEY"

    API_SECRET = "YOUR_API_SECRET"



    def sha256_hex(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()


    def hmac_sha256_hex(secret: str, data: str) -> str:
        return hmac.new(
            secret.encode("utf-8"),
            data.encode("utf-8"),
            hashlib.sha256,
        ).hexdigest()


    def main():
        method = "POST"
        host = "your-partner-api-host.com"  # e.g. partner-ramp-api.coinut.com
        path = "/payment/estimate"
        query = ""  # e.g. "currency=USDT&network=TRX"

        body_obj = {
            # your request body here
        }
        body = json.dumps(body_obj, separators=(",", ":"))

        timestamp = str(int(time.time()))
        nonce = str(uuid.uuid4())

        body_hash = sha256_hex(body) if body else ""

        canonical = "\n".join(
            [
                method.upper(),
                host,
                path,
                query,
                body_hash,
                timestamp,
                nonce,
            ]
        )

        signature = hmac_sha256_hex(API_SECRET, canonical)

        url = f"https://{host}{path}"
        if query:
            url += f"?{query}"

        headers = {
            "Content-Type": "application/json",
            "X-API-Key": API_KEY,
            "X-Timestamp": timestamp,
            "X-Nonce": nonce,
            "X-Signature": signature,
        }

        resp = requests.post(url, headers=headers, data=body)
        print(resp.status_code)
        print(resp.text)


    if __name__ == "__main__":
        main()
    ```


    </details>


    ---


    ## Crypto Address Ownership Verification


    <details>
      <summary><strong>Click to expand signing instructions</strong></summary>

    When calling `POST /create-va` with `to_address` and `to_network`, you must
    also provide a `signature` field to prove ownership of the wallet address.
    The signature is verified server-side; requests with an invalid or missing
    signature will be rejected.


    **Message to sign** (substitute actual values; date must be today in
    DD/MM/YYYY format):


    ```

    I am verifying ownership of the wallet address {to_address} as user
    {user_id}. This message was signed on {DD/MM/YYYY} to confirm my control
    over this wallet.

    ```


    **ETH — sign with ethers.js:**


    ```js

    import { ethers } from 'ethers';

    import dayjs from 'dayjs';


    const wallet = new ethers.Wallet(privateKey);

    const today = dayjs().format('DD/MM/YYYY');

    const message =
      `I am verifying ownership of the wallet address ${toAddress} as user ${userId}. ` +
      `This message was signed on ${today} to confirm my control over this wallet.`;
    const signature = await wallet.signMessage(message);

    ```


    **TRX — sign with TronWeb:**


    ```js

    import TronWeb from 'tronweb';

    import dayjs from 'dayjs';


    const tronWeb = new TronWeb({ fullHost: 'https://api.trongrid.io',
    privateKey });

    const today = dayjs().format('DD/MM/YYYY');

    const message =
      `I am verifying ownership of the wallet address ${toAddress} as user ${userId}. ` +
      `This message was signed on ${today} to confirm my control over this wallet.`;
    const signature = await tronWeb.trx.signMessageV2(message);

    ```


    </details>


    ---


    ## Webhook Retry Policy


    <details>
      <summary><strong>Callback delivery is processed by a scheduler that runs every 1 minute.</strong></summary>

    - A retry is triggered when either:
      - callback payload build fails, or
      - callback HTTP delivery fails.
    - Maximum retry count is 4 retries after the initial attempt (up to 5 total
    delivery attempts).

    - If all retries are exhausted, the event remains failed and no further
    automatic retries are performed.


    Retry intervals after each failed attempt:


    - Retry 1: 10 minutes after the previous failed attempt

    - Retry 2: 1 hour after the previous failed attempt

    - Retry 3: 1 day after the previous failed attempt

    - Retry 4: 1 week after the previous failed attempt


    </details>


    ---


    ## Release Notes (for Partners)


    <details>
      <summary><strong>2026-04-14 Release</strong></summary>

    ### Summary


    - Swagger contract changes:
      - **0** new endpoints
      - **0** removed endpoints
      - **1** updated endpoint

    ### Changes to existing endpoints


    - `POST /virtual-account/create`
      - Added optional request field: `whitelist` (object, AUD VA only)
        - `whitelist.bsbNumber` (string, required): BSB number of the source account that will send funds to the AUD VA
        - `whitelist.accountNumber` (string, required): Account number of the source account
        - `whitelist.accountName` (string, optional): Account holder name
      - When `whitelist` is provided for an AUD VA, the source account is automatically registered as a whitelisted sender once the virtual account is successfully created by the banking provider. Only whitelisted accounts can transfer funds into an AUD virtual account.

    ### Notes


    - This field is only meaningful for AUD currency. Providing `whitelist` for
    non-AUD currencies has no effect.

    - The whitelist registration happens asynchronously after the banking
    provider confirms VA creation (i.e., after the `va.approved` webhook is
    fired). There may be a short delay between VA approval and the whitelist
    taking effect.


    </details>


    <details>
      <summary><strong>2026-03-27 Release</strong></summary>

    ### Summary


    - Swagger contract changes since `98c5abc353dbcf686f8369250f9f3c75c78c6c1f`:
      - **0** new endpoints
      - **0** removed endpoints
      - **9** updated endpoints

    ### Changes to existing endpoints


    - `POST /customer/create`
      - Added optional request field: `externalId`
    - `GET /customer/detail`
      - Added optional query param: `externalId`
      - Added optional response fields: `externalId`, `sumsubLink`
    - `GET /customer/list`
      - Added optional response field in each list item: `externalId`
    - `POST /autoramp-flow/create`
      - Removed top-level request field: `customerId`
    - `POST /autoramp-flow/onramp`
      - Removed top-level request field: `customerId`
      - Added/required nested request field: `from.customerId`
    - `POST /autoramp-flow/offramp`
      - Removed top-level request field: `customerId`
      - Added/required nested request field: `from.customerId`
    - `GET /autoramp-flow/list`
      - Removed query param: `customerId`
      - Response list item no longer includes: `customerId`, `creator`
    - `GET /autoramp-flow/detail`
      - Response no longer includes: `customerId`, `creator`
    - `POST /virtual-account/create-aud-whitelist`
      - Request `sourceAccount.accountName` is now optional
      - Request no longer requires `sourceAccount.accountStatus` (server sets status internally)

    ### Breaking compatibility notes


    - Requests that still send top-level `customerId` for Autoramp Flow
    create/onramp/offramp should be updated to the new request shapes above.

    - Consumers of Autoramp Flow list/detail responses should stop relying on
    `customerId` and `creator` fields.


    </details>


    <details>
      <summary><strong>2026-03-22 Release</strong></summary>

    ### New modules


    - **Autoramp Flow**
      - `GET /autoramp-flow/list`
      - `GET /autoramp-flow/detail`
      - `POST /autoramp-flow/create`
      - `POST /autoramp-flow/onramp`
      - `POST /autoramp-flow/offramp`
      - `POST /autoramp-flow/delete`
    - **Deposit Address**
      - `POST /deposit-address/create`
      - `GET /deposit-address/list`
      - `GET /deposit-address/detail`
    - **Deposit**
      - `GET /deposit/list`
      - `GET /deposit/detail`
      - `POST /deposit/submitAttachmentFile`
    - **Trade**
      - `GET /trade/list`
      - `GET /trade/detail`

    ### Changes to existing endpoints


    - `GET /customer/list`
      - Response is now paginated: `data = { list, total, pageNum, pageSize }`
      - Added query params: `pageNum`, `pageSize`
    - `GET /bank-account/list`
      - Response is now paginated: `data = { list, total, pageNum, pageSize }`
      - `customerId` is now optional
      - Added query params: `pageNum`, `pageSize`
    - `GET /crypto-address/list`
      - Response is now paginated: `data = { list, total, pageNum, pageSize }`
      - `customerId` is now optional
      - Added query params: `pageNum`, `pageSize`
    - `GET /virtual-account/list`
      - Response is now paginated: `data = { list, total, pageNum, pageSize }`
      - `customerId` is now optional
      - Added query params: `pageNum`, `pageSize`

    ### Webhook update (important)


    - Webhook top-level structure is unchanged:
      - `event`
      - `payload`
    - `payload` now follows the same schema as the corresponding Detail API
    `data`.
      - Deposit events -> same schema as `GET /deposit/detail` `data`
      - Trade events -> same schema as `GET /trade/detail` `data`
      - Customer / Virtual Account / Deposit Address / Payment follow the same rule

    ### Backward compatibility


    - Old APIs under the `old` module are unchanged.

    - Old webhook format is unchanged.

    - This release only adds/updates the endpoints listed above.


    </details>

  version: '1.0'
  contact: {}
servers:
  - url: https://ramp-sandbox.hexarails.ai
    description: Sandbox
security:
  - api_key: []
  - api_secret: []
tags: []
paths:
  /payment/detail:
    get:
      tags:
        - Payment
      summary: Get payment detail
      description: Retrieves the detail of a payment transaction
      operationId: PaymentController_getPaymentDetail
      parameters:
        - name: id
          required: true
          in: query
          description: Payment ID
          schema:
            example: 12209d2d-47b4-4a07-8a01-b49052cd8204
            type: string
      responses:
        '200':
          description: Get payment detail successful
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetPaymentDetailResponseDto'
        '422':
          description: Error response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
components:
  schemas:
    GetPaymentDetailResponseDto:
      type: object
      properties:
        status:
          type: string
          description: Success response status
          enum:
            - SUCCESS
          example: SUCCESS
        data:
          description: Response data
          allOf:
            - $ref: '#/components/schemas/GetPaymentDetailDataDto'
      required:
        - status
        - data
    ErrorResponseDto:
      type: object
      properties:
        status:
          type: string
          description: Error status
          enum:
            - INVALID_PARAMETERS
            - UNAUTHORISED
            - FAILED
          example: INVALID_PARAMETERS
        message:
          type: string
          description: Error message
          example: invalid parameters
      required:
        - status
        - message
    GetPaymentDetailDataDto:
      type: object
      properties:
        id:
          type: string
          description: Payment ID
          example: 12209d2d-47b4-4a07-8a01-b49052cd8204
        status:
          type: string
          description: Payment status
          enum:
            - PROCESSING
            - CONFIRMED
          example: CONFIRMED
        rate:
          type: string
          description: Exchange rate
          example: '0.9950'
        deductAmount:
          type: string
          description: The amount to deduct from balance
          example: '180.50'
        deductCurrency:
          type: string
          description: Deduct currency
          example: USDT
        receivedCurrency:
          type: string
          description: Received currency
          example: USD
        receivedAmount:
          type: string
          description: Received amount
          example: '100.00'
        bankCharge:
          type: string
          description: Bank charge
          example: '80.00'
        reference:
          type: string
          description: Payment reference
          example: payment-123456
        createTime:
          type: string
          description: Create time
          example: '2026-03-12 17:22:04'
        bankAccount:
          description: The payee's bank account info
          allOf:
            - $ref: '#/components/schemas/PaymentDetailBankAccountDto'
      description: >-
        Payment detail payload. Shared by: (1) GET /payment/detail response; (2)
        Webhook callback when event is PAYMENT_SETTLED.
      required:
        - id
        - status
        - rate
        - deductAmount
        - deductCurrency
        - receivedCurrency
        - receivedAmount
        - bankCharge
        - reference
        - createTime
        - bankAccount
    PaymentDetailBankAccountDto:
      type: object
      properties:
        bankName:
          type: string
          description: Bank name
          example: New York Bank
        accountNo:
          type: string
          description: Bank account number
          example: '123451097890'
        bankCountry:
          type: object
          description: Bank country
          example: SG
          nullable: true
        bankAddress:
          type: object
          description: Bank address
          example: 1 Marina Blvd
          nullable: true
        swift:
          type: string
          description: SWIFT code
          example: CHASUS33
        beneficiaryName:
          type: string
          description: Beneficiary name
          example: John Doe
        beneficiaryAddress:
          type: string
          description: Beneficiary address
          example: 123 Main St
      required:
        - bankName
        - accountNo
        - swift
        - beneficiaryName
        - beneficiaryAddress
  securitySchemes:
    api_key:
      type: apiKey
      name: X-API-Key
      in: header
    api_secret:
      type: apiKey
      name: X-API-Secret
      in: header

````