PluginProbe
AI Chatbot for WooCommerce & Live Chat – onWebChat / 3.10.0
AI Chatbot for WooCommerce & Live Chat – onWebChat v3.10.0
3.10.0 3.9.2 3.9.3 3.9.1 3.9.0 3.8.4 3.8.2 3.8.1 3.8.0 3.7.2 3.7.1 3.7.0 3.6.0 3.5.5 trunk 1.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.2 1.0.3 1.0.4 1.0.5 All 49 releases
← All changes | includes/woocommerce-orders.php +103 -15 3.7.23.10.0 View file →
@@ -131,9 +131,17 @@
131 131 $order_number = isset($params['order_number']) ? sanitize_text_field($params['order_number']) : '';
132 132 $email = isset($params['email']) ? sanitize_email($params['email']) : '';
133 133 // last_name is accepted but is NOT a standalone key; the email is always required.
134 134 $last_name = isset($params['last_name']) ? sanitize_text_field($params['last_name']) : '';
135 + $action = isset($params['action']) ? sanitize_text_field($params['action']) : '';
135 136
137 + // "My recent orders" for a signed-in customer: email-only, no order number. Safe because the
138 + // onWebChat server only ever sends a VERIFIED email here (it recomputed the identity hash this
139 + // plugin emitted for the logged-in customer), and the request itself is HMAC-authenticated.
140 + if ($action === 'recent_orders') {
141 + return $this->handle_recent_orders($email);
142 + }
143 +
136 144 // Both the order number AND the email are required for verification.
137 145 if (empty($order_number) || empty($email)) {
138 146 return new WP_REST_Response(array('found' => false, 'verified' => false, 'error' => 'need_more_info'), 200);
139 147 }
@@ -179,9 +187,56 @@
179 187
180 188 // Successful match: clear the per-order failed-attempt counter.
181 189 delete_transient($fail_key);
182 190
183 - // Build the (verified) response.
191 + return new WP_REST_Response($this->build_order_response($order), 200);
192 + }
193 +
194 + /**
195 + * List the customer's most recent orders by email (action=recent_orders). Called (already
196 + * HMAC-authenticated) only with server-verified emails, so no per-order fail counting applies;
197 + * the response is always scoped to exactly that email.
198 + *
199 + * @param string $email
200 + * @return WP_REST_Response
201 + */
202 + private function handle_recent_orders($email) {
203 + if (empty($email)) {
204 + return new WP_REST_Response(array('found' => false, 'verified' => false, 'error' => 'need_more_info'), 200);
205 + }
206 +
207 + $wc_orders = wc_get_orders(array(
208 + 'billing_email' => $email,
209 + 'limit' => 5,
210 + 'orderby' => 'date',
211 + 'order' => 'DESC',
212 + 'type' => 'shop_order', // exclude refund objects
213 + ));
214 +
215 + $orders = array();
216 + foreach ($wc_orders as $order) {
217 + // Never expose unfinished checkout drafts.
218 + if ($order->get_status() === 'checkout-draft') {
219 + continue;
220 + }
221 + $orders[] = $this->build_order_response($order);
222 + }
223 +
224 + if (empty($orders)) {
225 + return new WP_REST_Response(array('found' => false, 'verified' => true, 'orders' => array()), 200);
226 + }
227 +
228 + return new WP_REST_Response(array('found' => true, 'verified' => true, 'orders' => $orders), 200);
229 + }
230 +
231 + /**
232 + * Build the verified per-order payload in the shape the onWebChat server normalizer expects.
233 + * Used by both the single-order lookup and the recent-orders listing.
234 + *
235 + * @param WC_Order $order
236 + * @return array
237 + */
238 + private function build_order_response($order) {
184 239 $items = array();
185 240 foreach ($order->get_items() as $item) {
186 241 $items[] = array(
187 242 'name' => $item->get_name(),
@@ -192,24 +247,57 @@
192 247 }
193 248 }
194 249
195 250 $status = $order->get_status(); // e.g. 'processing', 'completed'
196 - $data = array(
197 - 'found' => true,
198 - 'verified' => true,
199 - 'order_number' => (string) $order->get_order_number(),
200 - 'status' => $status,
201 - 'status_label' => function_exists('wc_get_order_status_name') ? wc_get_order_status_name($status) : $status,
202 - 'date_created' => $order->get_date_created() ? wc_format_datetime($order->get_date_created()) : '',
203 - 'total' => $order->get_total(),
204 - 'currency' => $order->get_currency(),
205 - 'payment_method' => $order->get_payment_method_title(),
206 - 'customer_note' => $order->get_customer_note(),
207 - 'items' => $items,
208 - 'tracking' => $this->get_tracking($order),
251 + return array(
252 + 'found' => true,
253 + 'verified' => true,
254 + 'order_number' => (string) $order->get_order_number(),
255 + 'status' => $status,
256 + 'status_label' => function_exists('wc_get_order_status_name') ? wc_get_order_status_name($status) : $status,
257 + 'date_created' => $order->get_date_created() ? wc_format_datetime($order->get_date_created()) : '',
258 + 'total' => $order->get_total(),
259 + 'currency' => $order->get_currency(),
260 + 'payment_method' => $order->get_payment_method_title(),
261 + 'shipping_method' => $order->get_shipping_method(),
262 + 'customer_note' => $order->get_customer_note(),
263 + 'items' => $items,
264 + 'tracking' => $this->get_tracking($order),
265 + 'history' => $this->get_history($order),
209 266 );
267 + }
210 268
211 - return new WP_REST_Response($data, 200);
269 + /**
270 + * Customer-facing order notes as a status timeline: lets the AI explain how the order has
271 + * progressed, and merchants often paste carrier + tracking info into these notes. ONLY notes of
272 + * type "customer" are exposed (internal/private notes may hold merchant-only info). Oldest first,
273 + * capped at the 6 most recent.
274 + *
275 + * @param WC_Order $order
276 + * @return array
277 + */
278 + private function get_history($order) {
279 + $history = array();
280 +
281 + if (!function_exists('wc_get_order_notes')) {
282 + return $history;
283 + }
284 +
285 + $notes = wc_get_order_notes(array(
286 + 'order_id' => $order->get_id(),
287 + 'type' => 'customer',
288 + 'limit' => 6,
289 + ));
290 +
291 + foreach (array_reverse($notes) as $note) {
292 + $history[] = array(
293 + 'date' => isset($note->date_created) && $note->date_created ? wc_format_datetime($note->date_created) : '',
294 + 'status' => '',
295 + 'comment' => isset($note->content) ? (string) $note->content : '',
296 + );
297 + }
298 +
299 + return $history;
212 300 }
213 301
214 302 /**
215 303 * Resolve an order number (which may be a custom/sequential number, possibly with a leading #)