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

# Orders: DEPOSIT, SALE, REFUND modes and statuses

> Understand the CoinVoyage order object at the center of every payment, including modes, statuses, and metadata.

An order is the core unit of work in CoinVoyage. Every payment, whether a user deposits funds to a wallet, a merchant collects a sale, or a merchant issues a refund, is represented as an order. You create one before showing the payment UI, and CoinVoyage updates it as payment moves through detection, confirmation, execution, and settlement.

## Order modes

CoinVoyage exposes `SALE` and `DEPOSIT` as direct create modes. `REFUND`, `SWAP`, and `FEE_CLAIM` are created through dedicated endpoints or platform workflows.

<Tabs>
  <Tab title="DEPOSIT">
    A `DEPOSIT` order moves funds directly to a wallet address you specify on a target chain. Use this when a user is topping up a wallet, funding an account, or making a transfer where you control the destination address.

    ```typescript theme={null}
    import { ApiClient, ChainId } from "@coin-voyage/paykit/server";

    const apiClient = ApiClient({
      apiKey: process.env.COIN_VOYAGE_API_KEY!,
    });

    const { data, error } = await apiClient.createDepositOrder({
      amount: "10",
      currency: {
        chain_id: ChainId.SUI,
        address: null,
      },
      recipient: "0xYourReceivingAddressHere",
      metadata: {
        items: [{ name: "Wallet top-up" }],
      },
    });
    ```

    <Note>
      `DEPOSIT` orders do not require an API secret for authorization. You can create them with only your API key.
    </Note>
  </Tab>

  <Tab title="SALE">
    A `SALE` order represents a merchant collecting payment for goods or services. Settlement goes to your organization's configured settlement currency, or to a specific asset and recipient if you provide `currency` and `recipient`.

    `SALE` orders require an API secret and must be created server-side.

    ```typescript theme={null}
    const { data, error } = await apiClient.createSaleOrder(
      {
        amount: "200",
        fiat_unit: "USD",
        metadata: {
          items: [
            {
              name: "t-shirt",
              description: "A nice t-shirt",
              image: "https://example.com/tshirt.jpg",
              quantity: 1,
              unit_price: 200,
              currency: "USD",
            },
          ],
        },
      },
      process.env.COIN_VOYAGE_API_SECRET!
    );
    ```

    If this specific order should settle to a particular on-chain asset instead of your dashboard default, include `currency` and `recipient`:

    ```typescript theme={null}
    const { data, error } = await apiClient.createSaleOrder(
      {
        amount: "570.52",
        fiat_unit: "USD",
        currency: {
          chain_id: ChainId.SOL,
          address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        },
        recipient: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
      },
      process.env.COIN_VOYAGE_API_SECRET!
    );
    ```

    <Warning>
      Never call `createSaleOrder` from the browser. The method signs the request with your API secret, which must remain confidential and server-side only.
    </Warning>
  </Tab>

  <Tab title="REFUND">
    A `REFUND` order sends funds back to a user for a previous payment. You reference the original order ID and specify the refund amount, currency, and recipient.

    ```typescript theme={null}
    const { data: refundOrder, error } = await apiClient.createRefundOrder(
      "original-order-id",
      {
        amount: "100",
        fiat_unit: "USD",
        currency: {
          chain_id: ChainId.ETH,
          address: null,
        },
        recipient: "0xRefundAddress",
        metadata: {
          refund: {
            reason: "Item out of stock",
            refund_amount: 100,
            currency: "USD",
          },
        },
      },
      process.env.COIN_VOYAGE_API_SECRET!
    );
    ```
  </Tab>
</Tabs>

## Order statuses

Your webhook handler and polling logic should account for each lifecycle state.

| Status                  | Description                                                                                                                                                           |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PENDING`               | The order has been created but is not yet ready for payment.                                                                                                          |
| `AWAITING_PAYMENT`      | The order is ready. CoinVoyage is waiting for the user to send funds.                                                                                                 |
| `AWAITING_CONFIRMATION` | A payment transaction has been detected on-chain and is pending confirmation.                                                                                         |
| `OPTIMISTIC_CONFIRMED`  | The transaction is optimistically confirmed; execution can begin before full finality.                                                                                |
| `EXECUTING_ORDER`       | Payment is confirmed and CoinVoyage is routing funds through the provider chain.                                                                                      |
| `COMPLETED`             | Funds have arrived at the destination. The order is complete.                                                                                                         |
| `EXPIRED`               | The payment window elapsed before the user sent funds.                                                                                                                |
| `REFUNDED`              | Execution failed or a refund was issued and funds were returned to the refund address.                                                                                |
| `FAILED`                | The order encountered an unrecoverable error during processing.                                                                                                       |
| `PARTIAL_PAYMENT`       | The user sent less than the required amount. Pay to Address can accept additional deposits until the order is fully funded; other payment methods require resolution. |

<Info>
  Webhook subscription event types and delivered payload values use uppercase `ORDER_*` identifiers, such as `ORDER_COMPLETED`.
</Info>

The typical happy-path progression is:

```text theme={null}
PENDING -> AWAITING_PAYMENT -> AWAITING_CONFIRMATION -> OPTIMISTIC_CONFIRMED -> EXECUTING_ORDER -> COMPLETED
```

Failure paths branch to `EXPIRED`, `REFUNDED`, `FAILED`, or `PARTIAL_PAYMENT` depending on where the problem occurs.

### Continue a Pay to Address partial payment

For Pay to Address, `PARTIAL_PAYMENT` is recoverable. Keep fulfillment paused and use `order.payment.funding.remaining_amount` to show the amount still due. Additional deposits are accumulated in `order.payment.funding.transactions`; fulfill only after the order reaches `COMPLETED`.

<Warning>
  This behavior applies only to Pay to Address. Wallet, card, and other payment methods do not support additional deposits after `PARTIAL_PAYMENT`.
</Warning>

## Order metadata

You can attach structured metadata when you create an order. Metadata is visible in the dashboard and included in webhook payloads, making it useful for reconciliation, analytics, and displaying order context to the user.

```typescript theme={null}
type OrderMetadata = {
  items?: Array<{
    name: string
    description?: string
    image?: string
    quantity?: number
    unit_price?: number
    currency?: string
  }>
  refund?: {
    name?: string
    reason?: string
    additional_info?: string
    refund_amount?: number
    currency?: string
  }
  [key: string]: unknown
}
```

### Items

Use the `items` array to describe what the user is paying for. Each item can include a name, description, image URL, quantity, and unit price.

```typescript theme={null}
metadata: {
  items: [
    {
      name: "Annual subscription",
      description: "Pro plan - 12 months",
      quantity: 1,
      unit_price: 199,
      currency: "USD",
    },
  ],
}
```

### Refund details

For refunds, populate the `refund` object to record the reason and amount being refunded:

```typescript theme={null}
metadata: {
  refund: {
    reason: "Item damaged in shipping",
    refund_amount: 49.99,
    currency: "USD",
  },
}
```

### Custom fields

You can add custom top-level fields to carry your own data, such as customer IDs, order references, or campaign tags.

```typescript theme={null}
metadata: {
  items: [{ name: "Premium plan" }],
  customer_id: "cust_12345",
  order_reference: "ORD-2026-001",
  campaign: "summer_sale",
}
```

<Tip>
  Use stable internal IDs in metadata so webhook handlers can reconcile CoinVoyage orders back to your own records without an extra lookup.
</Tip>
