> For the complete index of the 402pay docs, see [llms.txt](https://developer.402pay.co/llms.txt).

# Quickstart

Take your first payment in five steps, from a new account to a fulfilled order.

## Before you start

| You need | Why |
| --- | --- |
| A 402pay account | It owns your wallet, your keys and every payment. |
| A server | Secret keys only ever live on a server, never in a browser or app. |
| A public HTTPS endpoint | For the webhook that tells you when a payment succeeds. |

> Never put a secret key in browser or mobile code. Anyone who opens the developer tools could read it and act as your business. Create payments on your server and send the customer only the checkout `url`.

## Create an account

Sign up with your email and a passkey or a password, enter the code we email you, then add your business and create its wallet. It takes a few minutes. [Create an account](https://dash.402pay.co/auth/signup).

## Create a secret key

In the dashboard, open Developers, then API keys, and create a test key. Its secret starts with `402s_test_` and is shown once, so put it in your server's environment.

Shell:

```bash
export PAY402_SECRET_KEY="402s_test_..."
```

> During the beta, test and live keys act on the same business: what you create with a test key shows up in your dashboard and in every list, like anything else, and every event's `mode` is `live`. On a live business a test key can read everything, but creating a payment, a checkout or a remainder, or changing what customers see or pay, answers 403 `test_mode_unavailable`, so a test key never moves real money or changes a live checkout. See [test and live keys](https://developer.402pay.co/testing.md#test-and-live).

## Create a payment

Create it from your server when the customer is ready to pay. The response carries a hosted checkout `url`.

cURL:

```bash
curl -X POST "https://dash.402pay.co/api/v1/payments" \
  -H "Authorization: Bearer $PAY402_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 4900,
    "currency": "USD",
    "reference": "order_1042",
    "description": "Pro plan, monthly",
    "success_url": "https://example.com/thanks"
  }'
```

Node.js:

```js
const response = await fetch("https://dash.402pay.co/api/v1/payments", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAY402_SECRET_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: 4900,
    currency: "USD",
    reference: "order_1042",
    description: "Pro plan, monthly",
    success_url: "https://example.com/thanks"
  }),
});
const { data } = await response.json();
```

Python:

```python
import os

import requests

response = requests.post(
    "https://dash.402pay.co/api/v1/payments",
    headers={
        "Authorization": f"Bearer {os.environ['PAY402_SECRET_KEY']}",
    },
    json={
        "amount": 4900,
        "currency": "USD",
        "reference": "order_1042",
        "description": "Pro plan, monthly",
        "success_url": "https://example.com/thanks"
    },
)
data = response.json()["data"]
```

## Send the customer to checkout

Redirect to the payment's `url`. Checkout handles the coin and network, cards, short transfers and the receipt, then sends the customer to your `success_url`.

## Fulfill the order on a webhook

Add a webhook endpoint for `payment.succeeded`, in the dashboard under Developers, then Webhooks, or through the API. Its `url` must be a public `https://` address, so `localhost` and private networks are refused. Save the signing secret in the response: it's shown once.

cURL:

```bash
curl -X POST "https://dash.402pay.co/api/v1/webhooks" \
  -H "Authorization: Bearer $PAY402_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/402pay",
    "event_types": ["payment.succeeded"],
    "description": "Fulfill orders"
  }'
```

Node.js:

```js
const response = await fetch("https://dash.402pay.co/api/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAY402_SECRET_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://example.com/webhooks/402pay",
    event_types: ["payment.succeeded"],
    description: "Fulfill orders"
  }),
});
const { data } = await response.json();
```

Python:

```python
import os

import requests

response = requests.post(
    "https://dash.402pay.co/api/v1/webhooks",
    headers={
        "Authorization": f"Bearer {os.environ['PAY402_SECRET_KEY']}",
    },
    json={
        "url": "https://example.com/webhooks/402pay",
        "event_types": ["payment.succeeded"],
        "description": "Fulfill orders"
    },
)
data = response.json()["data"]
```

When it arrives, [verify the signature](https://developer.402pay.co/guides/webhooks.md#verify) and fulfill the order its `reference` points to.

webhooks.js, Node.js:

```js
// Fulfill once, when the payment succeeds.
app.post("/webhooks/402pay", express.raw({ type: "application/json" }), async (req, res) => {
  if (!verifyWebhook(process.env.PAY402_WEBHOOK_SECRET, req.headers, req.body)) {
    return res.sendStatus(400);
  }
  res.sendStatus(200);

  const event = JSON.parse(req.body);
  // A test delivery names a made-up payment, and its data holds only that id.
  if (event.test) return;
  if (event.type === "payment.succeeded") {
    await orders.markPaid(event.data.reference, event.data.id);
  }
});
```

> Each delivery is a signed HTTPS request to your endpoint. A local 402pay sends nothing and records each delivery with its signed request and a 200 response. Either way, read every attempt under Developers, then Webhooks, or with `GET /webhook-deliveries`. See [live and simulated businesses](https://developer.402pay.co/testing.md#sandbox).

## Next steps

- [Testing](https://developer.402pay.co/testing.md): Build with test keys and rehearse every way a payment can arrive.
- [Going live](https://developer.402pay.co/going-live.md): Everything to check before you take real payments, and after.
- [Hosted checkout](https://developer.402pay.co/guides/hosted-checkout.md): Everything between choosing how to pay and the receipt, on one hosted page.
- [Underpayments and overpayments](https://developer.402pay.co/guides/underpayments.md): What happens when a customer sends too little, too much, too late or on the wrong network.
