> ## Documentation Index
> Fetch the complete documentation index at: https://docs.publica.la/llms.txt
> Use this file to discover all available pages before exploring further.

# Sale Orders

Initiate Publica.la's checkout flow where customers pay directly through the platform. Orders start with `pending` status and transition to `approved` after successful payment via integrated payment gateways (Stripe, MercadoPago, etc.).

See [Choosing the Right Order Type](../orders-v3-overview.md#choosing-the-right-order-type) for guidance on when to use sale orders.

---

## Creating a Sale Order

### Endpoint

```
POST /api/v3/orders
```

### Request Body

```json
{
  "type": "sale",
  "external_id": "CART-12345",
  "return_url": "https://yoursite.com/thank-you",
  "unit_price": 29.99,
  "currency_id": "USD",
  "user": {
    "id": "customer-123",
    "email": "customer@example.com"
  },
  "products": [
    {
      "type": "content",
      "external_id": "9781234567890",
      "unit_price": 29.99,
      "currency_id": "USD"
    }
  ]
}
```

### Required Fields

| Field                    | Type   | Description                   |
| ------------------------ | ------ | ----------------------------- |
| `type`                   | string | Must be `"sale"`              |
| `return_url`             | string | URL to redirect after payment |
| `unit_price`             | float  | Total order amount            |
| `currency_id`            | string | ISO 4217 currency code        |
| `user`                   | object | User information              |
| `user.id`                | string | User external ID (required)   |
| `products`               | array  | Products in the order         |
| `products[].type`        | string | `content` or `subscription`   |
| `products[].external_id` | string | Product external ID           |
| `products[].unit_price`  | float  | Individual product price      |
| `products[].currency_id` | string | Product currency              |

### Optional Fields

| Field             | Type   | Description                               |
| ----------------- | ------ | ----------------------------------------- |
| `external_id`     | string | Your unique order ID (max 64 chars)       |
| `user.email`      | string | User email address                        |
| `products[].name` | string | Product name (required for auto-creation) |
| `products[].url`  | string | External URL (required for auto-creation) |

---

## External Content (Auto-Creation)

If you're selling content hosted outside Publica.la, you can create sale orders without pre-registering the product. When a `products[].external_id` doesn't exist, the system automatically creates a link-type product using the provided `name` and `url`.

This is useful when:

- Content is hosted on your own platform
- You want to track sales in Publica.la without uploading the actual content
- You need to maintain order history for external resources

### Example: External Content Sale

```bash
curl -X POST "https://yourstore.publica.la/api/v3/orders" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "sale",
    "return_url": "https://yourstore.com/confirmation",
    "unit_price": 19.99,
    "currency_id": "USD",
    "user": {
      "id": "user-123"
    },
    "products": [
      {
        "external_id": "EXTERNAL-COURSE-001",
        "type": "content",
        "name": "Advanced Photography Course",
        "url": "https://yourplatform.com/courses/photography",
        "unit_price": 19.99,
        "currency_id": "USD"
      }
    ]
  }'
```

The product will be created with `external_id: "EXTERNAL-COURSE-001"` and readers will be redirected to the provided URL.

---

## Request Examples

### Basic Sale Order (USD - Stripe)

```bash
curl -X POST "https://yourstore.publica.la/api/v3/orders" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "sale",
    "external_id": "WEB-ORDER-12345",
    "return_url": "https://yourstore.com/order/confirmation",
    "unit_price": 29.99,
    "currency_id": "USD",
    "user": {
      "id": "user-ext-789",
      "email": "customer@example.com"
    },
    "products": [
      {
        "external_id": "PREMIUM-EBOOK",
        "type": "content",
        "unit_price": 29.99,
        "currency_id": "USD"
      }
    ]
  }'
```

### Sale Order (ARS - MercadoPago)

```bash
curl -X POST "https://yourstore.publica.la/api/v3/orders" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "sale",
    "external_id": "ARS-ORDER-789",
    "return_url": "https://yourstore.com/gracias",
    "unit_price": 5000.00,
    "currency_id": "ARS",
    "user": {
      "id": "user-ar-456",
      "email": "cliente@ejemplo.com"
    },
    "products": [
      {
        "external_id": "LIBRO-DIGITAL",
        "type": "content",
        "unit_price": 5000.00,
        "currency_id": "ARS"
      }
    ]
  }'
```

### Sale Order with Subscription

```bash
curl -X POST "https://yourstore.publica.la/api/v3/orders" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "sale",
    "external_id": "SUB-ORDER-456",
    "return_url": "https://yourstore.com/subscription/welcome",
    "unit_price": 9.99,
    "currency_id": "USD",
    "user": {
      "id": "user-sub-123",
      "email": "subscriber@example.com"
    },
    "products": [
      {
        "external_id": "MONTHLY-PREMIUM",
        "type": "subscription",
        "unit_price": 9.99,
        "currency_id": "USD"
      }
    ]
  }'
```

### Sale Order with Multiple Products

```bash
curl -X POST "https://yourstore.publica.la/api/v3/orders" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "sale",
    "external_id": "CART-BUNDLE-001",
    "return_url": "https://yourstore.com/checkout/complete",
    "unit_price": 49.97,
    "currency_id": "USD",
    "user": {
      "id": "user-cart-123",
      "email": "shopper@example.com"
    },
    "products": [
      {
        "external_id": "EBOOK-001",
        "type": "content",
        "unit_price": 19.99,
        "currency_id": "USD"
      },
      {
        "external_id": "EBOOK-002",
        "type": "content",
        "unit_price": 14.99,
        "currency_id": "USD"
      },
      {
        "external_id": "EBOOK-003",
        "type": "content",
        "unit_price": 14.99,
        "currency_id": "USD"
      }
    ]
  }'
```

---

## Response

### Success Response (201 Created)

```json
{
  "data": {
    "id": "12345",
    "uuid": "4db50ffb-80fd-42b1-9bdb-c572e1a5487f",
    "external_id": "WEB-ORDER-12345",
    "type": "sale",
    "status": "pending",
    "unit_price": 29.99,
    "currency_id": "USD",
    "created_at": "2025-12-03T22:44:07.000000Z",
    "updated_at": "2025-12-03T22:44:07.000000Z"
  }
}
```

:::warning Pending Status
Sale orders are created with `status: pending`. The order transitions to `approved` only after the user completes payment.
:::

### Response with Checkout URL

Request the checkout URL with `?include=pending_checkout`:

```json
{
  "data": {
    "id": "12345",
    "uuid": "4db50ffb-80fd-42b1-9bdb-c572e1a5487f",
    "external_id": "WEB-ORDER-12345",
    "type": "sale",
    "status": "pending",
    "unit_price": 29.99,
    "currency_id": "USD",
    "created_at": "2025-12-03T22:44:07.000000Z",
    "updated_at": "2025-12-03T22:44:07.000000Z",
    "pending_checkout": {
      "url": "https://yourstore.publica.la/auth/token?external-auth-token=eyJ0eXAiOiJKV1QiLCJhbGc...",
      "ttl": 3600
    }
  }
}
```

### Checkout Object Fields

| Field                  | Type    | Description                                         |
| ---------------------- | ------- | --------------------------------------------------- |
| `pending_checkout.url` | string  | Redirect user here to complete payment              |
| `pending_checkout.ttl` | integer | Seconds until URL expires (typically 3600 = 1 hour) |

:::tip Checkout URL
You **MUST redirect** the user to `pending_checkout.url` to complete the payment. The URL contains an authentication token that expires after the TTL period.
:::

---

## Checkout Flow

```
1. Your App                    2. Publica.la                3. Payment Gateway
   |                              |                              |
   |--- POST /api/v3/orders ----->|                              |
   |<--- pending_checkout.url ----|                              |
   |                              |                              |
   |--- Redirect user ----------->|                              |
   |                              |--- Payment redirect -------->|
   |                              |                              |
   |                              |<--- Payment callback --------|
   |                              |                              |
   |<--- Redirect to return_url --|                              |
   |                              |                              |
   |--- GET /api/v3/orders/{id} ->|                              |
   |<--- status: approved --------|                              |
```

### Flow Steps

1. **Create order** via API
2. **Receive** `pending_checkout.url` in response
3. **Redirect** user to `pending_checkout.url`
4. User completes payment through gateway
5. User **redirected** to your `return_url`
6. **Verify** order status via API

### Return URL Parameters

After payment, user is redirected to your `return_url` with query parameters:

```
https://yourstore.com/thank-you?order_id=4db50ffb-80fd-42b1-9bdb-c572e1a5487f&status=approved
```

| Parameter  | Description                                          |
| ---------- | ---------------------------------------------------- |
| `order_id` | Publica.la order UUID                                |
| `status`   | Order status (`approved`, `pending`, or `cancelled`) |

---

## Payment Gateways

The payment gateway is automatically selected based on `currency_id`:

| Currency | Gateway                | Supported Countries |
| -------- | ---------------------- | ------------------- |
| **USD**  | Stripe                 | Global              |
| **EUR**  | Stripe                 | Global              |
| **ARS**  | MercadoPago            | Argentina           |
| **MXN**  | MercadoPago            | Mexico              |
| **BRL**  | MercadoPago            | Brazil              |
| **CLP**  | Flow (via MercadoPago) | Chile               |
| **COP**  | PayU                   | Colombia            |

:::info Gateway Configuration
Gateway availability depends on your tenant configuration. Contact support if you need to enable specific gateways.
:::

---

## Order Status Lifecycle

```
  [Create] ─────► pending ──────[User Pays]──────► approved
                    │                                  │
                    └──────[Expires/Cancel]──────► cancelled
```

| Status      | Description                         |
| ----------- | ----------------------------------- |
| `pending`   | Order created, awaiting payment     |
| `approved`  | Payment successful, user has access |
| `cancelled` | Order cancelled or payment failed   |

---

## Managing Sale Orders

### Check Order Status

```bash
curl -X GET "https://yourstore.publica.la/api/v3/orders/4db50ffb-80fd-42b1-9bdb-c572e1a5487f" \
  -H "X-User-Token: your-api-token"
```

### List Pending Orders

```bash
curl -X GET "https://yourstore.publica.la/api/v3/orders?filter[type]=sale&filter[status]=pending" \
  -H "X-User-Token: your-api-token"
```

### Get Checkout URL for Pending Order

If you need to resend the checkout URL:

```bash
curl -X GET "https://yourstore.publica.la/api/v3/orders/4db50ffb-80fd-42b1-9bdb-c572e1a5487f?include=pending_checkout" \
  -H "X-User-Token: your-api-token"
```

### Update Approved Order Expiration

Once approved, you can update expiration:

```bash
curl -X PUT "https://yourstore.publica.la/api/v3/orders/4db50ffb-80fd-42b1-9bdb-c572e1a5487f" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "expiration_date": "2026-12-31"
  }'
```

### Cancel Order

```bash
curl -X DELETE "https://yourstore.publica.la/api/v3/orders/4db50ffb-80fd-42b1-9bdb-c572e1a5487f" \
  -H "X-User-Token: your-api-token" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "Customer requested cancellation"
  }'
```

---

## Email Notifications

:::info Automatic Notifications
When a sale order is successfully paid and approved, an email notification is automatically sent to the user confirming their purchase and providing access details. To disable automatic emails, go to your dashboard at `/dashboard/settings#notifications`.
:::

---

## Error Handling

### Common Errors

| Status | Error               | Cause                                            |
| ------ | ------------------- | ------------------------------------------------ |
| 401    | Unauthenticated     | Invalid or missing API token                     |
| 404    | Product not found   | Product external ID doesn't exist                |
| 422    | user.id required    | Sale orders require user.id                      |
| 422    | user.email required | Sale orders require user.email                   |
| 422    | Currency mismatch   | Product currencies don't match order currency    |
| 422    | Invalid currency    | Currency not supported or gateway not configured |

### Error Response Example

```json
{
  "message": "The given data was invalid.",
  "errors": {
    "user.id": ["The user.id field is required."],
    "user.email": ["The user.email field is required."]
  }
}
```

---

## Best Practices

1. **Store checkout URLs** - Cache the checkout URL in case user needs to retry
2. **Verify status** - Always verify order status after return, don't trust URL params alone
3. **Handle expiration** - Checkout URLs expire; be prepared to request a new one
4. **Set appropriate return_url** - Include order ID in return URL for easy lookup

---

## See Also

- [Permission Orders](./orders-v3-permission.md) - Access without payment
- [Report Orders](./orders-v3-report.md) - External sales with payment records
- [Update & Cancel](../orders-v3-update-cancel.md) - Manage existing orders
- [Get Order](../orders-v3-get.md) - Retrieve order details
