# Take payments (/docs/products/dash-dots/developers/payments)

Your dot doesn't build a checkout. It asks Dash for a payment, and Dash shows its own payment sheet. The person pays the way they already do in Dash: from a linked account, by card or with mobile money. The money goes to your business's [Space](/docs/products/dash-dots#how-it-works), which owns the dot.

## Declare payments

Add `payments` to `dot.json`'s permissions, with what people pay for:

```json title="dot.json"
{
  "permissions": { "payments": "Pay for ferry tickets" }
}
```

Monime's review reads the reason. Dash doesn't ask the person for this permission, because they confirm every payment in the sheet.

## Ask for a payment

Call `this.dash.payments.request` with the order. Amounts are in minor units, as in Monime's API, so SLE 75.00 is `7500`:

```ts title="pages/tickets/controller.ts"
// One of the page's actions
async pay(state) {
  const result = await this.dash.payments.request({
    order: {
      name: 'Lungi ferry tickets',
      reference: state.orderId,
      lineItems: [
        { name: 'Adult ticket', unitPrice: { currency: 'SLE', value: 7500 }, quantity: state.adults },
      ],
    },
  });
  if (result.status === 'completed') {
    this.navigation.replace('tickets/confirmed', { orderId: state.orderId });
  } else if (result.status === 'pending') {
    this.setState({ note: "We're waiting for your payment to go through." });
  } else {
    this.setState({ note: "The payment didn't go through. Try again." });
  }
},
```

Dash adds up the total. An order holds:

- A `name` of 3 to 64 characters.
- Your own `reference` for the order, up to 64 characters. Use a new one for each order. Your server's order ID works well. Asking again with the same reference gives back the first payment, and never pays twice.
- 1 to 16 `lineItems`, all in one currency, `SLE` or `USD`. Each has a `name`, a `unitPrice` and a `quantity` from 1 to 100,000. An `imageUrl` shows a picture beside the item. It must be `https`, on a host in [`network.hosts`](/docs/products/dash-dots/developers/network-and-files#allowed-hosts).
- An optional `description`.
- Up to 64 `metadata` pairs, which stay with the payment and its webhooks. The [Payments reference](/docs/products/dash-dots/reference/dash-api/payments) lists the rules for keys and values.

## How a payment ends

If something goes wrong before the person picks a way to pay, `request` rejects with one of these codes:

| Code        | What happened                                                |
| ----------- | ------------------------------------------------------------ |
| `cancelled` | The person closed the sheet                                  |
| `invalid`   | Monime won't take the order. The message says what to change |
| `denied`    | Your dot or Space can't take payments right now              |
| `failed`    | Dash couldn't reach Monime. Trying again may work            |
| `timeout`   | Dash stopped waiting before the person picked a way to pay   |

Monime rejects an order below its smallest amount as `invalid`. A dot that isn't [published](#test-payments) yet gets `denied`, as does a call [Dash held back](/docs/products/dash-dots/developers/dash-apis#when-dash-holds-a-call-back).

A second `request` while your dot's payment sheet is open also gets `denied`, and the open sheet carries on. To stop a double tap, set the `Button`'s `loading` while you wait for the result.

Once they pick one, it resolves with the payment's `status`:

| Status      | What it means                                                                    |
| ----------- | -------------------------------------------------------------------------------- |
| `completed` | The money is paid                                                                |
| `pending`   | Not [confirmed](#check-a-pending-payment) yet                                    |
| `expired`   | The person didn't finish in time                                                 |
| `cancelled` | The person closed the sheet after picking a way to pay                           |
| `failed`    | Only from [`get`](#check-a-pending-payment): a pending payment that failed later |

If a way to pay fails, the sheet stays open, and the person can try again or pick another.

`request` takes no `timeout`, as Dash decides how long the sheet waits. After the person picks a way to pay, `cancel()` only stops your dot waiting. The payment carries on.

## Check a pending payment

Read a payment again with `this.dash.payments.get(reference)`. You get `undefined` if your dot never asked for it, or the person never picked a way to pay. Check a pending one when the person comes back to the page:

```ts
hooks: {
  async onShow() {
    const payment = await this.dash.payments.get(this.state.orderId);
    if (payment?.status === 'completed') {
      this.setState({ paid: true, note: '' });
    }
  },
},
```

## Choose how people pay

Leave out `methods` to offer all three ways to pay. To offer fewer, or to set a method's options, list them in the order the sheet should show them:

```ts
await this.dash.payments.request({
  order,
  methods: [
    { type: 'momo', providers: ['m17'] },
    { type: 'link' },
  ],
});
```

| Method | Pays with                            | Settings                   |
| ------ | ------------------------------------ | -------------------------- |
| `link` | An account the person linked in Dash | `providers`                |
| `card` | A card                               | None                       |
| `momo` | Mobile money                         | `providers`, `phoneNumber` |

`providers` lists the only providers that can pay, by Monime's IDs. Leave it out, or leave it empty, for any. Mobile money takes `m17` (Orange Money) and `m18` (Africell Money). A linked account is at a bank, so it takes only the banks: `slb001` (Sierra Leone Commercial Bank), `slb004` (Guaranty Trust Bank), `slb007`, `slb009` and `slb013`.

`phoneNumber` is the only number that can pay with mobile money, written as `+23276123456`.

## Where the money goes

The money goes to the Space's main account. To pay into another of the Space's accounts, give its ID:

```ts
await this.dash.payments.request({ order, financialAccountId: 'fac-k6VgVTfSmLruHpiQgMg4DUeySZx' });
```

The account must belong to the Space that owns the dot and hold the order's currency, or the request rejects with `invalid`.

## Confirm on your server

Anyone can change what a dot reports. Treat the result as something to show the person, not proof of payment. Deliver the ticket only once Monime confirms the payment to your server, by [webhook](/docs/developer-resources/guides/webhook/introduction) or by looking it up with your Space's API key.

The result names what to look up:

| Paid with        | Field               | What it is                                                                       |
| ---------------- | ------------------- | -------------------------------------------------------------------------------- |
| A card           | `checkoutSessionId` | A [checkout session](/docs/developer-resources/guides/checkout-session/overview) |
| Mobile money     | `paymentCodeId`     | A payment code                                                                   |
| A linked account | `paymentId`         | The payment                                                                      |

Each result also has the order's `reference` and total `amount`, and `orderNumber` once the payment completes, for the person's receipt. `requestId` is Dash's ID for the request. Monime's support asks for it if something goes wrong.

## Test payments

In the simulator, Dash's sheet is a stand-in. Pick a way to pay, then choose how the payment ends: **Paid**, **Pending**, **Declined** or **Expired**. **Declined** keeps the sheet open. No money moves.

In a [live test](/docs/products/dash-dots/developers/run-on-a-phone) on a phone, payments are real, and the money goes to your Space. They only work once the dot is [published](/docs/products/dash-dots/developers/publish-and-release) and `dot.json` has the `id` Monime gave it. Until then, `request` and `get` reject with `denied`, and the rest of the dot runs as usual. Keep test orders small.

In a [test](/docs/products/dash-dots/developers/testing), fake the call like any other:

```ts
const page = await testPage(tickets, {
  host: {
    'dash.payments.request': () => ({
      method: 'momo',
      paymentCodeId: 'pmc-test',
      reference: 'order-1042',
      requestId: 'req-test',
      status: 'completed',
      amount: { currency: 'SLE', value: 15000 },
    }),
  },
});
```
