# Create a checkout

`POST https://api.weavpayments.com/v1/checkouts`

One call, one link. Send your customer to `checkout_url`: they choose how to pay (QR Ph, e-wallets, cards or cash at a counter), give their name if you didn't, and pay. Paid once. Send `items` (Weav adds them up) or one `amount`, never both.

Each channel your customer starts is a payment with `checkout_reference_id` set (its PAYMENT_* webhooks still come). The first payment paid completes the checkout (`CHECKOUT_COMPLETED`). Never treat a redirect to `success_url` as proof of payment.

## How it works

- Your customer chooses a channel. Weav starts a payment on it for the checkout’s total, the same as a payment you’d create yourself, with `checkout_reference_id` set. You get its `PAYMENT_*` webhooks as usual.
- QR Ph also shows as each bank and e-wallet app that pays it (UnionBank, BDO Pay, GrabPay and more), because many customers know their app better than QR Ph. Choosing one makes a QR Ph payment with `qr_ph_app` set: the page shows that app’s logo, colors and steps. Any QR Ph app can still pay it.
- They can change their mind: choosing another channel cancels the payment in progress (you get `PAYMENT_CANCELLED`) and starts a new one.
- When you pay the fee, every channel shows the same total. When your customer pays it (fees on top), each channel shows its own total, fee included.
- The first payment paid completes the checkout: `status` becomes `COMPLETED`, `payment` is that payment, and you get `CHECKOUT_COMPLETED`.
- Rarely, a customer pays a QR code and then pays again another way. The second payment is real money: it’s credited like any payment, and Weav refunds it with you.
- A checkout that runs out unpaid becomes `EXPIRED` (`CHECKOUT_EXPIRED`). A payment still in progress, such as a counter code good for 48 hours, keeps it open until that payment ends. If it’s paid, the checkout completes.

```mermaid
sequenceDiagram
    autonumber
    actor Customer as Your customer
    participant Server as Your server
    participant Weav
    Server->>Weav: POST /v1/checkouts
    Weav-->>Server: 201 OPEN, checkout_url
    Server-->>Customer: Sends them to checkout_url
    Customer->>Weav: Chooses a channel
    Note over Weav: Starts a payment on it,<br/>with checkout_reference_id
    opt They choose another channel
        Weav->>Server: PAYMENT_CANCELLED webhook<br/>for the first payment
        Note over Weav: Starts a new payment
    end
    Customer->>Weav: Pays
    Weav->>Server: PAYMENT_COMPLETED and<br/>CHECKOUT_COMPLETED webhooks
```

## Statuses

```mermaid
flowchart LR
    O[OPEN] --> C[COMPLETED]
    O --> E[EXPIRED]
    O --> X[CANCELLED]
    E -. paid late .-> C
    class O warn
    class C ok
    class E error
    class X neutral
```

| Status | Meaning |
| --- | --- |
| `OPEN` | Waiting for your customer to pay. |
| `COMPLETED` | Paid. Final. |
| `EXPIRED` | Ran out unpaid. A late payment still completes it. |
| `CANCELLED` | You canceled it. Final. |

## Headers

- `Authorization` (string, required): `Bearer` plus your API key. See [Authentication](https://docs.weavpayments.com/authentication).
- `Idempotency-Key` (string): Optional. A unique value per new checkout, such as a UUID. Retrying with the same key and body returns the stored response; a different body returns 409. Without it, your `external_reference_id` is the key (a checkout with neither is never matched to an earlier one). Up to 255 characters.

## Body

JSON, with `Content-Type: application/json`.

- `items` (array of objects | null): An order, up to 50 items. Each has `name` (up to 255 characters), `quantity` (1 to 9,999) and `unit_amount` (pesos, a JSON number). Weav multiplies and adds them up; the total must be within your payment limits. Up to 50 items.
  - `name` (string, required): Up to 255 characters.
  - `quantity` (integer, required): At least 1. At most 9999.
  - `unit_amount` (number, required): Pesos, as a JSON number.
  - `amount` (number): `quantity` × `unit_amount`.
- `amount` (number | null): Pesos. Within your payment limits.
- `currency` (string | null): One of: `PHP`.
- `description` (string | null): What it's for, up to 255 characters. Your customer sees it. Up to 255 characters.
- `external_reference_id` (string | null): Unique across your checkouts in this mode when given. Up to 64 characters.
- `channels` (array of strings | null): Only these channels. Default every channel that's on. One of: `QR_PH`, `GCASH`, `MAYA`, `CARD`, `PAY_AND_GO`, `PALAWANPAY`, `BAYAD`.
- `qr_ph_apps` (array of strings | null): QR Ph only as these bank or e-wallet apps, for example a UnionBank link. Needs QR Ph among the channels. One of: `BDO`, `CHINABANK`, `GOTYME`, `LANDBANK`, `METROBANK`, `PNB`, `RCBC`, `ROBINSONS_BANK`, `SECURITY_BANK`, `UNIONBANK`, `GRABPAY`, `SHOPEEPAY`.
- `save_payment_method` (boolean | null): If your customer pays by card, also saves the card for later charges (auto-debit). Send `customer.id`. They agree to future charges on Weav's payment page, and their bank's check for this payment also confirms the card. Another way to pay saves nothing: send `channels: [CARD]` to take cards only. The payment's `payment_method_id` is the saved card: you get `PAYMENT_COMPLETED`, then `PAYMENT_METHOD_SAVED`. If that payment isn't paid, the card isn't kept (`PAYMENT_METHOD_FAILED`). Off until Weav turns on saving cards for your account (422 `4220401`).
- `customer` (object | null): All optional. With a name, your customer isn't asked for one; with an email, they get a receipt.
  - `id` (string): With `save_payment_method` only, and required then: your own ID for this customer. Their saved cards are listed by it (`GET /v1/payment-methods?customer_id=`). Up to 64 characters. Pattern: ^[A-Za-z0-9._:@-]+$.
  - `name` (string | null): Up to 255 characters.
  - `email` (string (email) | null)
  - `mobile_no` (string | null)
- `success_url` (string (uri) | null)
- `cancel_url` (string (uri) | null): HTTPS. Where your customer goes if they leave, or the checkout expires or is canceled. Up to 2048 characters.
- `webhook_url` (string (uri) | null)
- `expires_in_minutes` (integer | null): How long it stays open, 5 to 10,080 (7 days). Default 1,440 (24 hours). At least 5. At most 10080. Default: 1440.

## Responses

### 201 Created

Checkout created (code 2010300).

What `data` holds:

- `checkout` (object)
  - `reference_id` (string)
  - `external_reference_id` (string | null)
  - `type` (string): One of: `ORDER`, `AMOUNT`.
  - `status` (string): One of: `OPEN`, `COMPLETED`, `EXPIRED`, `CANCELLED`.
  - `currency` (string): ISO 4217. PHP today; more as they're enabled for your account. One of: `PHP`.
  - `amount` (number): The total, in pesos.
  - `description` (string | null)
  - `items` (array of objects)
    - `name` (string, required): Up to 255 characters.
    - `quantity` (integer, required): At least 1. At most 9999.
    - `unit_amount` (number, required): Pesos, as a JSON number.
    - `amount` (number): `quantity` × `unit_amount`.
  - `channels` (array of strings | null): One of: `QR_PH`, `GCASH`, `MAYA`, `CARD`, `PAY_AND_GO`, `PALAWANPAY`, `BAYAD`.
  - `qr_ph_apps` (array of strings | null)
  - `customer` (object)
    - `id` (string | null): Your ID for the customer, with `save_payment_method`.
    - `name` (string | null)
    - `email` (string | null)
    - `mobile_no` (string | null)
  - `save_payment_method` (boolean): A card payment also saves the card (the payment's `payment_method_id`).
  - `checkout_url` (string (uri))
  - `success_url` (string | null)
  - `cancel_url` (string | null)
  - `webhook_url` (string | null)
  - `payment` (any): The payment that paid it.
  - `expires_at` (string (date-time))
  - `completed_at` (string (date-time) | null)
  - `expired_at` (string (date-time) | null)
  - `cancelled_at` (string (date-time) | null)
  - `created_at` (string (date-time))
  - `updated_at` (string (date-time))

```json
{
    "code": "2010300",
    "success": true,
    "message": "Checkout created",
    "data": {
        "checkout": {
            "reference_id": "CK4T8M2Q7X9KBA",
            "external_reference_id": "ORD-10541",
            "type": "ORDER",
            "status": "OPEN",
            "currency": "PHP",
            "amount": 1250.0,
            "description": null,
            "items": [
                {
                    "name": "Canvas tote bag",
                    "quantity": 1,
                    "unit_amount": 1000.0,
                    "amount": 1000.0
                },
                {
                    "name": "Canvas Strap",
                    "quantity": 2,
                    "unit_amount": 125.0,
                    "amount": 250.0
                }
            ],
            "channels": null,
            "qr_ph_apps": null,
            "customer": {
                "id": null,
                "name": null,
                "email": null,
                "mobile_no": null
            },
            "save_payment_method": false,
            "checkout_url": "https://pay.weavpayments.com/checkout/CK4T8M2Q7X9KBA",
            "success_url": "https://shop.example.com/orders/ORD-10541/thanks",
            "cancel_url": "https://shop.example.com/cart",
            "webhook_url": null,
            "payment": null,
            "expires_at": "2026-10-27T10:12:00+08:00",
            "completed_at": null,
            "expired_at": null,
            "cancelled_at": null,
            "created_at": "2026-10-26T10:12:00+08:00",
            "updated_at": "2026-10-26T10:12:00+08:00"
        }
    }
}
```

### 401 Unauthorized

Missing, malformed or unknown API key (code 4010000).

```json
{
    "code": "4010000",
    "success": false,
    "message": "Missing or invalid API key."
}
```

### 403 Forbidden

The key can't make this request (code 4030000): an address outside the key's IP allowlist, a live key before your live mode is on, or an account that isn't active. A live key sent from a browser is refused (code 4030001): keep live keys on your server.

```json
{
    "code": "4030000",
    "success": false,
    "message": "Requests with this key must come from an address on its allowlist."
}
```

### 409 Conflict

The request conflicts with the current state, e.g. an Idempotency-Key reused with a different body (4090001), a request still in progress (4090002), a payment that can't be canceled (4090101) or can't be canceled at the provider yet (4090102, retry shortly), or a webhook still being sent (4090201).

```json
{
    "code": "4090001",
    "success": false,
    "message": "This Idempotency-Key was already used with a different request."
}
```

### 422 Unprocessable Content

One or more fields are invalid (code 4220000), or the channel is off (4220101) or doesn't take the currency (4220102).

```json
{
    "code": "4220000",
    "success": false,
    "message": "The given data was invalid.",
    "errors": {
        "channel": [
            "The channel field is required."
        ]
    }
}
```

### 429 Too Many Requests

Rate limited (code 4290000). Wait for `Retry-After` seconds.

```json
{
    "code": "4290000",
    "success": false,
    "message": "Too many requests. Wait, then retry."
}
```

## Examples

### An order

cURL:

```shell
curl -X POST https://api.weavpayments.com/v1/checkouts \
    -H "Authorization: Bearer $WEAV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "items": [
        {
            "name": "Canvas tote bag",
            "quantity": 1,
            "unit_amount": 1000.00
        },
        {
            "name": "Canvas Strap",
            "quantity": 2,
            "unit_amount": 125.00
        }
    ],
    "external_reference_id": "ORD-10541",
    "success_url": "https://shop.example.com/orders/ORD-10541/thanks",
    "cancel_url": "https://shop.example.com/cart"
}'
```

Node.js:

```js
const url = 'https://api.weavpayments.com/v1/checkouts';
const response = await fetch(url, {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.WEAV_API_KEY}`,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        items: [
            {
                name: 'Canvas tote bag',
                quantity: 1,
                unit_amount: 1000.0,
            },
            {
                name: 'Canvas Strap',
                quantity: 2,
                unit_amount: 125.0,
            },
        ],
        external_reference_id: 'ORD-10541',
        success_url: 'https://shop.example.com/orders/ORD-10541/thanks',
        cancel_url: 'https://shop.example.com/cart',
    }),
});

const result = await response.json();
```

JavaScript:

```js
import axios from 'axios';

const response = await axios.request({
    method: 'post',
    url: 'https://api.weavpayments.com/v1/checkouts',
    headers: {
        Authorization: `Bearer ${process.env.WEAV_API_KEY}`,
    },
    data: {
        items: [
            {
                name: 'Canvas tote bag',
                quantity: 1,
                unit_amount: 1000.0,
            },
            {
                name: 'Canvas Strap',
                quantity: 2,
                unit_amount: 125.0,
            },
        ],
        external_reference_id: 'ORD-10541',
        success_url: 'https://shop.example.com/orders/ORD-10541/thanks',
        cancel_url: 'https://shop.example.com/cart',
    },
    // Read error answers too: their body has the error code.
    validateStatus: () => true,
});

const result = response.data;
```

PHP:

```php
use Illuminate\Support\Facades\Http;

$response = Http::withToken(config('services.weav.key'))
    ->post('https://api.weavpayments.com/v1/checkouts', [
        'items' => [
            [
                'name' => 'Canvas tote bag',
                'quantity' => 1,
                'unit_amount' => 1000.00,
            ],
            [
                'name' => 'Canvas Strap',
                'quantity' => 2,
                'unit_amount' => 125.00,
            ],
        ],
        'external_reference_id' => 'ORD-10541',
        'success_url' => 'https://shop.example.com/orders/ORD-10541/thanks',
        'cancel_url' => 'https://shop.example.com/cart',
    ]);

$result = $response->json();
```

Python:

```python
import os
import requests

response = requests.post(
    "https://api.weavpayments.com/v1/checkouts",
    headers={
        "Authorization": f"Bearer {os.environ['WEAV_API_KEY']}",
    },
    json={
        "items": [
            {
                "name": "Canvas tote bag",
                "quantity": 1,
                "unit_amount": 1000.00,
            },
            {
                "name": "Canvas Strap",
                "quantity": 2,
                "unit_amount": 125.00,
            },
        ],
        "external_reference_id": "ORD-10541",
        "success_url": "https://shop.example.com/orders/ORD-10541/thanks",
        "cancel_url": "https://shop.example.com/cart",
    },
    timeout=30,
)

result = response.json()
```

Go:

```go
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "strings"
)

func main() {
    body := strings.NewReader(`{
    "items": [
        {
            "name": "Canvas tote bag",
            "quantity": 1,
            "unit_amount": 1000.00
        },
        {
            "name": "Canvas Strap",
            "quantity": 2,
            "unit_amount": 125.00
        }
    ],
    "external_reference_id": "ORD-10541",
    "success_url": "https://shop.example.com/orders/ORD-10541/thanks",
    "cancel_url": "https://shop.example.com/cart"
}`)

    req, err := http.NewRequest("POST", "https://api.weavpayments.com/v1/checkouts", body)
    if err != nil {
        panic(err)
    }
    req.Header.Set("Authorization", "Bearer "+os.Getenv("WEAV_API_KEY"))
    req.Header.Set("Content-Type", "application/json")

    res, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()

    result, err := io.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }
    fmt.Println(res.Status, string(result))
}
```

Rust:

```rust
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }, serde_json = "1"
use serde_json::json;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let key = std::env::var("WEAV_API_KEY")?;
    let body = json!({
        "items": [
            {
                "name": "Canvas tote bag",
                "quantity": 1,
                "unit_amount": 1000.00
            },
            {
                "name": "Canvas Strap",
                "quantity": 2,
                "unit_amount": 125.00
            }
        ],
        "external_reference_id": "ORD-10541",
        "success_url": "https://shop.example.com/orders/ORD-10541/thanks",
        "cancel_url": "https://shop.example.com/cart"
    });

    let response = reqwest::blocking::Client::new()
        .post("https://api.weavpayments.com/v1/checkouts")
        .bearer_auth(key)
        .json(&body)
        .send()?;

    let status = response.status();
    let result: serde_json::Value = response.json()?;
    println!("{status} {result:#}");
    Ok(())
}
```

Java:

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class WeavExample {
    public static void main(String[] args) throws Exception {
        String url = "https://api.weavpayments.com/v1/checkouts";
        String body =
                """
                {
                    "items": [
                        {
                            "name": "Canvas tote bag",
                            "quantity": 1,
                            "unit_amount": 1000.00
                        },
                        {
                            "name": "Canvas Strap",
                            "quantity": 2,
                            "unit_amount": 125.00
                        }
                    ],
                    "external_reference_id": "ORD-10541",
                    "success_url": "https://shop.example.com/orders/ORD-10541/thanks",
                    "cancel_url": "https://shop.example.com/cart"
                }
                """;

        HttpRequest request =
                HttpRequest.newBuilder()
                        .uri(URI.create(url))
                        .header("Authorization", "Bearer " + System.getenv("WEAV_API_KEY"))
                        .header("Content-Type", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(body))
                        .build();

        HttpResponse<String> response =
                HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}
```

Response (201):

```json
{
    "code": "2010300",
    "success": true,
    "message": "Checkout created",
    "data": {
        "checkout": {
            "reference_id": "CK4T8M2Q7X9KBA",
            "external_reference_id": "ORD-10541",
            "type": "ORDER",
            "status": "OPEN",
            "currency": "PHP",
            "amount": 1250.0,
            "description": null,
            "items": [
                {
                    "name": "Canvas tote bag",
                    "quantity": 1,
                    "unit_amount": 1000.0,
                    "amount": 1000.0
                },
                {
                    "name": "Canvas Strap",
                    "quantity": 2,
                    "unit_amount": 125.0,
                    "amount": 250.0
                }
            ],
            "channels": null,
            "qr_ph_apps": null,
            "customer": {
                "id": null,
                "name": null,
                "email": null,
                "mobile_no": null
            },
            "save_payment_method": false,
            "checkout_url": "https://pay.weavpayments.com/checkout/CK4T8M2Q7X9KBA",
            "success_url": "https://shop.example.com/orders/ORD-10541/thanks",
            "cancel_url": "https://shop.example.com/cart",
            "webhook_url": null,
            "payment": null,
            "expires_at": "2026-10-27T10:12:00+08:00",
            "completed_at": null,
            "expired_at": null,
            "cancelled_at": null,
            "created_at": "2026-10-26T10:12:00+08:00",
            "updated_at": "2026-10-26T10:12:00+08:00"
        }
    }
}
```

### A plan, saving the card

cURL:

```shell
curl -X POST https://api.weavpayments.com/v1/checkouts \
    -H "Authorization: Bearer $WEAV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
    "items": [
        {
            "name": "Monthly plan",
            "quantity": 1,
            "unit_amount": 499.00
        }
    ],
    "external_reference_id": "SUB-USER-123-START",
    "channels": [
        "CARD"
    ],
    "save_payment_method": true,
    "customer": {
        "id": "USER-123",
        "name": "Juan dela Cruz"
    },
    "success_url": "https://shop.example.com/account/plan"
}'
```

Node.js:

```js
const url = 'https://api.weavpayments.com/v1/checkouts';
const response = await fetch(url, {
    method: 'POST',
    headers: {
        Authorization: `Bearer ${process.env.WEAV_API_KEY}`,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        items: [
            {
                name: 'Monthly plan',
                quantity: 1,
                unit_amount: 499.0,
            },
        ],
        external_reference_id: 'SUB-USER-123-START',
        channels: ['CARD'],
        save_payment_method: true,
        customer: {
            id: 'USER-123',
            name: 'Juan dela Cruz',
        },
        success_url: 'https://shop.example.com/account/plan',
    }),
});

const result = await response.json();
```

JavaScript:

```js
import axios from 'axios';

const response = await axios.request({
    method: 'post',
    url: 'https://api.weavpayments.com/v1/checkouts',
    headers: {
        Authorization: `Bearer ${process.env.WEAV_API_KEY}`,
    },
    data: {
        items: [
            {
                name: 'Monthly plan',
                quantity: 1,
                unit_amount: 499.0,
            },
        ],
        external_reference_id: 'SUB-USER-123-START',
        channels: ['CARD'],
        save_payment_method: true,
        customer: {
            id: 'USER-123',
            name: 'Juan dela Cruz',
        },
        success_url: 'https://shop.example.com/account/plan',
    },
    // Read error answers too: their body has the error code.
    validateStatus: () => true,
});

const result = response.data;
```

PHP:

```php
use Illuminate\Support\Facades\Http;

$response = Http::withToken(config('services.weav.key'))
    ->post('https://api.weavpayments.com/v1/checkouts', [
        'items' => [
            [
                'name' => 'Monthly plan',
                'quantity' => 1,
                'unit_amount' => 499.00,
            ],
        ],
        'external_reference_id' => 'SUB-USER-123-START',
        'channels' => [
            'CARD',
        ],
        'save_payment_method' => true,
        'customer' => [
            'id' => 'USER-123',
            'name' => 'Juan dela Cruz',
        ],
        'success_url' => 'https://shop.example.com/account/plan',
    ]);

$result = $response->json();
```

Python:

```python
import os
import requests

response = requests.post(
    "https://api.weavpayments.com/v1/checkouts",
    headers={
        "Authorization": f"Bearer {os.environ['WEAV_API_KEY']}",
    },
    json={
        "items": [
            {
                "name": "Monthly plan",
                "quantity": 1,
                "unit_amount": 499.00,
            },
        ],
        "external_reference_id": "SUB-USER-123-START",
        "channels": [
            "CARD",
        ],
        "save_payment_method": True,
        "customer": {
            "id": "USER-123",
            "name": "Juan dela Cruz",
        },
        "success_url": "https://shop.example.com/account/plan",
    },
    timeout=30,
)

result = response.json()
```

Go:

```go
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "strings"
)

func main() {
    body := strings.NewReader(`{
    "items": [
        {
            "name": "Monthly plan",
            "quantity": 1,
            "unit_amount": 499.00
        }
    ],
    "external_reference_id": "SUB-USER-123-START",
    "channels": [
        "CARD"
    ],
    "save_payment_method": true,
    "customer": {
        "id": "USER-123",
        "name": "Juan dela Cruz"
    },
    "success_url": "https://shop.example.com/account/plan"
}`)

    req, err := http.NewRequest("POST", "https://api.weavpayments.com/v1/checkouts", body)
    if err != nil {
        panic(err)
    }
    req.Header.Set("Authorization", "Bearer "+os.Getenv("WEAV_API_KEY"))
    req.Header.Set("Content-Type", "application/json")

    res, err := http.DefaultClient.Do(req)
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()

    result, err := io.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }
    fmt.Println(res.Status, string(result))
}
```

Rust:

```rust
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }, serde_json = "1"
use serde_json::json;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let key = std::env::var("WEAV_API_KEY")?;
    let body = json!({
        "items": [
            {
                "name": "Monthly plan",
                "quantity": 1,
                "unit_amount": 499.00
            }
        ],
        "external_reference_id": "SUB-USER-123-START",
        "channels": [
            "CARD"
        ],
        "save_payment_method": true,
        "customer": {
            "id": "USER-123",
            "name": "Juan dela Cruz"
        },
        "success_url": "https://shop.example.com/account/plan"
    });

    let response = reqwest::blocking::Client::new()
        .post("https://api.weavpayments.com/v1/checkouts")
        .bearer_auth(key)
        .json(&body)
        .send()?;

    let status = response.status();
    let result: serde_json::Value = response.json()?;
    println!("{status} {result:#}");
    Ok(())
}
```

Java:

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class WeavExample {
    public static void main(String[] args) throws Exception {
        String url = "https://api.weavpayments.com/v1/checkouts";
        String body =
                """
                {
                    "items": [
                        {
                            "name": "Monthly plan",
                            "quantity": 1,
                            "unit_amount": 499.00
                        }
                    ],
                    "external_reference_id": "SUB-USER-123-START",
                    "channels": [
                        "CARD"
                    ],
                    "save_payment_method": true,
                    "customer": {
                        "id": "USER-123",
                        "name": "Juan dela Cruz"
                    },
                    "success_url": "https://shop.example.com/account/plan"
                }
                """;

        HttpRequest request =
                HttpRequest.newBuilder()
                        .uri(URI.create(url))
                        .header("Authorization", "Bearer " + System.getenv("WEAV_API_KEY"))
                        .header("Content-Type", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(body))
                        .build();

        HttpResponse<String> response =
                HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}
```
