PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.3
WooCommerce v10.9.3
10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / StoreApi / Schemas / V1 / Agentic / CheckoutSessionSchema.php
woocommerce / src / StoreApi / Schemas / V1 / Agentic Last commit date
CheckoutSessionSchema.php 7 months ago
CheckoutSessionSchema.php
876 lines
1 <?php
2 /**
3 * CheckoutSessionSchema class.
4 *
5 * @package Automattic\WooCommerce\StoreApi\Schemas\V1\Agentic
6 */
7
8 declare(strict_types=1);
9 namespace Automattic\WooCommerce\StoreApi\Schemas\V1\Agentic;
10
11 use Automattic\WooCommerce\StoreApi\Routes\V1\Agentic\Enums\SessionKey;
12 use Automattic\WooCommerce\Internal\Agentic\Enums\Specs\CheckoutSessionStatus;
13 use Automattic\WooCommerce\Internal\Agentic\Enums\Specs\MessageType;
14 use Automattic\WooCommerce\Internal\Agentic\Enums\Specs\MessageContentType;
15 use Automattic\WooCommerce\Internal\Agentic\Enums\Specs\FulfillmentType;
16 use Automattic\WooCommerce\Internal\Agentic\Enums\Specs\TotalType;
17 use Automattic\WooCommerce\Internal\Agentic\Enums\Specs\LinkType;
18 use Automattic\WooCommerce\Internal\Agentic\Enums\Specs\PaymentMethod;
19 use Automattic\WooCommerce\StoreApi\Schemas\V1\AbstractSchema;
20 use Automattic\WooCommerce\StoreApi\Routes\V1\Agentic\AgenticCheckoutSession;
21 use Automattic\WooCommerce\StoreApi\Utilities\AgenticCheckoutUtils;
22 use Automattic\WooCommerce\StoreApi\Utilities\CartTokenUtils;
23 use Automattic\WooCommerce\StoreApi\Utilities\DraftOrderTrait;
24 use WC_Order;
25
26 /**
27 * Handles the schema for Agentic Checkout API checkout sessions.
28 * This schema formats WooCommerce cart/order data according to the
29 * Agentic Commerce Protocol specification.
30 *
31 * @internal The specification for agentic requests is subject to abrupt changes; backwards compatibility cannot be guaranteed.
32 */
33 class CheckoutSessionSchema extends AbstractSchema {
34 use DraftOrderTrait;
35
36 /**
37 * The schema item name.
38 *
39 * @var string
40 */
41 protected $title = 'agentic_checkout_session';
42
43 /**
44 * The schema item identifier.
45 *
46 * @var string
47 */
48 const IDENTIFIER = 'agentic-checkout-session';
49
50 /**
51 * Checkout session schema properties.
52 *
53 * @return array
54 */
55 public function get_properties() {
56 return [
57 'id' => [
58 'description' => __( 'Unique identifier for the checkout session.', 'woocommerce' ),
59 'type' => 'string',
60 'context' => [ 'view', 'edit' ],
61 'readonly' => true,
62 ],
63 'buyer' => [
64 'description' => __( 'Buyer information.', 'woocommerce' ),
65 'type' => [ 'object', 'null' ],
66 'context' => [ 'view', 'edit' ],
67 'properties' => [
68 'first_name' => [
69 'description' => __( 'First name.', 'woocommerce' ),
70 'type' => 'string',
71 ],
72 'last_name' => [
73 'description' => __( 'Last name.', 'woocommerce' ),
74 'type' => 'string',
75 ],
76 'email' => [
77 'description' => __( 'Email address.', 'woocommerce' ),
78 'type' => 'string',
79 ],
80 'phone_number' => [
81 'description' => __( 'Phone number.', 'woocommerce' ),
82 'type' => 'string',
83 ],
84 ],
85 ],
86 'payment_provider' => [
87 'description' => __( 'Payment provider information.', 'woocommerce' ),
88 'type' => [ 'object', 'null' ],
89 'context' => [ 'view', 'edit' ],
90 'properties' => [
91 'provider' => [
92 'description' => __( 'Payment provider identifier.', 'woocommerce' ),
93 'type' => 'string',
94 ],
95 'supported_payment_methods' => [
96 'description' => __( 'List of supported payment methods.', 'woocommerce' ),
97 'type' => 'array',
98 'items' => [
99 'type' => 'string',
100 ],
101 ],
102 ],
103 ],
104 'status' => [
105 'description' => __( 'Status of the checkout session.', 'woocommerce' ),
106 'type' => 'string',
107 'context' => [ 'view', 'edit' ],
108 'enum' => [
109 CheckoutSessionStatus::NOT_READY_FOR_PAYMENT,
110 CheckoutSessionStatus::READY_FOR_PAYMENT,
111 CheckoutSessionStatus::COMPLETED,
112 CheckoutSessionStatus::CANCELED,
113 ],
114 'readonly' => true,
115 ],
116 'currency' => [
117 'description' => __( 'Currency code (ISO 4217).', 'woocommerce' ),
118 'type' => 'string',
119 'context' => [ 'view', 'edit' ],
120 'readonly' => true,
121 ],
122 'line_items' => [
123 'description' => __( 'Line items in the checkout session.', 'woocommerce' ),
124 'type' => 'array',
125 'context' => [ 'view', 'edit' ],
126 'items' => [
127 'type' => 'object',
128 'properties' => [
129 'id' => [
130 'description' => __( 'Line item ID.', 'woocommerce' ),
131 'type' => 'string',
132 ],
133 'item' => [
134 'description' => __( 'Product item details.', 'woocommerce' ),
135 'type' => 'object',
136 'properties' => [
137 'id' => [
138 'description' => __( 'Product ID.', 'woocommerce' ),
139 'type' => 'string',
140 ],
141 'quantity' => [
142 'description' => __( 'Quantity.', 'woocommerce' ),
143 'type' => 'integer',
144 ],
145 ],
146 ],
147 'base_amount' => [
148 'description' => __( 'Base amount in cents.', 'woocommerce' ),
149 'type' => 'integer',
150 ],
151 'discount' => [
152 'description' => __( 'Discount amount in cents.', 'woocommerce' ),
153 'type' => 'integer',
154 ],
155 'subtotal' => [
156 'description' => __( 'Subtotal in cents.', 'woocommerce' ),
157 'type' => 'integer',
158 ],
159 'tax' => [
160 'description' => __( 'Tax amount in cents.', 'woocommerce' ),
161 'type' => 'integer',
162 ],
163 'total' => [
164 'description' => __( 'Total amount in cents.', 'woocommerce' ),
165 'type' => 'integer',
166 ],
167 ],
168 ],
169 ],
170 'fulfillment_address' => [
171 'description' => __( 'Fulfillment/shipping address.', 'woocommerce' ),
172 'type' => [ 'object', 'null' ],
173 'context' => [ 'view', 'edit' ],
174 'properties' => [
175 'name' => [
176 'description' => __( 'Full name.', 'woocommerce' ),
177 'type' => 'string',
178 ],
179 'line_one' => [
180 'description' => __( 'Address line 1.', 'woocommerce' ),
181 'type' => 'string',
182 ],
183 'line_two' => [
184 'description' => __( 'Address line 2.', 'woocommerce' ),
185 'type' => [ 'string', 'null' ],
186 ],
187 'city' => [
188 'description' => __( 'City.', 'woocommerce' ),
189 'type' => 'string',
190 ],
191 'state' => [
192 'description' => __( 'State/province.', 'woocommerce' ),
193 'type' => 'string',
194 ],
195 'country' => [
196 'description' => __( 'Country code (ISO 3166-1 alpha-2).', 'woocommerce' ),
197 'type' => 'string',
198 ],
199 'postal_code' => [
200 'description' => __( 'Postal/ZIP code.', 'woocommerce' ),
201 'type' => 'string',
202 ],
203 ],
204 ],
205 'fulfillment_options' => [
206 'description' => __( 'Available fulfillment options.', 'woocommerce' ),
207 'type' => 'array',
208 'context' => [ 'view', 'edit' ],
209 'items' => [
210 'type' => 'object',
211 'properties' => [
212 'type' => [
213 'description' => __( 'Fulfillment type.', 'woocommerce' ),
214 'type' => 'string',
215 'enum' => [ FulfillmentType::SHIPPING, FulfillmentType::DIGITAL ],
216 ],
217 'id' => [
218 'description' => __( 'Fulfillment option ID.', 'woocommerce' ),
219 'type' => 'string',
220 ],
221 'title' => [
222 'description' => __( 'Title.', 'woocommerce' ),
223 'type' => 'string',
224 ],
225 'subtitle' => [
226 'description' => __( 'Subtitle.', 'woocommerce' ),
227 'type' => [ 'string', 'null' ],
228 ],
229 'carrier' => [
230 'description' => __( 'Carrier name.', 'woocommerce' ),
231 'type' => [ 'string', 'null' ],
232 ],
233 'earliest_delivery_time' => [
234 'description' => __( 'Earliest delivery time (ISO 8601).', 'woocommerce' ),
235 'type' => [ 'string', 'null' ],
236 ],
237 'latest_delivery_time' => [
238 'description' => __( 'Latest delivery time (ISO 8601).', 'woocommerce' ),
239 'type' => [ 'string', 'null' ],
240 ],
241 'subtotal' => [
242 'description' => __( 'Subtotal in cents.', 'woocommerce' ),
243 'type' => 'integer',
244 ],
245 'tax' => [
246 'description' => __( 'Tax in cents.', 'woocommerce' ),
247 'type' => 'integer',
248 ],
249 'total' => [
250 'description' => __( 'Total in cents.', 'woocommerce' ),
251 'type' => 'integer',
252 ],
253 ],
254 ],
255 ],
256 'fulfillment_option_id' => [
257 'description' => __( 'Selected fulfillment option ID.', 'woocommerce' ),
258 'type' => [ 'string', 'null' ],
259 'context' => [ 'view', 'edit' ],
260 ],
261 'totals' => [
262 'description' => __( 'Order totals breakdown.', 'woocommerce' ),
263 'type' => 'array',
264 'context' => [ 'view', 'edit' ],
265 'items' => [
266 'type' => 'object',
267 'properties' => [
268 'type' => [
269 'description' => __( 'Total type.', 'woocommerce' ),
270 'type' => 'string',
271 ],
272 'display_text' => [
273 'description' => __( 'Display text.', 'woocommerce' ),
274 'type' => 'string',
275 ],
276 'amount' => [
277 'description' => __( 'Amount in cents.', 'woocommerce' ),
278 'type' => 'integer',
279 ],
280 ],
281 ],
282 ],
283 'messages' => [
284 'description' => __( 'Messages (info, warnings, errors).', 'woocommerce' ),
285 'type' => 'array',
286 'context' => [ 'view', 'edit' ],
287 'items' => [
288 'type' => 'object',
289 'properties' => [
290 'type' => [
291 'description' => __( 'Message type.', 'woocommerce' ),
292 'type' => 'string',
293 'enum' => [ MessageType::INFO, MessageType::WARNING, MessageType::ERROR ],
294 ],
295 'param' => [
296 'description' => __( 'JSON path to the related field.', 'woocommerce' ),
297 'type' => [ 'string', 'null' ],
298 ],
299 'content_type' => [
300 'description' => __( 'Content type.', 'woocommerce' ),
301 'type' => 'string',
302 'enum' => [ MessageContentType::PLAIN, MessageContentType::MARKDOWN ],
303 ],
304 'content' => [
305 'description' => __( 'Message content.', 'woocommerce' ),
306 'type' => 'string',
307 ],
308 ],
309 ],
310 ],
311 'links' => [
312 'description' => __( 'Related links.', 'woocommerce' ),
313 'type' => 'array',
314 'context' => [ 'view', 'edit' ],
315 'items' => [
316 'type' => 'object',
317 'properties' => [
318 'type' => [
319 'description' => __( 'Link type.', 'woocommerce' ),
320 'type' => 'string',
321 ],
322 'url' => [
323 'description' => __( 'URL.', 'woocommerce' ),
324 'type' => 'string',
325 ],
326 ],
327 ],
328 ],
329 ];
330 }
331
332 /**
333 * Convert a WooCommerce cart to the Agentic Checkout session format.
334 *
335 * @param AgenticCheckoutSession $checkout_session Checkout session object.
336 * @return array Formatted checkout session data.
337 */
338 public function get_item_response( $checkout_session ) {
339 $cart = $checkout_session->get_cart();
340
341 // If validation already went through and we have errors, no need to repeat them.
342 if ( ! $checkout_session->get_messages()->has_errors() ) {
343 // Validate the checkout session. Messages will be added to the collection, if any.
344 AgenticCheckoutUtils::validate( $checkout_session );
345 }
346
347 $completed_order = WC()->session
348 ? wc_get_order( WC()->session->get( SessionKey::AGENTIC_CHECKOUT_COMPLETED_ORDER_ID ) )
349 : null;
350
351 // Get line items from cart, or from completed order if cart is empty.
352 $cart_items = $cart->get_cart();
353 $line_items = $completed_order instanceof WC_Order
354 ? $this->format_line_items_from_order( $completed_order )
355 : $this->format_line_items_from_cart( $cart_items );
356
357 $response = [
358 'id' => $checkout_session->get_id(),
359 'buyer' => $completed_order instanceof WC_Order
360 ? $this->format_buyer_from_order( $completed_order )
361 : $this->format_buyer(),
362 'payment_provider' => $this->format_payment_provider(),
363 'status' => AgenticCheckoutUtils::calculate_status( $checkout_session ),
364 'currency' => $completed_order instanceof WC_Order
365 ? strtolower( $completed_order->get_currency() )
366 : strtolower( get_woocommerce_currency() ),
367 'line_items' => $line_items,
368 'fulfillment_address' => $completed_order instanceof WC_Order
369 ? $this->format_fulfillment_address_from_order( $completed_order )
370 : $this->format_fulfillment_address(),
371 'fulfillment_options' => $completed_order instanceof WC_Order
372 ? $this->format_fulfillment_options_from_order( $completed_order )
373 : $this->format_fulfillment_options(),
374 'fulfillment_option_id' => $completed_order instanceof WC_Order
375 ? $this->get_selected_fulfillment_option_id_from_order( $completed_order )
376 : $this->get_selected_fulfillment_option_id(),
377 'totals' => $completed_order instanceof WC_Order
378 ? $this->format_totals_from_order( $completed_order )
379 : $this->format_totals( $cart ),
380 'messages' => $checkout_session->get_messages()->get_formatted_messages(),
381 'links' => $this->get_links(),
382 ];
383
384 // Add order data if a completed order exists.
385 if ( $completed_order instanceof WC_Order ) {
386 $response['order'] = [
387 'id' => (string) $completed_order->get_id(),
388 'checkout_session_id' => $checkout_session->get_id(),
389 'permalink_url' => $completed_order->get_checkout_order_received_url(),
390 ];
391 }
392
393 return $response;
394 }
395
396 /**
397 * Format buyer information.
398 *
399 * @return array|null Buyer data or null.
400 */
401 protected function format_buyer() {
402 $customer = WC()->customer;
403
404 if ( ! $customer ) {
405 return null;
406 }
407
408 $first_name = $customer->get_billing_first_name() ? $customer->get_billing_first_name() : $customer->get_shipping_first_name();
409 $last_name = $customer->get_billing_last_name() ? $customer->get_billing_last_name() : $customer->get_shipping_last_name();
410 $email = $customer->get_billing_email();
411
412 if ( ! $first_name && ! $last_name && ! $email ) {
413 return null;
414 }
415
416 return [
417 'first_name' => $first_name ? $first_name : '',
418 'last_name' => $last_name ? $last_name : '',
419 'email' => $email ? $email : '',
420 'phone_number' => $customer->get_billing_phone() ? $customer->get_billing_phone() : '',
421 ];
422 }
423
424 /**
425 * Format buyer information from order.
426 *
427 * @param \WC_Order $order Order object.
428 * @return array|null Buyer data or null.
429 */
430 protected function format_buyer_from_order( $order ) {
431 $first_name = $order->get_billing_first_name() ? $order->get_billing_first_name() : $order->get_shipping_first_name();
432 $last_name = $order->get_billing_last_name() ? $order->get_billing_last_name() : $order->get_shipping_last_name();
433 $email = $order->get_billing_email();
434
435 if ( ! $first_name && ! $last_name && ! $email ) {
436 return null;
437 }
438
439 return [
440 'first_name' => $first_name ? $first_name : '',
441 'last_name' => $last_name ? $last_name : '',
442 'email' => $email ? $email : '',
443 'phone_number' => $order->get_billing_phone() ? $order->get_billing_phone() : '',
444 ];
445 }
446
447 /**
448 * Format payment provider information.
449 *
450 * @return array|null Payment provider data or null.
451 */
452 protected function format_payment_provider() {
453 $available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
454
455 if ( empty( $available_gateways ) ) {
456 return null;
457 }
458
459 // Look for gateway with agentic_commerce capability.
460 $gateway = AgenticCheckoutUtils::get_agentic_commerce_gateway( $available_gateways );
461
462 if ( null !== $gateway ) {
463 return [
464 'provider' => $gateway->get_agentic_commerce_provider(),
465 'supported_payment_methods' => $gateway->get_agentic_commerce_payment_methods(),
466 ];
467 }
468
469 return [
470 'provider' => 'stripe',
471 'supported_payment_methods' => [ PaymentMethod::CARD ], // Default, can be expanded.
472 ];
473 }
474
475 /**
476 * Convert amount from decimal to cents.
477 *
478 * @param string|float $amount Amount in decimal.
479 * @return int Amount in cents.
480 */
481 protected function amount_to_cents( $amount ) {
482 return (int) $this->extend->get_formatter( 'money' )->format(
483 $amount
484 );
485 }
486
487 /**
488 * Format line items from cart.
489 *
490 * @param array $cart_items Cart items array.
491 * @return array Formatted line items.
492 */
493 protected function format_line_items_from_cart( $cart_items ) {
494 $items = [];
495
496 foreach ( $cart_items as $cart_item_key => $cart_item ) {
497 $product = $cart_item['data'];
498 $quantity = $cart_item['quantity'];
499 $base_amount = $this->amount_to_cents( $product->get_price() * $quantity );
500 $discount = $this->amount_to_cents( $cart_item['line_subtotal'] - $cart_item['line_total'] );
501 $subtotal = $base_amount - $discount;
502 $tax = $this->amount_to_cents( $cart_item['line_tax'] );
503 $total = $subtotal + $tax;
504
505 $items[] = [
506 'id' => (string) $cart_item_key,
507 'item' => [
508 'id' => (string) $product->get_id(),
509 'quantity' => $quantity,
510 ],
511 'base_amount' => $base_amount,
512 'discount' => $discount,
513 'subtotal' => $subtotal,
514 'tax' => $tax,
515 'total' => $total,
516 ];
517 }
518
519 return $items;
520 }
521
522 /**
523 * Format line items from order.
524 *
525 * @param \WC_Order $order Order object.
526 * @return array Formatted line items.
527 */
528 protected function format_line_items_from_order( $order ) {
529 $items = [];
530
531 foreach ( $order->get_items() as $item_id => $item ) {
532 $quantity = $item->get_quantity();
533 $base_amount = $this->amount_to_cents( $item->get_subtotal() );
534 $discount = $this->amount_to_cents( $item->get_subtotal() - $item->get_total() );
535 $subtotal = $base_amount - $discount;
536 $tax = $this->amount_to_cents( $item->get_total_tax() );
537 $total = $subtotal + $tax;
538
539 // Use product_id from the order item, with variation_id as fallback.
540 $item_product_id = $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id();
541
542 $items[] = [
543 'id' => (string) $item_id,
544 'item' => [
545 'id' => (string) $item_product_id,
546 'quantity' => $quantity,
547 ],
548 'base_amount' => $base_amount,
549 'discount' => $discount,
550 'subtotal' => $subtotal,
551 'tax' => $tax,
552 'total' => $total,
553 ];
554 }
555
556 return $items;
557 }
558
559 /**
560 * Format fulfillment address.
561 *
562 * @return array|null Address data or null.
563 */
564 protected function format_fulfillment_address() {
565 $customer = WC()->customer;
566
567 if ( ! $customer || ! $customer->get_shipping_address_1() ) {
568 return null;
569 }
570
571 return $this->build_address_array(
572 $customer->get_shipping_first_name(),
573 $customer->get_shipping_last_name(),
574 $customer->get_shipping_address_1(),
575 $customer->get_shipping_address_2(),
576 $customer->get_shipping_city(),
577 $customer->get_shipping_state(),
578 $customer->get_shipping_country(),
579 $customer->get_shipping_postcode()
580 );
581 }
582
583 /**
584 * Format fulfillment address from order.
585 *
586 * @param \WC_Order $order Order object.
587 * @return array|null Address data or null.
588 */
589 protected function format_fulfillment_address_from_order( $order ) {
590 if ( ! $order->get_shipping_address_1() ) {
591 return null;
592 }
593
594 return $this->build_address_array(
595 $order->get_shipping_first_name(),
596 $order->get_shipping_last_name(),
597 $order->get_shipping_address_1(),
598 $order->get_shipping_address_2(),
599 $order->get_shipping_city(),
600 $order->get_shipping_state(),
601 $order->get_shipping_country(),
602 $order->get_shipping_postcode()
603 );
604 }
605
606 /**
607 * Build address array from components.
608 *
609 * @param string $first_name First name.
610 * @param string $last_name Last name.
611 * @param string $address_1 Address line 1.
612 * @param string $address_2 Address line 2.
613 * @param string $city City.
614 * @param string $state State.
615 * @param string $country Country.
616 * @param string $postcode Postcode.
617 * @return array Address array.
618 */
619 protected function build_address_array( $first_name, $last_name, $address_1, $address_2, $city, $state, $country, $postcode ) {
620 $name = trim( $first_name . ' ' . $last_name );
621
622 return [
623 'name' => $name ? $name : 'Customer',
624 'line_one' => $address_1,
625 'line_two' => $address_2 ? $address_2 : '',
626 'city' => $city,
627 'state' => $state,
628 'country' => $country,
629 'postal_code' => $postcode,
630 ];
631 }
632
633 /**
634 * Format fulfillment options (shipping methods).
635 *
636 * @return array Fulfillment options.
637 */
638 protected function format_fulfillment_options() {
639 $options = [];
640 $packages = WC()->shipping()->get_packages();
641
642 foreach ( $packages as $package ) {
643 if ( empty( $package['rates'] ) ) {
644 continue;
645 }
646
647 foreach ( $package['rates'] as $rate ) {
648 $options[] = [
649 'type' => FulfillmentType::SHIPPING,
650 'id' => $rate->get_id(),
651 'title' => $rate->get_label(),
652 'subtitle' => null,
653 'carrier' => $rate->get_method_id(),
654 'earliest_delivery_time' => null,
655 'latest_delivery_time' => null,
656 'subtotal' => $this->amount_to_cents( $rate->get_cost() ),
657 'tax' => $this->amount_to_cents( $rate->get_shipping_tax() ),
658 'total' => $this->amount_to_cents( $rate->get_cost() + $rate->get_shipping_tax() ),
659 ];
660 }
661 }
662
663 return $options;
664 }
665
666 /**
667 * Format fulfillment options from order.
668 *
669 * @param \WC_Order $order Order object.
670 * @return array Fulfillment options.
671 */
672 protected function format_fulfillment_options_from_order( $order ) {
673 $options = [];
674 $shipping_methods = $order->get_shipping_methods();
675
676 foreach ( $shipping_methods as $item ) {
677 $options[] = [
678 'type' => FulfillmentType::SHIPPING,
679 'id' => $item->get_method_id() . ':' . $item->get_instance_id(),
680 'title' => $item->get_name(),
681 'subtitle' => null,
682 'carrier' => $item->get_method_id(),
683 'earliest_delivery_time' => null,
684 'latest_delivery_time' => null,
685 'subtotal' => $this->amount_to_cents( $item->get_total() ),
686 'tax' => $this->amount_to_cents( $item->get_total_tax() ),
687 'total' => $this->amount_to_cents( $item->get_total() + $item->get_total_tax() ),
688 ];
689 }
690
691 return $options;
692 }
693
694 /**
695 * Get selected fulfillment option ID.
696 *
697 * @return string|null Selected option ID or null.
698 */
699 protected function get_selected_fulfillment_option_id() {
700 $chosen_methods = WC()->session->get( SessionKey::CHOSEN_SHIPPING_METHODS );
701
702 return ! empty( $chosen_methods[0] ) ? $chosen_methods[0] : null;
703 }
704
705 /**
706 * Get selected fulfillment option ID from order.
707 *
708 * @param \WC_Order $order Order object.
709 * @return string|null Selected option ID or null.
710 */
711 protected function get_selected_fulfillment_option_id_from_order( $order ) {
712 $shipping_methods = $order->get_shipping_methods();
713 if ( empty( $shipping_methods ) ) {
714 return null;
715 }
716
717 $shipping_method = reset( $shipping_methods );
718 return $shipping_method->get_method_id() . ':' . $shipping_method->get_instance_id();
719 }
720
721 /**
722 * Format totals array.
723 *
724 * @param \WC_Cart $cart Cart object.
725 * @return array Totals array.
726 */
727 protected function format_totals( $cart ) {
728 $totals = [];
729
730 // Items base amount.
731 $items_base = 0;
732 foreach ( $cart->get_cart() as $cart_item ) {
733 $product = $cart_item['data'];
734 $items_base += $product->get_price() * $cart_item['quantity'];
735 }
736 $totals[] = [
737 'type' => TotalType::ITEMS_BASE_AMOUNT,
738 'display_text' => __( 'Items Base Amount', 'woocommerce' ),
739 'amount' => $this->amount_to_cents( $items_base ),
740 ];
741
742 // Items discount.
743 $discount = $cart->get_cart_discount_total();
744 $totals[] = [
745 'type' => TotalType::ITEMS_DISCOUNT,
746 'display_text' => __( 'Items Discount', 'woocommerce' ),
747 'amount' => $this->amount_to_cents( $discount ),
748 ];
749
750 // Subtotal.
751 $totals[] = [
752 'type' => TotalType::SUBTOTAL,
753 'display_text' => __( 'Subtotal', 'woocommerce' ),
754 'amount' => $this->amount_to_cents( $cart->get_subtotal() - $discount ),
755 ];
756
757 // Fulfillment (shipping).
758 $totals[] = [
759 'type' => TotalType::FULFILLMENT,
760 'display_text' => __( 'Shipping', 'woocommerce' ),
761 'amount' => $this->amount_to_cents( $cart->get_shipping_total() ),
762 ];
763
764 // Tax.
765 $totals[] = [
766 'type' => TotalType::TAX,
767 'display_text' => __( 'Tax', 'woocommerce' ),
768 'amount' => $this->amount_to_cents( $cart->get_total_tax() ),
769 ];
770
771 // Total.
772 $totals[] = [
773 'type' => TotalType::TOTAL,
774 'display_text' => __( 'Total', 'woocommerce' ),
775 'amount' => $this->amount_to_cents( $cart->get_total( 'edit' ) ),
776 ];
777
778 return $totals;
779 }
780
781 /**
782 * Format totals array from order.
783 *
784 * @param \WC_Order $order Order object.
785 * @return array Totals array.
786 */
787 protected function format_totals_from_order( $order ) {
788 $totals = [];
789
790 // Items base amount.
791 $items_base = 0;
792 foreach ( $order->get_items() as $item ) {
793 $product = $item->get_product();
794 $items_base += $product->get_price() * $item->get_quantity();
795 }
796 $totals[] = [
797 'type' => TotalType::ITEMS_BASE_AMOUNT,
798 'display_text' => __( 'Items Base Amount', 'woocommerce' ),
799 'amount' => $this->amount_to_cents( $items_base ),
800 ];
801
802 // Items discount.
803 $discount = $order->get_discount_total();
804 $totals[] = [
805 'type' => TotalType::ITEMS_DISCOUNT,
806 'display_text' => __( 'Items Discount', 'woocommerce' ),
807 'amount' => $this->amount_to_cents( $discount ),
808 ];
809
810 // Subtotal.
811 $totals[] = [
812 'type' => TotalType::SUBTOTAL,
813 'display_text' => __( 'Subtotal', 'woocommerce' ),
814 'amount' => $this->amount_to_cents( $items_base - $discount ),
815 ];
816
817 // Fulfillment (shipping).
818 $totals[] = [
819 'type' => TotalType::FULFILLMENT,
820 'display_text' => __( 'Shipping', 'woocommerce' ),
821 'amount' => $this->amount_to_cents( $order->get_shipping_total() ),
822 ];
823
824 // Tax.
825 $totals[] = [
826 'type' => TotalType::TAX,
827 'display_text' => __( 'Tax', 'woocommerce' ),
828 'amount' => $this->amount_to_cents( $order->get_total_tax() ),
829 ];
830
831 // Total.
832 $totals[] = [
833 'type' => TotalType::TOTAL,
834 'display_text' => __( 'Total', 'woocommerce' ),
835 'amount' => $this->amount_to_cents( $order->get_total() ),
836 ];
837
838 return $totals;
839 }
840
841 /**
842 * Get links for the session.
843 *
844 * @return array Links array.
845 */
846 protected function get_links() {
847 $links = [];
848
849 // Terms of use.
850 $terms_page_id = wc_terms_and_conditions_page_id();
851 if ( $terms_page_id ) {
852 $permalink = get_permalink( $terms_page_id );
853 if ( $permalink ) {
854 $links[] = [
855 'type' => LinkType::TERMS_OF_USE,
856 'url' => $permalink,
857 ];
858 }
859 }
860
861 // Privacy policy.
862 $privacy_page_id = get_option( 'wp_page_for_privacy_policy' );
863 if ( $privacy_page_id ) {
864 $permalink = get_permalink( $privacy_page_id );
865 if ( $permalink ) {
866 $links[] = [
867 'type' => LinkType::PRIVACY_POLICY,
868 'url' => $permalink,
869 ];
870 }
871 }
872
873 return $links;
874 }
875 }
876