# YoPlanning Payment Manager — API Documentation

### `POST /api/create-payment`

Creates a payment link (Stripe) and returns a URL to redirect the customer to.

**Base URL** : `https://payment.yoplanning.pro`

***

### Authentication

The Payment Manager uses **token-based authentication**, separate from the main YoPlanning API token.

```
Authorization: Token <PAYMENT_MANAGER_TOKEN>
Content-Type: application/json
```

> **Important** : The Payment Manager token is **not** the same as the YoPlanning API v3.1 token. They are two distinct tokens. Using the wrong one will return `401 "Invalid token."`.

| Token                     | Used for                                                 | Where to find it                           |
| ------------------------- | -------------------------------------------------------- | ------------------------------------------ |
| YoPlanning API Token      | `yoplanning.pro/api/v3.1/*` (availability, orders, etc.) | Back-office > API > Tokens                 |
| **Payment Manager Token** | `payment.yoplanning.pro/api/*`                           | Back-office > Payment settings > API Token |

***

### Request

```
POST https://payment.yoplanning.pro/api/create-payment
Authorization: Token <PAYMENT_MANAGER_TOKEN>
Content-Type: application/json
```

#### Body parameters

| Field                         | Required | Type          | Description                                                                                 |
| ----------------------------- | -------- | ------------- | ------------------------------------------------------------------------------------------- |
| `order_id`                    | Yes      | string        | Your order identifier (typically the UUID returned by `/order-validation`)                  |
| `vendor_id`                   | Yes      | string (UUID) | Your vendor identifier (found in your YoPlanning payment settings)                          |
| `price`                       | Yes      | number        | Total amount in the currency's main unit (e.g. `196.00` for 196 euros). **Not in cents.**   |
| `currency`                    | Yes      | string        | ISO 4217 currency code (e.g. `"EUR"`, `"USD"`)                                              |
| `callback_url`                | Yes      | string (URL)  | IPN notification URL — must be publicly accessible. Called via POST when payment completes. |
| `redirection_url`             | No       | string (URL)  | Where to redirect the customer after a successful payment                                   |
| `cancel_url`                  | No       | string (URL)  | Where to redirect the customer if they cancel                                               |
| `payer_email`                 | No       | string        | Pre-fills the email field on the payment page                                               |
| `cardholder_first_name`       | No       | string        | Pre-fills the cardholder first name                                                         |
| `cardholder_last_name`        | No       | string        | Pre-fills the cardholder last name                                                          |
| `billing_address_line1`       | No       | string        | Billing address line 1                                                                      |
| `billing_address_line2`       | No       | string        | Billing address line 2                                                                      |
| `billing_address_city`        | No       | string        | City                                                                                        |
| `billing_address_postal_code` | No       | string        | Postal code                                                                                 |
| `billing_address_country`     | No       | string        | Country                                                                                     |
| `billing_address_state`       | No       | string        | State / Region                                                                              |

#### Important notes

* **The field is called `price`, not `amount`.** Sending `amount` instead of `price` will result in a `500 Internal Server Error` because the server receives `price=None`.
* **`price` is in euros (or your currency), not in cents.** Send `196.00`, not `19600`.
* **`callback_url` is required**, even if you don't actively process IPN notifications. Omitting it returns a validation error.
* The Payment Manager is a **flat payment link service** — it takes a total price, not a line-item breakdown. Order items belong to the YoPlanning booking API (`/order-validation`), not here.

***

### Response

#### Success (`200`)

```
{
  "success": true,
  "payment_id": "179384e3-4e32-4f03-b6ea-2fdc998e2c6c",
  "customer_id": null,
  "vakario_fee": "0.00",
  "payment_solution": "Stripe",
  "payment_url": "https://payment.yoplanning.pro/pay/8edf6170-5495-432d-8b04-6717ccb8ad68"
}
```

| Field              | Type           | Description                                               |
| ------------------ | -------------- | --------------------------------------------------------- |
| `success`          | boolean        | `true` if the payment link was created                    |
| `payment_id`       | string (UUID)  | Unique payment identifier                                 |
| `customer_id`      | string or null | Customer ID if recognized                                 |
| `vakario_fee`      | string         | Platform fee (decimal string)                             |
| `payment_solution` | string         | Payment provider used (`"Stripe"`)                        |
| `payment_url`      | string (URL)   | **Redirect the customer to this URL to complete payment** |

#### Validation error (`400`)

```
{
  "success": false,
  "errors": {
    "callback_url": ["This field is required."]
  }
}
```

#### Authentication error (`401`)

No `Authorization` header:

```
{"detail": "Authentication credentials were not provided."}
```

Wrong token (e.g. using the YoPlanning API token instead of the Payment Manager token):

```
{"detail": "Invalid token."}
```

#### Server error (`500`)

Returns an HTML error page (not JSON) with the message *"The payment platform is down for a moment."*

This is **not** an actual platform outage. In practice, 500 errors are caused by:

| Root cause                      | Server-side error                                                                                  | Fix                                                                                     |
| ------------------------------- | -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `price` field missing or `null` | `TypeError: '>' not supported between instances of 'NoneType' and 'int'` in `checkPaymentSolution` | Ensure `price` is a number in the JSON body. The field is called `price`, not `amount`. |
| `vendor_id` missing or invalid  | Various Django form errors                                                                         | Ensure `vendor_id` is a valid UUID string                                               |

> The 500 response returns HTML, not JSON. Parse defensively if you expect JSON.

***

### IPN Callback

When the payment is completed (or fails), the Payment Manager sends a `POST` request to your `callback_url`:

```
{
  "success": true,
  "payed": true,
  "order_id": "your-order-id",
  "payment_solution": "Stripe",
  "payer_lang": "fr"
}
```

| Field              | Type    | Description                                         |
| ------------------ | ------- | --------------------------------------------------- |
| `success`          | boolean | Whether the payment succeeded                       |
| `payed`            | boolean | Whether the payment was collected                   |
| `order_id`         | string  | The `order_id` you passed when creating the payment |
| `payment_solution` | string  | Provider used                                       |
| `payer_lang`       | string  | Language of the payer's browser                     |

Requirements for `callback_url`:

* Must be publicly accessible (no authentication)
* Must accept POST requests
* Must return a 2xx status code

***

### Complete example

#### cURL

```
curl -X POST "https://payment.yoplanning.pro/api/create-payment" \
  -H "Authorization: Token YOUR_PAYMENT_MANAGER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "order_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "vendor_id": "your-vendor-uuid",
    "price": 196.00,
    "currency": "EUR",
    "payer_email": "customer@example.com",
    "cardholder_first_name": "Jean",
    "cardholder_last_name": "Dupont",
    "callback_url": "https://yoursite.com/api/payment-callback",
    "redirection_url": "https://yoursite.com/confirmation?status=success",
    "cancel_url": "https://yoursite.com/confirmation?status=cancelled"
  }'
```

#### JavaScript (fetch)

```
const response = await fetch('https://payment.yoplanning.pro/api/create-payment', {
  method: 'POST',
  headers: {
    'Authorization': 'Token YOUR_PAYMENT_MANAGER_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    order_id: orderId,
    vendor_id: 'your-vendor-uuid',
    price: 196.00,       // euros, NOT cents
    currency: 'EUR',
    payer_email: 'customer@example.com',
    callback_url: 'https://yoursite.com/api/payment-callback',
    redirection_url: 'https://yoursite.com/confirmation?status=success',
    cancel_url: 'https://yoursite.com/confirmation?status=cancelled',
  }),
});
​
const data = await response.json();
​
if (data.success && data.payment_url) {
  // Redirect customer to payment page
  window.open(data.payment_url, '_blank');
}
```

#### Python (requests)

```
import requests
​
response = requests.post(
    "https://payment.yoplanning.pro/api/create-payment",
    headers={
        "Authorization": "Token YOUR_PAYMENT_MANAGER_TOKEN",
        "Content-Type": "application/json",
    },
    json={
        "order_id": order_id,
        "vendor_id": "your-vendor-uuid",
        "price": 196.00,
        "currency": "EUR",
        "payer_email": "customer@example.com",
        "callback_url": "https://yoursite.com/api/payment-callback",
        "redirection_url": "https://yoursite.com/confirmation?status=success",
        "cancel_url": "https://yoursite.com/confirmation?status=cancelled",
    },
)
​
data = response.json()
payment_url = data.get("payment_url")
```

***

### Typical integration flow

```
1. Customer selects dates and products
         │
         ▼
2. Check availability
   GET yoplanning.pro/api/v3.1/teams/{team}/online-products/{product}/availabilities/
         │
         ▼
3. Validate the order
   POST yoplanning.pro/api/v3.1/teams/{team}/order-validation
   → Returns order ID
         │
         ▼
4. Create payment link
   POST payment.yoplanning.pro/api/create-payment
   → Returns payment_url
         │
         ▼
5. Redirect customer to payment_url (Stripe checkout)
         │
         ▼
6. Customer pays → redirected to redirection_url
   Payment Manager POSTs to callback_url (IPN)
```

***

### CORS

The Payment Manager API does **not** return CORS headers. Direct calls from a browser will be blocked by the browser's same-origin policy.

**Solutions** :

* Use a server-side proxy (e.g. Cloudflare Worker, Node.js backend, serverless function) to relay requests
* Call the API from your backend, not from client-side JavaScript

***

### FAQ

**Q: I get `401 "Invalid token"` but my token works on the YoPlanning API.** A: The Payment Manager has its own token, separate from the YoPlanning API v3.1 token. Check your payment settings in the back-office for the correct token.

**Q: I get a `500` HTML error page instead of JSON.** A: This almost always means a required field is missing or `null`. Check that `price` (not `amount`) is a number in your request body. Also verify `vendor_id` is present and valid.

**Q: My `callback_url` isn't reachable yet. Can I skip it?** A: No, `callback_url` is required. You can point it to a placeholder URL that returns 200, but the field must be present.

**Q: Do I need to pass order line items to the Payment Manager?** A: No. The Payment Manager only handles payment — it takes a flat `price`. Line items belong to the YoPlanning order API (`/order-validation`).

**Q: Is `price` in cents or in the currency's main unit?** A: Main unit. For EUR, send `196.00`, not `19600`.
