# Missio Shop API – Customer & Shop Module

Comprehensive reference for the public customer-facing endpoints that power the Missio Shop mobile and web clients. Every request is company-scoped via the `X-Host` header and most customer actions require Bearer authentication tokens issued by the auth endpoints below.

## Base URL & Environment Variables

| Variable | Description |
| --- | --- |
| `{{base_url}}` | Root domain of your Missio instance (e.g., `https://portal.yourmissio.com`). Do **not** include a trailing slash. |
| `{{x_host}}` | Company hash from the `companies` table. Mandatory for **all** endpoints. |
| `{{token}}` | Bearer access token captured from Signup/Login responses. |
| `{{cart_token}}` | Cart identifier returned by cart endpoints (guest or authenticated). |
| `{{payment_intent_id}}`, `{{client_secret}}` | Stripe identifiers captured during the payment flow. |

## Global Headers

- `X-Host: {{x_host}}` – company identifier, always required.
- `Authorization: Bearer {{token}}` – required for authenticated endpoints (profile, authenticated carts, checkout after login, payment confirmation).
- `Content-Type: application/json` – required whenever a JSON body is sent (POST, PUT, DELETE with payloads).

## Customer Authentication Flow

1. Retrieve the company hash (`X-Host`).
2. Call **Signup** (to create a customer) or **Login** with `X-Host`.
3. Persist the returned `access_token` (set as Postman collection `token`).
4. Pass `Authorization: Bearer {access_token}` for all protected endpoints.
5. Use **Refresh Token** to rotate tokens and **Logout** to revoke them.

## Customer Status Codes

- `1` – Active (can login and shop).
- `0` – Inactive (login returns HTTP 403 Forbidden).

---

# Endpoint Reference

Each endpoint lists method, path, purpose, required headers, body schema, and example responses copied from the provided Postman collection.

## 1. Authentication

### 1.1 Signup

- **Method/Path:** `POST /api/auth/signup`
- **Purpose:** Create a new customer profile and issue an access token.

**Headers**

| Header | Required | Example | Notes |
| --- | --- | --- | --- |
| `Content-Type` | Yes | `application/json` | JSON payload |
| `X-Host` | Yes | `{{x_host}}` | Company scope |

**Request Body (JSON)**

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `fname`, `lname` | string (≤100) | Yes | Customer name |
| `email` | string (email, ≤255) | Yes | Must be unique per company |
| `password` | string (≥6) | Yes | Plain text, hashed server-side |
| `username` | string (≤150) | Optional | Defaults to email |
| `phone` | string (≤50) | Optional | |
| `address`, `city`, `state`, `country`, `zip_code` | string | Optional | Defaults country to `US` if omitted |

**Sample Request**

```http
POST {{base_url}}/api/auth/signup
Content-Type: application/json
X-Host: {{x_host}}

{
	"fname": "John",
	"lname": "Doe",
	"email": "john.doe+{{$timestamp}}@example.com",
	"password": "12345678",
	"username": "johndoe{{$timestamp}}",
	"phone": "+1234567890",
	"address": "123 Main Street",
	"city": "Boston",
	"state": "MA",
	"country": "US",
	"zip_code": "02101"
}
```

**Success 200 Response**

```json
{
	"status": "success",
	"message": "Signup successfully",
	"access_token": "1|abcdef123456789...",
	"token_type": "Bearer",
	"customer": {
		"id": 1,
		"company_id": 5,
		"fname": "John",
		"lname": "Doe",
		"slug": "john-doe",
		"email": "john.doe@example.com",
		"username": "john.doe@example.com",
		"phone": "+1234567890",
		"status": "1",
		"email_verified_at": "2026-01-07 12:00:00",
		"created_at": "2026-01-07T12:00:00.000000Z",
		"updated_at": "2026-01-07T12:00:00.000000Z"
	},
	"company": {
		"id": 5,
		"name": "Acme Corporation"
	}
}
```

**Common Errors**

- `422 Unprocessable Entity` – Missing `X-Host` or validation failures (missing fields, weak password, invalid email).
- `500` – Server error.

### 1.2 Login

- **Method/Path:** `POST /api/auth/login`
- **Purpose:** Authenticate an existing customer using `email` or `username` plus password.

**Headers:** Same as Signup.

**Body Fields**

| Field | Required | Notes |
| --- | --- | --- |
| `email` or `username` | One required | Accepts either identifier |
| `password` | Yes | Plain text |

**Sample Request**

```http
POST {{base_url}}/api/auth/login
Content-Type: application/json
X-Host: {{x_host}}

{
	"email": "john.doe@example.com",
	"password": "secret123"
}
```

**Success 200 Response**

```json
{
	"status": "success",
	"message": "Login successful",
	"access_token": "2|xyz789...",
	"token_type": "Bearer",
	"customer": {
		"id": 1,
		"email": "john.doe@example.com",
		"fname": "John",
		"lname": "Doe"
	},
	"company": {
		"id": 5,
		"name": "Acme Corporation"
	}
}
```

**Errors**

- `401 Unauthorized` – Invalid credentials.
- `403 Forbidden` – Account inactive (`status = 0`).
- `422 Unprocessable Entity` – Missing `X-Host` or payload.

### 1.3 Refresh Token

- **Method/Path:** `POST /api/auth/refresh`
- **Purpose:** Revoke the current token and issue a new one.

**Headers**: `Authorization` + `X-Host` + `Content-Type`.

**Body:** Empty JSON body.

**Success 200 Response**

```json
{
	"status": "success",
	"message": "Token refreshed",
	"access_token": "3|newtoken123...",
	"token_type": "Bearer"
}
```

### 1.4 Logout

- **Method/Path:** `POST /api/auth/logout`
- **Purpose:** Revoke the current access token.
- **Headers:** Same as Refresh Token.
- **Body:** Empty.

**Response**

```json
{
	"status": "success",
	"message": "Logged out"
}
```

---

## 2. Profile

### 2.1 Get Profile

- **Method/Path:** `GET /api/me`
- **Purpose:** Retrieve the authenticated customer’s data.

**Headers:** `Authorization`, `X-Host`.

**Response**

```json
{
	"status": "success",
	"message": "Profile",
	"customer": {
		"id": 1,
		"company_id": 5,
		"fname": "John",
		"lname": "Doe",
		"slug": "john-doe",
		"email": "john.doe@example.com",
		"username": "johndoe",
		"phone": "+1234567890",
		"address": "123 Main Street",
		"city": "Boston",
		"state": "MA",
		"country": "US",
		"zip_code": "02101",
		"about": "Software developer",
		"status": "1",
		"email_verified_at": "2026-01-07 12:00:00",
		"created_at": "2026-01-07T12:00:00.000000Z",
		"updated_at": "2026-01-07T12:00:00.000000Z"
	}
}
```

### 2.2 Update Profile

- **Method/Path:** `PUT /api/me`
- **Purpose:** Update customer details. Send only the fields that change.

**Headers:** `Authorization`, `X-Host`, `Content-Type`.

**Body Example**

```json
{
	"fname": "John",
	"lname": "Smith",
	"phone": "+1987654321",
	"email": "john.doe+1771523095@example.com",
	"username": "johnsmith",
	"address": "456 Oak Avenue",
	"city": "Cambridge",
	"state": "MA",
	"country": "US",
	"zip_code": "02139",
	"about": "Full stack developer"
}
```

**Success Response**

```json
{
	"status": "success",
	"message": "Profile updated",
	"customer": {
		"id": 1,
		"fname": "John",
		"lname": "Smith",
		"phone": "+1987654321",
		"updated_at": "2026-01-07T14:30:00.000000Z"
	}
}
```

---

## 3. Catalog (Public, X-Host Required)

All catalog endpoints are company-scoped but do not require authentication tokens.

### 3.1 Categories

- **Method/Path:** `GET /api/catalog/categories`
- **Purpose:** Return the full category tree with nested children.
- **Headers:** `X-Host` only.

**Response Shape**

```json
{
	"status": "success",
	"categories": [
		{
			"id": 10,
			"name": "Daily Needs",
			"slug": "daily-needs",
			"image": "https://.../daily-needs-sm.jpg",
			"children": [
				{ "id": 11, "name": "Groceries", "children": [] }
			]
		}
	]
}
```

### 3.2 Products (List)

- **Method/Path:** `GET /api/catalog/products`
- **Purpose:** Paginated product listing with filters.
- **Query Parameters:**
	- `search` – full-text search on name/description.
	- `category_slug` / `category_id` – filter by category (slug includes descendants).
	- `min_price`, `max_price` – numeric filters.
	- `sort` – `name|date|price|price-desc|popularity`.
	- `page`, `per_page` – pagination (1–100 items per page).

**Response Highlights**

```json
{
	"status": "success",
	"products": {
		"data": [
			{
				"id": 990,
				"name": "Hybrid Bike",
				"slug": "bike",
				"price": 499.99,
				"images": {
					"sm": "https://.../bike-sm.jpg",
					"md": "https://.../bike-md.jpg",
					"lg": "https://.../bike-lg.jpg"
				},
				"categories": ["Daily Needs"],
				"views": 302
			}
		],
		"meta": {
			"current_page": 1,
			"per_page": 20
		}
	}
}
```

### 3.3 Product Detail

- **Method/Path:** `GET /api/catalog/products/{slug|id}`
- **Purpose:** Retrieve a single product with variants, all images, stock, pricing, and metadata.
- **Headers:** `X-Host` only.

**Example**

```http
GET {{base_url}}/api/catalog/products/bike
X-Host: {{x_host}}
```

**Response (trimmed)**

```json
{
	"status": "success",
	"product": {
		"id": 990,
		"name": "Hybrid Bike",
		"slug": "bike",
		"description": "...",
		"price": 499.99,
		"min_price": 399.99,
		"max_price": 549.99,
		"variants": [
			{
				"id": 1201,
				"sku": "BIKE-BLK-M",
				"attributes": { "Color": "Black", "Size": "M" },
				"images": ["https://.../bike-variant1.jpg"]
			}
		],
		"files": [],
		"categories": ["Daily Needs", "Bikes"],
		"stock": 25
	}
}
```

---

## 4. Cart

Cart endpoints work for both guests and authenticated customers. If no `Authorization` header is provided, the API manages a guest session via cookies and returns a `cart_token` that clients must persist.

### 4.1 Get Cart

- **Method/Path:** `GET /api/catalog/cart`
- **Headers:** `X-Host` (required), `Authorization` (optional).
- **Query:** `cart_token` (optional for guest carts).

**Response**

```json
{
	"status": "success",
	"data": {
		"cart_token": "8e6b...",
		"customer_id": 1,
		"items": [
			{
				"id": 123,
				"product_id": 990,
				"variant_id": 1201,
				"name": "Hybrid Bike",
				"sku": "BIKE-BLK-M",
				"quantity": 2,
				"unit_price": 499.99,
				"amount": 999.98,
				"attributes": {"Color": "Black", "Size": "M"},
				"image": "https://.../bike-sm.jpg"
			}
		],
		"coupon_code": null,
		"totals": {
			"subtotal": 999.98,
			"discount": 0,
			"tax": 0,
			"shipping": 0,
			"total": 999.98,
			"items_count": 2
		}
	}
}
```

### 4.2 Add Item to Cart

- **Method/Path:** `POST /api/catalog/cart/items`
- **Headers:** `X-Host`, `Content-Type`, optional `Authorization`.

**Request Body**

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `product_id` | integer | Yes | Must belong to company |
| `attributes` | object | Optional | Attribute ID → value ID mapping, used to resolve variants |
| `quantity` | integer | Optional | Defaults to 1 |
| `cart_token` | string | Optional | Guest carts reuse returned token |

**Sample**

```json
{
	"product_id": 990,
	"attributes": {
		"19": "674",
		"20": "1136"
	},
	"quantity": 2,
	"cart_token": ""
}
```

**Behavior Notes**

- Matching items (same product + attributes) are incremented unless a quantity is provided.
- Rejects mixing recurring vs one-time or `missio_owned` vs regular products.
- Returns updated cart details and `cart_token`.

### 4.3 Remove Cart Item / Clear Cart

- **Method/Path:** `DELETE /api/catalog/cart/items/{item_id}`
- **Headers:** `X-Host`, optional `Authorization`.
- **URL Parameter:** `item_id` (`all` clears entire cart).
- **Query Parameter:** `cart_token` (guest carts).

**Response** – Success payload with refreshed totals.

---

## 5. Checkout

### Save Checkout Form

- **Method/Path:** `POST /api/catalog/checkout`
- **Purpose:** Persist billing/shipping details (stored under `order_carts.checkoutData`).
- **Headers:** `X-Host`, `Content-Type`, optional `Authorization`.
- **Query:** `cart_token` (required for guest carts).

**Body Example**

```json
{
	"_token": "xxOsb97nIMwq3D5KmQiqyHFqPfBlENTPZSAN6Xts",
	"PhysicalProductinList": "1",
	"billing_first_name": "John",
	"billing_last_name": "Doe66",
	"billing_email": "john.doe@example.com",
	"billing_company_name": "",
	"billing_address": "123 Main Street",
	"billing_address2": "",
	"billing_city": "Boston",
	"billing_country": "US",
	"billing_state": "Arkansas",
	"billing_zip": "02101",
	"billing_phone": "(123) 456-7890",
	"shipping_first_name": "John",
	"shipping_last_name": "Doe66",
	"shipping_email": "john.doe@example.com",
	"shipping_company_name": "",
	"shipping_address": "123 Main Street",
	"shipping_address2": "",
	"shipping_city": "Boston",
	"shipping_country": "US",
	"shipping_state": "Arkansas",
	"shipping_zip": "02101",
	"shipping_phone": "(123) 456-7890"
}
```

**Response** – `status`, `data.cart_token`, and `data.next_step` (`"payment"`).

---

## 6. Payment Flow (Stripe)

### 6.1 Apply Coupon (Optional)

- **Method/Path:** `POST /api/shop/apply-coupon`
- **Headers:** `X-Host`, `Content-Type`, `Authorization` (recommended for customer tracking).

**Body**

```json
{
	"coupon_code": "SAVE10",
	"cart_token": ""
}
```

**Response**

```json
{
	"status": "success",
	"data": {
		"coupon_code": "SAVE10",
		"discount_type": "percentage",
		"discount_value": 10,
		"discount_amount": 99.99,
		"subtotal": 999.9,
		"new_total": 899.91
	}
}
```

### 6.2 Step 1 – Create Payment Intent

- **Method/Path:** `POST /api/shop/payment-intent`
- **Headers:** `X-Host`, `Authorization`, `Content-Type`.
- **Query:** `cart_token={{cart_token}}`.
- **Body:** `{}` (server calculates totals).

**Response**

```json
{
	"status": "success",
	"data": {
		"payment_intent_id": "pi_3PGJvHKx...",
		"client_secret": "pi_3PGJvHKx_secret_...",
		"amount": 89991
	}
}
```

Persist `payment_intent_id` and `client_secret` for the confirmation step / Stripe.js.

### 6.3 Step 2 – Confirm Payment

- **Method/Path:** `POST /api/shop/payment-confirm`
- **Headers:** `X-Host`, `Authorization`, `Content-Type`.
- **Body**

```json
{
	"payment_intent_id": "{{payment_intent_id}}",
	"cart_token": ""
}
```

**Response**

```json
{
	"status": "success",
	"data": {
		"order_id": 4521,
		"order_number": "ODR#005421",
		"transaction_id": "pi_3PGJvHKx..."
	}
}
```

### 6.4 Test Payment Confirmation (Postman Shortcut)

- **Method/Path:** `POST /api/shop/test-payment-confirm`
- **Purpose:** Automate Stripe test-card confirmation for API testing only.
- **Headers & Body:** Same as Step 2, but the server simulates confirmation using Stripe test card `4242 4242 4242 4242`.

**Response:** Identical to Step 2 (order + transaction info).

---

## Error Handling Reference

| HTTP Status | Scenario |
| --- | --- |
| `401 Unauthorized` | Missing/invalid Bearer token, expired token, or protected endpoint hit without auth. |
| `403 Forbidden` | Customer account inactive. |
| `422 Unprocessable Entity` | Missing `X-Host`, validation failures, malformed payloads. |
| `500 Internal Server Error` | Unexpected server issues (check logs). |

---

## Testing Tips

- Postman scripts in the collection automatically capture `access_token`, `payment_intent_id`, `client_secret`, and `cart_token` for reuse.
- For guest carts, store the `cart_token` returned from **Add Item** or **Get Cart** and pass it to checkout and payment endpoints.
- Use the **Test Payment Confirm** endpoint only in non-production environments; production mobile apps must call Stripe client SDKs to confirm payments before hitting `/payment-confirm`.

This document mirrors the flows defined in the `Missio Shop API – Complete` Postman collection and should be kept in sync with future collection updates.

