# Create Payment

### Overview

* Endpoint: `POST /Payments/crypto` (Sandbox base `https://api.test.devs.beadpay.io`)
* Supports crypto and digital wallet tenders
* Returns one or more hosted payment URLs you can embed or open
* Optionally redirects the shopper to `redirectUrl`
* Supports optional per-payment webhooks via `webhookUrls`, in addition to the terminal’s default webhook

For an end-to-end Sandbox flow, see Quick Start.

### Step 1: Authenticate

Payments use header-based authentication with a terminal API key.

The terminal API key is the secret credential used to authenticate requests. The masked API key is a display value only and cannot be used to authenticate.

#### Required headers

```http
X-Api-Key: {apiKey}
Content-Type: application/json
Accept: application/json
```

#### Authentication guidance

* **Preferred for new integrations**: use `X-Api-Key`
* **Supported for existing legacy Payments integrations**: OAuth password grant with `Authorization: Bearer {access_token}`
* **Do not** place the API key in the request body or URL
* **Do not** expose the API key in browser or mobile client code

See Authentication for full token and legacy OAuth details.

### Step 2: Create a hosted payment URL

Send a request to create a payment. The response includes `trackingId`, `paymentPageId`, and `paymentUrls`.

#### Endpoint

```http
POST https://api.test.devs.beadpay.io/Payments/crypto
```

#### Headers

```http
X-Api-Key: {apiKey}
Content-Type: application/json
Accept: application/json
```

### Request shape guidance

Required fields can vary depending on terminal configuration, terminal `type`, and tender-specific requirements.

At a minimum, all payment creation requests should include:

* `terminalId`
* `merchantId`
* `requestedAmount`

Other common fields include:

* `reference`
* `description`
* `redirectUrl`
* `emailReceipt`
* `smsReceipt`
* `refundEmail`
* `webhookUrls`
* `customer`

### Terminal `type` considerations

For some payment flows, required fields differ depending on the terminal `type`.

#### Terminal `type` = `virtual`

For flows such as Klarna testing, terminals with `type` = `virtual` may require:

* `merchantId`
* `terminalId`
* `requestedAmount`
* `refundEmail`
* `customer`

#### Terminal `type` = `physical`

For flows such as Klarna testing, terminals with `type` = `physical` may require:

* `merchantId`
* `terminalId`
* `requestedAmount`
* `customer`

#### Important note

The exact minimum request body is not universal across all tenders and terminal setups.

Examples:

* some payment flows only require the basic payment fields
* some flows require the full `customer` object
* some flows require `refundEmail`, especially for terminals with `type` = `virtual`
* terminals with `type` = `physical` may allow a different minimum request shape

When documenting or testing a specific tender, use a request example that matches that tender and terminal `type`.

### Example request body (basic)

Use this as a basic hosted payment example. It is not a guaranteed universal minimum for every payment flow.

```json
{
  "terminalId": "TERM-123",
  "merchantId": "MERCH-456",
  "requestedAmount": 100.00,
  "reference": "ORDER-4821"
}
```

### Example request body for terminal `type` = `virtual`

This example is appropriate for flows that require customer details and `refundEmail`, such as certain digital wallet or Klarna-related scenarios.

```json
{
  "terminalId": "{terminalId}",
  "merchantId": "{merchantId}",
  "requestedAmount": 1.00,
  "refundEmail": "alex.tester@example.com",
  "customer": {
    "firstName": "Jordan",
    "lastName": "Reed",
    "email": "jordan.reed@example.com",
    "address": "456 Market St",
    "address2": "Suite 210",
    "city": "Chicago",
    "state": "IL",
    "postalCode": "60601",
    "countryCode": "US"
  }
}
```

### Example request body for terminal `type` = `physical`

This example is appropriate for flows that require customer details but do not require `refundEmail`.

```json
{
  "terminalId": "{terminalId}",
  "merchantId": "{merchantId}",
  "requestedAmount": 1.00,
  "customer": {
    "firstName": "Taylor",
    "lastName": "Brooks",
    "email": "taylor.brooks@example.com",
    "address": "88 Lakeview Ave",
    "address2": "",
    "city": "Denver",
    "state": "CO",
    "postalCode": "80202",
    "countryCode": "US"
  }
}
```

### Example request body (expanded)

```json
{
  "terminalId": "TERM-123",
  "merchantId": "MERCH-456",
  "requestedAmount": 100.00,
  "reference": "ORDER-4821",
  "description": "Coffee subscription order",
  "customer": {
    "email": "casey.hart@example.com",
    "firstName": "Casey",
    "lastName": "Hart",
    "address": "19 Harbor Point Rd",
    "address2": "Floor 2",
    "city": "Seattle",
    "state": "WA",
    "countryCode": "US",
    "postalCode": "98101"
  },
  "redirectUrl": "https://merchant.example.com/payment-return",
  "emailReceipt": true,
  "smsReceipt": false,
  "refundEmail": "billing.ops@example.com",
  "webhookUrls": [
    "https://merchant.example.com/payments/webhook"
  ]
}
```

#### Notes

* `refundEmail` is recommended when you have a payer email available. It is used to send reclaim instructions if the payment ends as `underpaid`, `overpaid`, `expired`, `invalid`, or `cancelled`.
* The `customer` object may be required depending on terminal configuration and tender flow.
* If you include the `customer` object, provide all required customer fields.

### Example response

```json
{
  "trackingId": "c10b29e3c4b54d8aa12f9934",
  "paymentPageId": "6539fa89f0363f1722b377ef",
  "paymentUrls": [
    "https://pay.test.devs.beadpay.io/6539fa89f0363f1722b377ef"
  ]
}
```

### Key fields

* `terminalId` — Terminal identifier in Bead. Determines device-level settings such as tender configuration and terminal default webhook.
* `merchantId` — Merchant identifier. Used for reporting and settlement.
* `requestedAmount` — Amount in the requested currency.
* `reference` — Merchant-side reference such as an order or invoice number.
* `description` — Description that may be shown to the customer and in reporting.
* `customer` — Customer object when required by the payment flow.
* `redirectUrl` — Optional URL Bead redirects to after checkout completes or is cancelled.
* `emailReceipt`, `smsReceipt` — Optional receipt delivery settings.
* `refundEmail` — Optional email used for reclaim instructions when unconverted crypto must be returned to the payer.
* `webhookUrls` — Optional array of additional webhook endpoints for this payment.

### Underpayments and overpayments

In wallet-based crypto payments, the customer typically scans a QR code to set the destination and then manually enters the amount in their wallet app. If they mistype the amount, the payment can end in one of these outcomes:

* `underpaid` — The customer sent less than requested
* `overpaid` — The customer sent more than requested

#### Recommended handling

* **Underpaid**: treat as unsuccessful. Do not fulfill. If the customer still wants to pay, create a new payment and start a new hosted checkout.
* **Overpaid**: treat as successful for the requested amount. You may fulfill once confirmed. The overage is returned to the payer through the reclaim flow.

#### Email behavior for reclaim

* If you provide `refundEmail`, Bead emails reclaim instructions when reclaim is required.
* If `refundEmail` is not provided, the hosted payment page prompts the payer to enter an email address when an underpaid or overpaid outcome occurs, then Bead emails reclaim instructions.

### Webhooks

If you include `webhookUrls`, Bead sends payment lifecycle events to both:

1. the terminal’s default webhook
2. each URL in the `webhookUrls` array

Configure at least one webhook endpoint before you depend on webhooks in production. See Payment Webhooks for payload and retry details.

### Customer data rule

If your payment flow requires a `customer` object, include all required customer fields.

Typical required fields are:

* `firstName`
* `lastName`
* `email`
* `address`
* `address2`
* `city`
* `state`
* `postalCode`
* `countryCode`

If you send a partial `customer` object, the API may return `400 Bad Request` with validation errors.

### Step 3: Present the hosted page

Use the first URL in `paymentUrls` to start checkout.

#### Common patterns

* **Web or SPA**: embed the URL in an iframe or open a new tab or window
* **Native app**: load the URL in an in-app browser or webview

#### Redirect behavior

* If `redirectUrl` is provided, Bead redirects the browser to `redirectUrl` after success or cancel and includes context in the query string.
* If `redirectUrl` is not provided, Bead displays a hosted confirmation page that indicates success or cancellation.

Regardless of redirect choice, always confirm the final status via webhooks or status polling before shipping goods or granting access.

### Step 4: Confirm payment status

You can confirm payment status using webhooks or polling.

#### Option A: Webhooks

* Configure a terminal-level webhook and optionally supply `webhookUrls` per payment
* Your server receives JSON payloads whenever `statusCode` changes
* Respond with `200 OK` as soon as you have persisted or queued the event

#### Option B: Polling

**Endpoint**

```http
GET https://api.test.devs.beadpay.io/payments/tracking/{trackingId}
```

**Headers**

```http
X-Api-Key: {apiKey}
Accept: application/json
```

**Example curl**

```bash
curl -s -X GET "https://api.test.devs.beadpay.io/payments/tracking/{trackingId}" \
  -H "X-Api-Key: {apiKey}" \
  -H "Accept: application/json"
```

Use the `trackingId` returned from the Create Payment response.

#### Typical statuses

* `created` and `processing` while the customer is paying
* `completed` when the payment has fully succeeded
* `underpaid`, `overpaid`, `expired`, `invalid`, or `cancelled` for non-happy-path outcomes

For full status details, see Payment Statuses.

### Troubleshooting

* **401 Unauthorized**
  * API key is missing, invalid, or sent using the wrong header name. The header must be exactly `X-Api-Key`.
* **Hosted page will not load**
  * Confirm you are using a URL from the `paymentUrls` array returned by the create payment response.
  * Confirm the terminal is configured for at least one tender in the environment you are testing.
* **Validation error on payment creation**
  * Confirm you are using a request shape that matches the terminal `type` and tender requirements.
  * If using a terminal with `type` = `virtual`, confirm whether `refundEmail` is required for the flow you are testing.
  * If using a flow that requires `customer`, confirm that all required customer fields are present.
* **No status change**
  * Confirm you are polling the correct `trackingId`.
  * If using webhooks, confirm your endpoint is reachable and returns HTTP 200 quickly.

### Next steps

After you can create and complete a payment in Sandbox:

* Configure Payment Webhooks and verify your endpoint receives status events
* Use Payment Statuses for ad hoc queries and troubleshooting
* Explore Reporting and Settlement to build payment history and reconciliation jobs
* When ready for Production, request production credentials and switch API base URLs to Production

### Legacy authentication

Some existing integrations authenticate Payments using OAuth 2.0 password grant with a terminal username and password. New Payments integrations should use the terminal API key method described above.

If you are using legacy OAuth for Payments, obtain an access token using the password grant and send:

```http
Authorization: Bearer {access_token}
```

See the [Authentication](https://developers.bead.xyz/authentication) page for token endpoint details, realms, and refresh token usage.
