# API — magento-shippingfields

> **Last verified:** 2026-02-26 (Europe/London)
> **Owner:** Engineering

All externally consumed API behaviour must be defined in this document. Undocumented behaviour is undefined and unsupported.

---

## REST API

### Save Shipping Fields

| Attribute | Value |
|-----------|-------|
| Route | `POST /V1/carts/mine/save-shipping-fields` |
| Authentication | Anonymous (guest and logged-in customers) |
| Service | `PortalPeople\ShippingFields\Api\ShippingFieldsInterface::saveShippingFields()` |
| Implementation | `PortalPeople\ShippingFields\Model\ShippingFieldsManagement` |
| Scope | Frontend — saves to the active checkout session quote |

#### Request Body

```json
{
  "shippingDate": "2026-03-15",
  "shippingMessage": "Please leave at front door"
}
```

| Parameter | Type | Required | Notes |
|-----------|------|----------|-------|
| `shippingDate` | string | `UNKNOWN — Requires Clarification` (mixin validates; layout marks optional) | Date string — format `YYYY-MM-DD` assumed |
| `shippingMessage` | string | No | Max 255 chars (DB column limit) |

#### Response

| Status | Body | Notes |
|--------|------|-------|
| 200 | `true` | Fields saved successfully |
| 200 | `false` | Fields not saved (error logged server-side) |

`UNKNOWN — Requires Clarification`: The API returns `bool` but does not return structured error information on failure. Frontend behaviour on `false` response is not documented.

---

### Request Lifecycle Diagram

```mermaid
sequenceDiagram
  %% Title: magento-shippingfields — Save Shipping Fields API (L3)
  %% Layer: L3
  %% Owner: Engineering
  %% Last verified: 2026-02-26 (Europe/London)
  %% Scope: Frontend REST call to save shipping fields on quote
  %% Excludes: Place order flow, admin edit

  autonumber
  participant JS as Checkout JS (Mixin)
  participant API as Magento REST Router
  participant Mgmt as ShippingFieldsManagement
  participant Session as Checkout Session
  participant Repo as CartRepository
  participant DB as quote DB

  JS->>API: POST /V1/carts/mine/save-shipping-fields
  API->>Mgmt: saveShippingFields(shippingDate, shippingMessage)
  Mgmt->>Session: getQuote()
  Session-->>Mgmt: Quote object
  Mgmt->>Mgmt: setData(shipping_date, shipping_message)
  Mgmt->>Repo: save(quote)
  Repo->>DB: Persist
  DB-->>Repo: OK
  Repo-->>Mgmt: Saved
  Mgmt-->>API: true
  API-->>JS: true
```

---

## Extension Attributes

Extension attributes are declared in `/modules/magento-shippingfields/src/etc/extension_attributes.xml`.

### On `Magento\Quote\Api\Data\CartInterface`

| Attribute | Type | Notes |
|-----------|------|-------|
| `shipping_date` | string | Preferred delivery date |
| `shipping_message` | string | Shipping instructions |

### On `Magento\Sales\Api\Data\OrderInterface`

| Attribute | Type | Notes |
|-----------|------|-------|
| `shipping_date` | string | Copied from quote on order submit |
| `shipping_message` | string | Copied from quote on order submit |

---

## Event Observer Contract

### `sales_model_service_quote_submit_before`

| Attribute | Value |
|-----------|-------|
| Observer class | `PortalPeople\ShippingFields\Observer\QuoteSubmitBefore` |
| Trigger | Magento core — fired before quote is converted to order |
| Action | Reads `shipping_date` and `shipping_message` from quote; writes them to the order object |
| Writes to | `sales_order` (via order object, persisted by Magento core) |

---

## Plugin Contracts

| Plugin | Target Interface | Method | Behaviour |
|--------|-----------------|--------|-----------|
| `ShippingInformationManagementPlugin` | `Magento\Checkout\Model\ShippingInformationManagement` | `beforeSaveAddressInformation` | Extracts `shipping_date` and `shipping_message` from request JSON; saves to quote |
| `QuoteRepositoryPlugin` | `Magento\Quote\Model\QuoteRepository` | `beforeSave`, `afterGet` | Syncs extension attributes ↔ quote data fields |
| `CartRepositoryPlugin` | `Magento\Quote\Model\CartRepository` | `beforeSave`, `afterGet` | Syncs extension attributes ↔ cart data fields |
| `OrderRepositoryPlugin` | `Magento\Sales\Model\OrderRepository` | `beforeSave`, `afterGet` | Syncs extension attributes ↔ order data fields; reads from request on `afterGet` |
| `QuoteToOrderPlugin` | `Magento\Quote\Model\Quote\Item\ToOrderItem` | `aroundConvert` | Transfers `shipping_date` and `shipping_message` from quote to order item on conversion |
| `PaymentInformationManagementPlugin` | `Magento\Checkout\Model\PaymentInformationManagement` | `beforeSavePaymentInformationAndPlaceOrder` | Debug logging only — no field capture |

---

## Admin HTTP Routes (Inbound)

### Update Shipping Fields

| Attribute | Value |
|-----------|-------|
| Route prefix | `/admin/shippingfields/` |
| Router ID | `admin` (from `src/etc/adminhtml/routes.xml`, `frontName="shippingfields"`) |
| Controller | `PortalPeople\ShippingFields\Controller\Adminhtml\Order\Update` |
| Method | POST |
| Action | Saves `shipping_date` and `shipping_message` for an existing order |
| ACL | `Magento_Sales::actions_edit` — confirmed in `Update::_isAllowed()` |

---

## Frontend Checkout Config

Provided via `DeliveryDateConfigProvider` to `window.checkoutConfig.portalpeopleShippingFields`:

| Key | Type | Notes |
|-----|------|-------|
| `disabled_weekdays` | array | Day-of-week integers disabled in date picker (from `portalpeople_disableddates` config) |
| `disabled_dates` | array | Specific disabled date strings (from `PortalPeople\DisabledDates` DB collection) |
| `lead_time` | array/string | Today + next 2 working days — earliest selectable dates |

---

## Confirmed Facts

- REST API is anonymous — no customer session or token required.
- API returns `bool` — no structured error body on failure.
- Extension attributes are used throughout to pass fields across Magento API boundaries.
- Admin route uses custom `shippingfields` frontName.

---

## Unknowns

1. `UNKNOWN — Requires Clarification`: Is `shippingDate` required by the mixin? The layout XML marks it optional but the mixin appears to validate its presence.
2. `UNKNOWN — Requires Clarification`: What is the exact date format expected by the backend? (`YYYY-MM-DD`, `DD/MM/YYYY`, or other?)
3. ACL resource confirmed: `Magento_Sales::actions_edit` — source-verified in `Update::_isAllowed()`.
4. `UNKNOWN — Requires Clarification`: What happens when the API returns `false`? Does the checkout block or continue?
5. `QuoteToOrderPlugin` confirmed dead code — removed from `di.xml`. `QuoteSubmitBefore` observer is the authoritative transfer path.

---

## Evidence

- `/modules/magento-shippingfields/src/etc/webapi.xml`
- `/modules/magento-shippingfields/src/etc/extension_attributes.xml`
- `/modules/magento-shippingfields/src/etc/events.xml`
- `/modules/magento-shippingfields/src/etc/di.xml`
- `/modules/magento-shippingfields/src/etc/adminhtml/routes.xml`
- `/modules/magento-shippingfields/src/Api/ShippingFieldsInterface.php`
- `/modules/magento-shippingfields/src/Model/ShippingFieldsManagement.php`
- `/modules/magento-shippingfields/src/Model/Checkout/DeliveryDateConfigProvider.php`
- `/modules/magento-shippingfields/src/Observer/QuoteSubmitBefore.php`
- `/modules/magento-shippingfields/src/Plugin/ShippingInformationManagementPlugin.php`
- `/modules/magento-shippingfields/src/Plugin/QuoteRepositoryPlugin.php`
- `/modules/magento-shippingfields/src/Plugin/CartRepositoryPlugin.php`
- `/modules/magento-shippingfields/src/Plugin/OrderRepositoryPlugin.php`
- `/modules/magento-shippingfields/src/Plugin/QuoteToOrderPlugin.php`
- `/modules/magento-shippingfields/src/Controller/Adminhtml/Order/Update.php`
- `/modules/magento-shippingfields/src/view/frontend/web/js/action/set-shipping-information-mixin.js`
- `/modules/magento-shippingfields/src/view/frontend/web/js/action/place-order-mixin.js`
