> For the complete documentation index, see [llms.txt](https://docs-sdk.crypto-chief.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-sdk.crypto-chief.com/processing/kotlin/guides/accept-payments.md).

# Accept crypto payments

Accept incoming crypto payments in Kotlin by creating PayIn orders (invoices).

A **PayIn** is an incoming-payment order (an invoice). Create one, show the customer the deposit address or payment link, and receive a webhook when it's paid.

There are two modes:

* **`crypto`** — fix the exact coin, network, and amount upfront.
* **`fiat`** — price the order in fiat and let the customer pick the asset at payment time.

## Crypto mode

```kotlin
import com.cryptochief.processing.Asset
import com.cryptochief.processing.Chain
import com.cryptochief.processing.models.CreatePayInRequest
import com.cryptochief.processing.models.PayInMode

val invoice = client.payIns.create(
    CreatePayInRequest(
        orderId      = "invoice-1001",
        userId       = "u-7",
        mode         = PayInMode.CRYPTO,
        amountCrypto = "10.0",
        asset        = Asset(coin = "USDT", network = Chain.TRON_MAINNET),
        urlCallback  = "https://your.app/webhooks/payin",
    ),
)

println("pay to: ${invoice.toAddress}")
println("payment link: ${invoice.paymentLink}")
```

## Fiat mode

Price in fiat; the customer chooses the coin/network when they pay.

```kotlin
val invoice = client.payIns.create(
    CreatePayInRequest(
        orderId     = "invoice-1002",
        userId      = "u-7",
        mode        = PayInMode.FIAT,
        amountFiat  = "49.99",
        currency    = "USD",
        urlCallback = "https://your.app/webhooks/payin",
    ),
)
// invoice.status == "waiting_asset_select"
// invoice.coins lists the offered options
```

When the customer picks an asset, commit it to get the address and final crypto amount:

```kotlin
import com.cryptochief.processing.models.SelectAssetRequest

val paid = client.payIns.selectAsset(
    SelectAssetRequest(
        uuid    = invoice.uuid,
        coin    = "USDT",
        network = Chain.TRON_MAINNET,
    ),
)
println("pay to: ${paid.toAddress} ${paid.amountCrypto} ${paid.paymentCoin}")
```

{% hint style="info" %}
Restrict which coins are offered in fiat mode with the `assets` allow/exclude policy. Use `accuracyPaymentPercent` to tolerate small under/over-payments and `lifetimeSec` to set an expiry.
{% endhint %}

`currency` takes any code `client.currencies.fiats()` lists, so build the dropdown from that call rather than a hard-coded list — see [what the platform can put a price on](/processing/kotlin/concepts/chains.md#what-the-platform-can-put-a-price-on). The companion call, `client.currencies.cryptos()`, lists what the platform has a **rate** for and not what you can be paid in; the coins offered here come from [`client.blockchain.contractsAvailable()`](/processing/kotlin/concepts/chains.md#assets-platform-wide-and-project-wide).

## Mainnet or testnet

An order belongs to one environment: the real chains, or the test ones. Set `environment` to `mainnet` or `testnet` on create.

```kotlin
import com.cryptochief.processing.models.Environment

val inv = client.payIns.create(
    CreatePayInRequest(
        orderId     = "invoice-1003",
        userId      = "user-1",
        mode        = PayInMode.FIAT,
        environment = Environment.TESTNET,
        amountFiat  = "49.99",
        currency    = "USD",
    ),
)
```

It changes nothing when the request names a concrete network — that is your choice. It matters exactly where the **platform** picks the asset: fiat mode, and a network of `ANY`. Without it, an unconstrained pick could put a real payment on a test network.

Omit it and the project's own default applies. A project may be allowed one environment or both; asking for testnet on a project that does not permit it is refused with `TESTNET_NOT_ALLOWED` rather than quietly served on mainnet, and a value that is neither environment is `ENVIRONMENT_INVALID` rather than a silent fallback.

{% hint style="info" %}
`CreatePayInRequest.masterWalletAddress` pins the order's deposit wallet to one of your project's master wallets — the address these funds are ultimately swept to. The order's chain family must match the master wallet's. `SelectAssetRequest` carries the same field, and a value there overrides one given at create.

Left unset, the platform picks the project's oldest master of that chain family, which on a project shared by several merchants is somebody else's wallet — name it whenever the project has more than one. On memo/tag chains such as XRPL the order's deposit wallet is a shared tagged account rather than a fresh address, so the same master serves every order on it, and it cannot be re-pointed afterwards (`shared_transit_cannot_be_rebound`).
{% endhint %}

## Track the order

```kotlin
import com.cryptochief.processing.poll.waitForPayIn

val final = client.waitForPayIn(invoice.uuid)
if (final.succeeded) { // status == "paid"
    // fulfill the order
}
```

You can also `cancel`, `resetAsset` (revert to asset selection), and page through `history`. Prefer reacting to the `invoice.*` [webhook](/processing/kotlin/guides/webhooks.md) over polling.

When what you have is a deposit address rather than an order — a payer says they sent funds and quotes where — `client.wallets.history()` lists the pay-ins that address served, in this same `PayInHistoryResponse` shape. See [Wallets](/processing/kotlin/guides/wallets.md#every-pay-in-that-used-one-address).

## Order lifecycle

`waiting_asset_select` → `pending` → `processing` → **`paid`** (terminal). Terminal failures are `cancel` and `expired`. Check with `order.isTerminal` / `order.succeeded`.
