> For the complete documentation index, see [llms.txt](https://docs.idrx.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.idrx.co/integration/processing-mint-idrx-requests.md).

# Processing Mint IDRX Requests

Using a user account's API key, you can process mint requests for the specified user. To do so, you can use the `POST /api/transaction/mint-request` endpoint. Here is an example implementation.

{% hint style="info" %}
Check [this page](https://docs.idrx.co/introduction/supported-chain-and-contract-address) to see the list of chains where IDRX is available&#x20;
{% endhint %}

{% hint style="info" %}

* The balance will be processed and credited to \`**`` destinationWalletAddress` ``** max 24 hours after your request is submitted.
* Minimum transaction for IDRX is Rp20,000 IDR
* Maximum transaction for IDRX is 1,000,000,000 IDR.
* For transactions more than the maximum limit, please send your request to <support@idrx.co> to be processed.
* The transaction will be automatically canceled if you have not made a payment within 24 hours.
* IDRX is not responsible for deposit errors from incorrect Virtual Account numbers. Refunds for payments made to Virtual Account numbers under a different name will be processed within 14 business days.
  {% endhint %}

### Overview

A mint request flows through two independent state machines:

1. **Payment** — tracked by `paymentStatus`. Reflects the state of the fiat payment (VA, QRIS, or hosted checkout).
2. **Minting** — tracked by `userMintStatus`. Reflects the on-chain delivery of IDRX (or other token) to the destination wallet.

Both fields are returned by the [Transaction History API](/api/transaction-api/get-api-transaction-user-transaction-history.md) and must be tracked together to determine the final outcome.

### Step 1 — Create the Mint Request

Use your API key to call:

```http
POST /api/transaction/mint-request
```

#### Example Implementation

```typescript
import { createSignature } from "./createSignature";
const axios = require("axios");

const apiKey = "{YOUR API KEY}";
const secret = "{YOUR SECRET}";

async function mintRequest() {
  const path = "https://idrx.co/api/transaction/mint-request";

  const req = {
    toBeMinted: "51500",
    destinationWalletAddress: "0x8BD53F7fF88fD895D3686fe6369a07432822d30F",
    expiryPeriod: 60,
    networkChainId: "137",
    returnUrl: "https://your-app.example.com/callback",
    requestType: "idrx"
  };

  const bufferReq = Buffer
    .from(JSON.stringify(req), "base64")
    .toString("utf8");

  const timestamp = Date.now().toString();

  const sig = createSignature(
    "POST",
    path,
    bufferReq,
    timestamp,
    secret
  );

  const res = await axios.post(path, req, {
    headers: {
      "Content-Type": "application/json",
      "idrx-api-key": apiKey,
      "idrx-api-sig": sig,
      "idrx-api-ts": timestamp,
      "User-Agent": "my-app/1.0"
    }
  });

  console.log(res.data);

  return res.data.data.merchantOrderId;
}
```

#### Example Response

```json
{
  "statusCode": 200,
  "message": "success",
  "data": {
    "merchantCode": "D11808",
    "reference": "D11808T8GVTL81VV2U7HV",
    "paymentUrl": "https://app-prod.duitku.com/redirect_checkout?reference=D11808T8GVTL81VV2U7HV",
    "amount": "54500.00",
    "statusCode": "00",
    "statusMessage": "SUCCESS",
    "merchantOrderId": "20231219101707"
  }
}
```

{% hint style="info" %}
`amount` is the final amount paid by the user after fees.\
Store `merchantOrderId` in your database — it is required for status tracking and reconciliation.
{% endhint %}

The user then completes payment using `paymentUrl`.

For mobile or white-label integrations that skip the hosted checkout page, see:

* `Flow B — Direct Payment` : [Link](/api/transaction-api/post-api-transaction-mint-request.md#flow-b-direct-payment)

***

### Step 2 — Track the Status

Query Transaction History using `merchantOrderId`.

#### Example Request

```typescript
import { createSignature } from "./createSignature";
const axios = require("axios");

const apiKey = "{YOUR API KEY}";
const secret = "{YOUR SECRET}";

async function getMintStatus(merchantOrderId: string) {
  const path =
    `https://idrx.co/api/transaction/user-transaction-history` +
    `?transactionType=MINT` +
    `&merchantOrderId=${merchantOrderId}` +
    `&page=1&take=1`;

  const bufferReq = Buffer
    .from("", "base64")
    .toString("utf8");

  const timestamp = Date.now().toString();

  const sig = createSignature(
    "GET",
    path,
    bufferReq,
    timestamp,
    secret
  );

  const res = await axios.get(path, {
    headers: {
      "Content-Type": "application/json",
      "idrx-api-key": apiKey,
      "idrx-api-sig": sig,
      "idrx-api-ts": timestamp,
      "User-Agent": "my-app/1.0"
    }
  });

  return res.data.records[0];
}
```

#### Example Response

```json
{
  "statusCode": 200,
  "message": "success",
  "metadata": {
    "page": 1,
    "perPage": 1,
    "pageCount": 1,
    "totalCount": 1
  },
  "records": [
    {
      "id": 311,
      "paymentAmount": 54500,
      "merchantOrderId": "20231219101707",
      "productDetails": "Minting IDRX",
      "customerVaName": "JOHN SMITH",
      "destinationWalletAddress": "0x8BD53F7fF88fD895D3686fe6369a07432822d30F",
      "toBeMinted": "51500",
      "chainId": 137,
      "createdAt": "2023-12-19T10:17:07.903Z",
      "paymentStatus": "WAITING_FOR_PAYMENT",
      "userMintStatus": "NOT_AVAILABLE",
      "adminMintStatus": "REQUESTED",
      "expiryTimestamp": "1703197027900",
      "reference": "D11808T8GVTL81VV2U7HV",
      "txHash": null,
      "requestType": "",
      "refundStatus": null
    }
  ]
}
```

The two fields that matter for integrations are:

| Field            | Purpose                   |
| ---------------- | ------------------------- |
| `paymentStatus`  | Fiat payment status       |
| `userMintStatus` | Blockchain minting status |

{% hint style="warning" %}
`adminMintStatus` is an internal operational field and is not part of the public API contract.\
Do not build integration logic around it.
{% endhint %}

***

### paymentStatus Values

| Value                 | Meaning                                            |
| --------------------- | -------------------------------------------------- |
| `WAITING_FOR_PAYMENT` | Mint request created and awaiting payment          |
| `PAID`                | Payment confirmed                                  |
| `EXPIRED`             | Payment window expired before payment was received |

***

### userMintStatus Values

| Value           | Meaning                                   |
| --------------- | ----------------------------------------- |
| `NOT_AVAILABLE` | No minting activity yet                   |
| `PROCESSING`    | Payment confirmed and minting in progress |
| `MINTED`        | Tokens successfully delivered             |
| `REJECTED`      | Request rejected after review             |
| `REFUND`        | Payment refunded                          |

{% hint style="info" %}
`userMintStatus` never becomes `FAILED`.\
Failed mint attempts are retried internally until resolved.
{% endhint %}

***

### Transaction Lifecycle

| Stage            | `paymentStatus`       | `userMintStatus` | Terminal |
| ---------------- | --------------------- | ---------------- | -------- |
| Awaiting payment | `WAITING_FOR_PAYMENT` | `NOT_AVAILABLE`  | No       |
| Paid, minting    | `PAID`                | `PROCESSING`     | No       |
| Success          | `PAID`                | `MINTED`         | Yes      |
| Payment expired  | `EXPIRED`             | `NOT_AVAILABLE`  | Yes      |
| Refunded         | `PAID`                | `REFUND`         | Yes      |
| Rejected         | `PAID`                | `REJECTED`       | Yes      |

#### Lifecycle Diagram

```
WAITING_FOR_PAYMENT / NOT_AVAILABLE
        |
        |-- customer pays --------> PAID / PROCESSING ---> PAID / MINTED
        |                                  |
        |                                  |---> PAID / REFUND
        |                                  |
        |                                  |---> PAID / REJECTED
        |
        |-- payment expires -----> EXPIRED / NOT_AVAILABLE
```

***

### Step 3 — Reconcile

There are two ways to know a transaction has reached its final state:

1. **Webhook callback** (recommended) — your backend receives an HTTP POST when the transaction settles. See [Webhook Callback.](/api/callback.md)
2. **Polling** — call the Transaction History endpoint from Step 2 until you reach a terminal state.

A reference reconciliation loop:

```typescript
async function reconcileMint(merchantOrderId: string) {
  const TERMINAL_PAYMENT = ["EXPIRED"];

  const TERMINAL_MINT = [
    "MINTED",
    "REFUND",
    "REJECTED"
  ];

  while (true) {
    const tx = await getMintStatus(merchantOrderId);

    if (TERMINAL_PAYMENT.includes(tx.paymentStatus)) {
      return {
        outcome: "EXPIRED",
        tx
      };
    }

    if (TERMINAL_MINT.includes(tx.userMintStatus)) {
      return {
        outcome: tx.userMintStatus,
        tx
      };
    }

    await new Promise(r => setTimeout(r, 5000));
  }
}
```

{% hint style="warning" %}
Always re-fetch transaction state from Transaction History before crediting users.\
Treat webhook payloads as untrusted input.
{% endhint %}

***

### Edge Case — Payment Succeeded but Mint Still Processing

If:

```
paymentStatus = PAID
userMintStatus = PROCESSING
```

persists longer than a few minutes, the blockchain delivery did not complete on the first attempt.

The transaction is still being handled internally by IDRX operations.

The final outcome will eventually become one of:

* `MINTED`
* `REFUND`

Do not timeout the transaction on your side.

Continue polling until a terminal state is reached.

***

### Edge Case — Customer Pays After Expiry

If a customer pays after `expiryPeriod` has elapsed, the bank network will usually reverse the payment automatically.

Late payments are not credited.

Create a new mint request if the customer wants to retry.

***

### Edge Case — Wrong Sender Name

For VA payments, the bank sender name must match the customer name registered on the IDRX account.

Mismatched payments are refunded within 14 business days.

***

### Recommended Polling Cadence

* Poll every `5 seconds` during the first 5 minutes
* Then back off to every `30 seconds`
* Stop polling after reaching a terminal state
* Prefer webhook callbacks for production systems

After a successful payment, IDRX tokens will be minted to the specified wallet address. To check the status of the transaction, you can use the [Transaction History API](/api/transaction-api/get-api-transaction-user-transaction-history.md). Next, we will explore on processing redeem requests.
