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

# Accept crypto payments

Accept incoming crypto payments in Node.js 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

```ts
import { CryptoChiefClient, Chain, PayInMode } from '@cryptochiefs/cryptochief-crypto-processing-node';

const inv = await client.payIns.create({
  orderId: 'invoice-1001',
  userId: 'u-7',
  mode: PayInMode.Crypto,
  amountCrypto: '10.0',
  asset: { coin: 'USDT', network: Chain.TronMainnet },
  urlCallback: 'https://your.app/webhooks/payin',
});

console.log('pay to:', inv.toAddress);
console.log('payment link:', inv.paymentLink);
```

## Fiat mode

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

```ts
const inv = await client.payIns.create({
  orderId: 'invoice-1002',
  userId: 'u-7',
  mode: PayInMode.Fiat,
  amountFiat: '49.99',
  currency: 'USD',
  urlCallback: 'https://your.app/webhooks/payin',
});
// inv.status === 'waiting_asset_select'; inv.coins lists the offered options.
```

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

```ts
const paid = await client.payIns.selectAsset({
  uuid: inv.uuid,
  coin: 'USDT',
  network: Chain.TronMainnet,
});
console.log('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 %}

## Mainnet or testnet

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

```ts
import { Environment } from '@cryptochiefs/cryptochief-crypto-processing-node';

const inv = await client.payIns.create({
  orderId: 'invoice-1003',
  userId: 'user-1',
  mode: PayInMode.Fiat,
  amountFiat: '49.99',
  currency: 'USD',
  environment: Environment.Testnet,
});
```

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.

## Pin the deposit wallet to a master

Both `create` and `selectAsset` accept `masterWalletAddress`, which pins this order's transit deposit wallet to one of your project's master wallets — the address these funds are ultimately swept to.

```ts
const paid = await client.payIns.selectAsset({
  uuid: inv.uuid,
  coin: 'USDT',
  network: Chain.TronMainnet,
  masterWalletAddress: masterAddress,
});
```

The order's chain family must match the master wallet's; a foreign or mismatched address is refused with `400`. A value on `selectAsset` overrides one given at create.

{% hint style="warning" %}
Leaving it out is not neutral. Without it the platform falls back to the **oldest** master of that chain family — on a project shared by several merchants, somebody else's wallet — and the order's funds settle there. Name it whenever the project has more than one master. On memo/tag chains such as XRPL a whole family of orders shares one tagged deposit account, so the master is decided for the family, not per order.
{% endhint %}

## Track the order

```ts
const order = await client.payIns.info(inv.uuid);
if (order.status === 'paid') {
  // fulfill the order
}
```

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

## Order lifecycle

`waiting_asset_select` → `pending` → `processing` → **`paid`** (terminal). Terminal failures are `cancel` and `expired`.
