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

# Multi-Currency Payin API Reference

> Create, cancel, refund, and query multi-currency payins across APAC using Hello Clever v2. Supports bank transfer, e-wallet, QR, and mobile money methods.

The v2 Payin API lets you collect payments from customers in multiple currencies using a single, consistent schema. You can accept bank transfers, e-wallets, QR payments, mobile money, and more, all through the same set of endpoints. This page documents every Payin endpoint, including sandbox simulation for testing.

<Note>
  Before creating a payin, call Get Payin Methods and Get Required Fields to discover which payment methods and customer fields are available for your configured currency.
</Note>

***

## Get Payin Methods

<Badge color="green">GET</Badge> `/v2/payins/payin_methods`

Returns all payment methods available for your `app-id`. The methods returned depend on the country and currency configured in your Hello Clever Merchant Dashboard.

### Response fields

<ResponseField name="records" type="object[]">
  Array of available payin methods.

  <Expandable title="properties">
    <ResponseField name="currency" type="string">
      ISO 4217 currency code (e.g., `MYR`, `VND`).
    </ResponseField>

    <ResponseField name="payin_method_name" type="string">
      The method identifier (e.g., `my_bank_duitnow_myr`). Pass this value as `payin_method_name` when you create a payin.
    </ResponseField>
  </Expandable>
</ResponseField>

<Note>
  v2 identifies payment methods by `payin_method_name`. The `payin_method_code` field belongs to Payment Gateway 3 and is not returned or accepted here. Call Get Payin Required Fields for a method's limits, logo, and required parameters.
</Note>

<CodeGroup>
  ```bash Request theme={null}
  curl --request GET \
    --url https://api.cleverhub.co/api/v2/payins/payin_methods \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY'
  ```

  ```json Response theme={null}
  {
    "records": [
      {
        "currency": "MYR",
        "payin_method_name": "my_bank_duitnow_myr"
      }
    ]
  }
  ```
</CodeGroup>

***

## Get Payin Required Fields

<Badge color="green">GET</Badge> `/v2/payins/payin_method/params`

Returns the fields required (or optional) to create a payin for a specific payment method. Call this once per method to understand what customer data you need to collect.

### Query parameters

<ParamField query="payin_method_name" type="string" required>
  The method identifier returned from Get Payin Methods (e.g., `my_bank_duitnow_myr`).
</ParamField>

### Response fields

<ResponseField name="record" type="object">
  Details for the requested payment method.

  <Expandable title="properties">
    <ResponseField name="currency" type="string">
      ISO 4217 currency code.
    </ResponseField>

    <ResponseField name="symbol" type="string">
      Currency symbol.
    </ResponseField>

    <ResponseField name="logo" type="string">
      URL of the bank or e-wallet logo.
    </ResponseField>

    <ResponseField name="payin_method_name" type="string">
      The method identifier.
    </ResponseField>

    <ResponseField name="description" type="string">
      Human-readable description of the method.
    </ResponseField>

    <ResponseField name="payin_method_params" type="object">
      The parameters to send in `payin_method_params` when creating the payin. Each entry is formatted as `type:Mandatory|Optional <description>`.
    </ResponseField>

    <ResponseField name="banks" type="object[]">
      Supported banks or e-wallets for the method, each with a `code` and `name`. Empty for methods that do not require a provider selection.
    </ResponseField>

    <ResponseField name="payment_type" type="string">
      `pay_in` for payin methods.
    </ResponseField>

    <ResponseField name="is_refundable" type="boolean">
      Whether a completed payin using this method can be refunded.
    </ResponseField>

    <ResponseField name="is_cancellable" type="boolean">
      Whether a pending payin using this method can be cancelled.
    </ResponseField>

    <ResponseField name="require_contact" type="boolean">
      Whether you must create a contact record before initiating a payin.
    </ResponseField>

    <ResponseField name="require_kyc" type="boolean">
      Whether KYC verification is required for this method.
    </ResponseField>

    <ResponseField name="min_amount" type="number">
      Minimum transaction amount in the currency's base unit.
    </ResponseField>

    <ResponseField name="max_amount" type="number">
      Maximum transaction amount in the currency's base unit.
    </ResponseField>
  </Expandable>
</ResponseField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request GET \
    --url 'https://api.cleverhub.co/api/v2/payins/payin_method/params?payin_method_name=au_payid_npp_aud' \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY'
  ```

  ```json Response theme={null}
  {
    "record": {
      "currency": "AUD",
      "symbol": "$",
      "logo": "",
      "payin_method_name": "au_payid_npp_aud",
      "description": "New Payments Platform (NPP)",
      "payin_method_params": {
        "callback_url": "string:Mandatory callback URL"
      },
      "payment_type": "pay_in",
      "is_refundable": false,
      "is_cancellable": false,
      "require_contact": true,
      "require_kyc": false,
      "min_amount": 20,
      "max_amount": 10000
    }
  }
  ```
</CodeGroup>

***

## Create Payin

<Badge color="blue">POST</Badge> `/v2/payins`

Creates a new payin request. You must first call Get Payin Methods and Get Required Fields to gather the correct method code and required fields.

<Warning>
  For payin methods `jp_bank_jpy`, `ar_bank_ars`, and `kr_bank_va_kyc_krw`, payment credentials such as virtual account numbers are delivered asynchronously via webhook after the payin is created, not in the API response.
</Warning>

### Body parameters

<ParamField body="payin_method_name" type="string" required>
  The method identifier returned by Get Payin Methods (e.g., `my_bank_duitnow_myr`).
</ParamField>

<ParamField body="payin_method_params" type="object" required>
  Method-specific parameters. Call Get Payin Required Fields for the exact keys this method expects. They differ per method, and some are mandatory. Send `{}` for methods that require none.

  <Expandable title="properties">
    <ParamField body="transliterate" type="boolean" default="false">
      Converts the customer's name into the bank's required script before submitting. Supported only for `jp_bank_jpy`, which converts `first_name` / `last_name` into full-width Katakana. Input must be romaji using Latin letters only; invalid input returns a validation error. Ignored by other methods.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="amount" type="number" required>
  The payment amount in the currency's base unit. For `VND`, `JPY`, `XAF`, `KRW`, and `XOF`, this value is rounded to the precision the currency supports. See [amount rounding](/api/v2/introduction#amount-rounding).
</ParamField>

<ParamField body="currency" type="string" required>
  ISO 4217 currency code (e.g., `MYR`, `VND`, `AUD`).
</ParamField>

<ParamField body="name" type="string" required>
  Customer name.
</ParamField>

<ParamField body="email" type="string" required>
  Customer email address. If the method reports `require_contact: true`, this must match the email used to create the contact.
</ParamField>

<ParamField body="description" type="string" required>
  A description for this payment.
</ParamField>

<ParamField body="gst" type="boolean" required>
  Include GST in the transaction. Set to `true` only for AUD transactions by AU merchants.
</ParamField>

<ParamField body="webhook_notification" type="object" required>
  Webhook configuration for this payin.

  <Expandable title="properties">
    <ParamField body="endpoint_url" type="string" required>
      Your webhook endpoint URL. Must be TLS 1.2 with a certificate from a well-known commercial authority. Self-signed and internally signed certificates are rejected.
    </ParamField>

    <ParamField body="authorization_header" type="string" required>
      Authorization header value sent with each webhook call. Use a unique value per payment for security.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="external_id" type="string">
  Your own identifier for this transaction. Returned on the payin and usable as a filter on Get Payins in a Period.
</ParamField>

<ParamField body="expired_at" type="string">
  Expiry timestamp in `YYYY-MM-DDTHH:mm:ss` format. Applies only to `vn_vietqr_vnd`; every other method uses a fixed default that cannot be overridden. Must be at least 15 minutes in the future.
</ParamField>

<ParamField body="metadata" type="object">
  Optional custom key-value data to attach to the transaction. Returned unchanged on the payin and in webhook payloads.
</ParamField>

<Note>
  There is no `sender_info`, `expire_in`, or `redirect_url` in v2. Those belong to Payment Gateway 3. Send the customer as top-level `name` and `email`, and use `expired_at` for expiry.
</Note>

### Response fields

<ResponseField name="uuid" type="string">
  Unique identifier for this payin transaction.
</ResponseField>

<ResponseField name="status" type="string">
  Current status: `pending`, `waiting`, `received`, `expired`, `return_pending`, `return_received`, `return_expired`, or `return_rejected`. See [payment statuses](/api/webhooks#payment-statuses) for the full lifecycle.
</ResponseField>

<ResponseField name="stage" type="string">
  Where the payment sits in its authorisation flow: `normal_stage`, `authorize_otp`, or `authorize_stk`.
</ResponseField>

<ResponseField name="pay_code" type="object">
  The details the customer uses to complete payment. Contents vary by method: `payment_url` for hosted flows, or bank fields such as `account_name`, `account_number`, and `bank_name` for bank transfers. For `jp_bank_jpy`, `ar_bank_ars`, and `kr_bank_va_kyc_krw` this arrives by webhook rather than in the response.
</ResponseField>

<ResponseField name="payment_method" type="string">
  The `payin_method_name` used for this transaction.
</ResponseField>

<ResponseField name="amount" type="number">
  Transaction amount, alongside `gst_amount`, `total`, and `paid_amount`.
</ResponseField>

<ResponseField name="is_refundable" type="boolean">
  Whether this payin can be refunded.
</ResponseField>

<ResponseField name="expired_at" type="string">
  Timestamp when the payin expires. `pay_by` returns the deadline for the customer to pay.
</ResponseField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request POST \
    --url https://api.cleverhub.co/api/v2/payins \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
      "currency": "MYR",
      "amount": 10000,
      "name": "Jane Smith",
      "email": "customer@example.com",
      "gst": false,
      "external_id": "123456",
      "description": "Invoice #1234",
      "payin_method_name": "my_bank_duitnow_myr",
      "payin_method_params": {},
      "webhook_notification": {
        "endpoint_url": "https://yoursite.com/webhooks/payin",
        "authorization_header": "Bearer abc123xyz"
      },
      "metadata": {
        "custom_note": "Priority customer"
      }
    }'
  ```

  ```json Response theme={null}
  {
    "uuid": "Q3NT0K80",
    "name": "Jane Smith",
    "description": "Invoice #1234",
    "email": "customer@example.com",
    "external_id": "123456",
    "status": "pending",
    "stage": "normal_stage",
    "pay_code": {
      "payment_url": "https://paylink.cleverhub.co/UTNOVDBLODA"
    },
    "currency": "MYR",
    "gst": false,
    "amount": "10000.0",
    "gst_amount": "0",
    "total": "10000.0",
    "paid_amount": "0",
    "payment_method": "my_bank_duitnow_myr",
    "is_refundable": "true",
    "expired_at": "2025-09-11T16:30:02.712Z",
    "pay_by": "2025-09-11T16:30:02.712Z"
  }
  ```
</CodeGroup>

<Note>
  Webhooks are called on a best-effort basis. If your endpoint does not return HTTP 200, Hello Clever retries 3 times with 15-minute delays between attempts. Design your webhook handler to be idempotent.
</Note>

***

## Get Payin Details

<Badge color="green">GET</Badge> `/v2/payins/detail`

Returns the full details of a specific payin request by UUID.

### Query parameters

<ParamField query="uuid" type="string" required>
  The unique identifier of the payin transaction.
</ParamField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request GET \
    --url 'https://api.cleverhub.co/api/v2/payins/detail?uuid=Q3NT0K80' \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY'
  ```

  ```json Response theme={null}
  {
    "uuid": "Q3NT0K80",
    "status": "received",
    "amount": 10000,
    "currency": "MYR",
    "payin_method_name": "my_bank_duitnow_myr",
    "created_at": "2025-09-11T10:30:02.000Z",
    "expired_at": "2025-09-11T16:30:02.712Z",
    "is_refundable": true,
    "is_cancellable": false
  }
  ```
</CodeGroup>

***

## Get Payins in a Period

<Badge color="green">GET</Badge> `/v2/payins/all`

Returns a paginated list of payin transactions linked to your `app-id`, filtered by date range and optional status. Returns 20 records per page by default.

### Query parameters

<ParamField query="from_date" type="string" required>
  Start of the date range in ISO 8601 format (e.g., `2025-01-01T00:00:00Z`).
</ParamField>

<ParamField query="to_date" type="string" required>
  End of the date range in ISO 8601 format.
</ParamField>

<ParamField query="external_id" type="string">
  Filter to the payin matching your own identifier.
</ParamField>

<ParamField query="page" type="number" default="1">
  Page number for pagination.
</ParamField>

<ParamField query="per_page" type="number" default="20">
  Number of records per page.
</ParamField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request GET \
    --url 'https://api.cleverhub.co/api/v2/payins/all?from_date=2025-01-01T00:00:00Z&to_date=2025-01-31T23:59:59Z&page=1&per_page=20' \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY'
  ```

  ```json Response theme={null}
  {
    "total": 45,
    "page": 1,
    "limit": 20,
    "records": [
      {
        "uuid": "Q3NT0K80",
        "status": "received",
        "amount": 10000,
        "currency": "MYR",
        "created_at": "2025-01-15T10:30:00.000Z"
      }
    ]
  }
  ```
</CodeGroup>

***

## Cancel Payin

<Badge color="blue">POST</Badge> `/v2/payins/cancel`

Cancels a `pending` payin transaction. The status transitions to `expired`. Check `is_cancellable == true` on the payin method before calling this endpoint.

### Body parameters

<ParamField body="uuid" type="string" required>
  The UUID of the pending payin to cancel.
</ParamField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request POST \
    --url https://api.cleverhub.co/api/v2/payins/cancel \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY' \
    --header 'Content-Type: application/json' \
    --data '{ "uuid": "Q3NT0K80" }'
  ```

  ```json Response theme={null}
  {
    "uuid": "Q3NT0K80",
    "status": "expired",
    "message": "Payin cancelled successfully"
  }
  ```
</CodeGroup>

***

## Refund Payin

<Badge color="blue">POST</Badge> `/v2/payins/refund`

Initiates a refund on a completed (`received`) payin. Check `is_refundable == true` on the transaction before calling this endpoint.

<Warning>
  If you initiate a refund immediately after a successful payin, the refund must be for the full amount. Partial refunds are only supported 30 minutes to 1 hour after the payin completes.
</Warning>

### Body parameters

<ParamField body="uuid" type="string" required>
  The UUID of the received payin to refund.
</ParamField>

<ParamField body="description" type="string" required>
  Reason for the refund. Must be at least 5 characters.
</ParamField>

<ParamField body="amount" type="number">
  The amount to refund. If omitted, the full amount is refunded. Partial refunds are only allowed 30–60 minutes after payin completion.
</ParamField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request POST \
    --url https://api.cleverhub.co/api/v2/payins/refund \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
      "uuid": "Q3NT0K80",
      "amount": 5000,
      "description": "Partial refund requested by customer"
    }'
  ```

  ```json Response theme={null}
  {
    "uuid": "Q3NT0K80",
    "refund_status": "return_pending",
    "refund_amount": 5000,
    "message": "Refund initiated"
  }
  ```
</CodeGroup>

***

## Payin Simulation (sandbox only)

<Badge color="blue">POST</Badge> `/v2/payins/simulate`

Simulates the outcome of a pending payin in the sandbox environment. The payin must be in `pending` status. This endpoint is not available in production.

### Body parameters

<ParamField body="uuid" type="string" required>
  The UUID of the pending payin to simulate.
</ParamField>

<ParamField body="status" type="string" required>
  The outcome to simulate: `completed` or `failed`.
</ParamField>

<ParamField body="amount" type="number">
  Amount to simulate as paid. Defaults to the full payin amount.
</ParamField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request POST \
    --url https://api.cleverhub.co/api/v2/payins/simulate \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY' \
    --header 'Content-Type: application/json' \
    --data '{ "uuid": "Q3NT0K80", "status": "completed" }'
  ```

  ```json Response theme={null}
  {
    "uuid": "Q3NT0K80",
    "status": "received",
    "message": "Payin simulation successful"
  }
  ```
</CodeGroup>

***

## Authorize OTP Transaction

<Badge color="blue">POST</Badge> `/v2/payins/confirm_otp`

After creating a payin using a mobile money method (e.g., `gh_mobile_money_ghs`), if the response `stage` is `authorize_otp`, an OTP has been sent to the customer's phone. Collect the OTP from the customer and submit it to this endpoint. On success, an STK push is sent to the customer's phone to confirm the payment with their PIN.

### Body parameters

<ParamField body="uuid" type="string" required>
  The UUID of the payin awaiting OTP authorization.
</ParamField>

<ParamField body="code" type="string" required>
  The OTP received on the customer's phone.
</ParamField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request POST \
    --url https://api.cleverhub.co/api/v2/payins/confirm_otp \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY' \
    --header 'Content-Type: application/json' \
    --data '{ "uuid": "Q3NT0K80", "code": "123456" }'
  ```

  ```json Response theme={null}
  {
    "uuid": "Q3NT0K80",
    "status": "pending",
    "message": "OTP verified. STK push sent to customer."
  }
  ```
</CodeGroup>

***

## Resend OTP

<Badge color="blue">POST</Badge> `/v2/payins/resend_otp`

Resends the OTP for a mobile money payin when the original OTP has expired or was not received by the customer.

### Body parameters

<ParamField body="uuid" type="string" required>
  The UUID of the payin for which to resend the OTP.
</ParamField>

<CodeGroup>
  ```bash Request theme={null}
  curl --request POST \
    --url https://api.cleverhub.co/api/v2/payins/resend_otp \
    --header 'app-id: YOUR_APP_ID' \
    --header 'secret-key: YOUR_SECRET_KEY' \
    --header 'Content-Type: application/json' \
    --data '{ "uuid": "Q3NT0K80" }'
  ```

  ```json Response theme={null}
  {
    "uuid": "Q3NT0K80",
    "message": "OTP resent successfully"
  }
  ```
</CodeGroup>


## Related topics

- [Multi-Currency Payment API](/api/v2/introduction.md)
- [Handle Multi-Currency Payins and Payouts](/developer-reference/api-use-cases/handle-multi-currency-payins-and-payouts.md)
- [Multi-Currency Payout API Reference](/api/v2/payout.md)
