> 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/dotnet/guides/accept-payments.md).

# Accept crypto payments

Accept incoming crypto payments in .NET / C# 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

```csharp
using CryptoChief.Processing.Chains;
using CryptoChief.Processing.Models;

var invoice = await client.PayIns.CreateAsync(new CreatePayInRequest
{
    OrderId      = "invoice-1001",
    UserId       = "u-7",
    Mode         = PayInMode.Crypto,
    AmountCrypto = "10.0",
    Asset        = new Asset { Coin = "USDT", Network = Chain.TronMainnet },
    UrlCallback  = "https://your.app/webhooks/payin",
});

Console.WriteLine($"pay to: {invoice.ToAddress}");
Console.WriteLine($"payment link: {invoice.PaymentLink}");
```

## Fiat mode

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

```csharp
var invoice = await client.PayIns.CreateAsync(new 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:

```csharp
var paid = await client.PayIns.SelectAssetAsync(new SelectAssetRequest
{
    Uuid    = invoice.Uuid,
    Coin    = "USDT",
    Network = Chain.TronMainnet,
});
Console.WriteLine($"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 %}

### Which currencies can you price in?

`Currency` above takes an ISO 4217 code, and `client.Currencies` lists the ones the platform actually has rates for. Both lists are platform-wide, so neither call takes anything but a `CancellationToken`:

```csharp
var fiats = await client.Currencies.FiatsAsync();
foreach (var f in fiats)
    Console.WriteLine($"{f.Code} — {f.Name}");   // SEK — Swedish Krona

var cryptos = await client.Currencies.CryptosAsync();
Console.WriteLine($"{cryptos.Count} tickers quoted against {cryptos.Quote}");  // … against USDT
```

`FiatsAsync` answers a **bare JSON array** of `FiatCurrency`, which is why it returns `IReadOnlyList<FiatCurrency>` rather than a response record with `Items`. Those codes are what `Currency` accepts on a fiat-mode order, and what the fiat side of a rate quote (`FiatToCryptoAsync` / `CryptoToFiatAsync`) accepts. Populate a currency dropdown from it instead of shipping a hard-coded list that drifts.

`CryptosAsync` answers an object: `Tickers` is every ticker deduplicated, `ByExchange` maps each exchange to the tickers it carries, `Count` is the size of `Tickers`, and `Quote` is what the rates are quoted against.

```csharp
if (cryptos.ByExchange.TryGetValue("binance", out var binance))
    Console.WriteLine($"binance carries {binance.Count} of them");
```

Both endpoints spell an empty answer `null` rather than `[]` on the wire — the whole body, `tickers`, or one exchange's list inside `by_exchange`. The SDK turns every one of those into an empty collection, so `fiats`, `cryptos.Tickers`, `cryptos.ByExchange` and each list inside it are always safe to enumerate and never null.

{% hint style="warning" %}
**A ticker with a rate is not an asset you can be paid in.** `CryptosAsync` is rate availability only — it says the platform can price something, not that it takes deposits, sweeps or payouts in it. The list that governs orders is `client.Blockchain.ContractsAvailableAsync()`; see [Ask the platform instead of the table](/processing/dotnet/concepts/chains.md#ask-the-platform-instead-of-the-table).
{% endhint %}

## Mainnet or testnet

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

```csharp
var inv = await client.PayIns.CreateAsync(new CreatePayInRequest
{
    OrderId = "invoice-1003",
    UserId = "user-1",
    Mode = PayInMode.Fiat,
    AmountFiat = "49.99",
    Currency = "USD",
    Environment = PayInEnvironment.Testnet,
});
```

The constants live in `PayInEnvironment`, not `Environment`: this namespace is imported wholesale, and a type called `Environment` would collide with `System.Environment` in every file that does so.

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" %}
The same request accepts `MasterWalletAddress`, which 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.
{% endhint %}

## Track the order

```csharp
var order = await client.PayIns.InfoAsync(invoice.Uuid);
if (order.Succeeded)        // status == "paid"
{
    // fulfill the order
}
```

You can also `CancelAsync`, `ResetAssetAsync` (revert to asset selection), and page through `HistoryAsync`. Prefer reacting to the `invoice.*` [webhook](/processing/dotnet/guides/webhooks.md) over polling.

## Block until terminal

If you need a synchronous flow, the SDK ships a polling extension:

```csharp
using CryptoChief.Processing.Polling;

var final = await client.WaitForPayInAsync(invoice.Uuid, new PollOptions
{
    Interval = TimeSpan.FromSeconds(10),
    Timeout  = TimeSpan.FromMinutes(30),
});
```

## Order lifecycle

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