Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 60 additions & 5 deletions docs/specification/embedded-checkout.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,11 +492,11 @@ for `postMessage()` calls — before the `ec.ready` message is sent.
Core messages are defined by the ECP specification and **MUST** be supported by
all implementations. All messages are sent from Embedded Checkout to host.

| Category | Purpose | Pattern | Core Messages |
| :--------------- | :------------------------------------------------------ | :----------- | :----------------------------------------------------------------------------------- |
| **Handshake** | Establish connection between host and Embedded Checkout | Request | `ec.ready` |
| **Lifecycle** | Inform of checkout state transitions | Notification | `ec.start`, `ec.complete` |
| **State Change** | Inform of checkout field changes | Notification | `ec.line_items.change`, `ec.buyer.change`, `ec.payment.change`, `ec.messages.change` |
| Category | Purpose | Pattern | Core Messages |
| :--------------- | :------------------------------------------------------ | :----------- | :------------------------------------------------------------------------------------------------------- |
| **Handshake** | Establish connection between host and Embedded Checkout | Request | `ec.ready` |
| **Lifecycle** | Inform of checkout state transitions | Notification | `ec.start`, `ec.complete` |
| **State Change** | Inform of checkout field changes | Notification | `ec.line_items.change`, `ec.buyer.change`, `ec.payment.change`, `ec.messages.change`, `ec.totals.change` |

#### Extension Messages

Expand Down Expand Up @@ -825,6 +825,61 @@ informational notices about the checkout state.
}
```

#### `ec.totals.change`

Checkout totals have been updated. This message covers all total line changes
including taxes, fees, discounts, and fulfillment costs — many of which have no
other domain-specific change message. Businesses **MUST** send this message
whenever `checkout.totals` changes for any reason.

When a change also triggers a domain-specific message (e.g.,
`ec.line_items.change`, `ec.buyer.change`, or `ec.payment.change`), the business
**MUST** send the domain-specific message first, then follow it with
`ec.totals.change`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naive QQ: I understand this is nice from a consistency perspective, but is there any additional benefits from mandating business to MUST send both notifications if the domain-specific message also has its own mandate to send the entire latest checkout state (which naturally encapsulates ec.totals.change)? It feels like we are going to be exchanging the same checkout object multiple times throughout the embedded checkout's lifecycle (especially if we are going to mirror this pattern for other EP bindings like Embedded Cart, where it's possible many line_item changes will occur).

Should we loosen the guideline to MAY (or SHOULD) or do the opposite where if a domain-specific message exist, then that must be the one message sent back to host with the updated totals?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this internally too, as were also a little worried by the amount of data flying over the wire for these changes. In the end, I think this contract is still right, though — it is weird for only some changes to totals to be communicated with this message, but not others, and I felt uncomfortable with the implementation instructions for both host and embedded checkout when I tried writing docs for a version that deduplicated ec.totals.change with other messages.


- **Direction:** Embedded Checkout → host
- **Type:** Notification
- **Payload:**
- `checkout`: The latest state of the checkout

**Example Message:**

```json
{
"jsonrpc": "2.0",
"method": "ec.totals.change",
"params": {
"checkout": {
"id": "checkout_123",
// The entire checkout object is provided, including the updated totals
"totals": [
{
"type": "subtotal",
"display_text": "Subtotal",
"amount": 4000
},
{
"type": "fulfillment",
"display_text": "Shipping",
"amount": 599
},
{
"type": "tax",
"display_text": "Tax",
"amount": 382
},
{
"type": "total",
"display_text": "Total",
"amount": 4981
}
]
// ...
}
}
}
```

#### `ec.payment.change`

Payment state has been updated. See [`ec.payment.change`](#ecpaymentchange) for
Expand Down
16 changes: 16 additions & 0 deletions source/services/shopping/embedded.openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,22 @@
]
},

{
"name": "ec.totals.change",
"summary": "Checkout totals changed",
"description": "Merchant notifies host that checkout.totals has changed. Covers all total line updates including taxes, fees, discounts, and fulfillment costs. For changes that also affect a specific domain (e.g., fulfillment cost update), this message MUST be sent after the domain-specific change message (e.g., ec.fulfillment.change).",
"params": [
{
"name": "checkout",
"required": true,
"schema": {
"$ref": "../../schemas/shopping/checkout.json",
"description": "Current checkout state with updated totals."
}
}
]
},

{
"name": "ec.payment.change",
"summary": "Payment state changed",
Expand Down
Loading