'POST', 'callback' => array( $this, 'upsert' ), 'permission_callback' => array( $this->verifier, 'verify' ), ) ); register_rest_route( self::NAMESPACE, '/orders/(?P\d+)/status', array( 'methods' => 'POST', 'callback' => array( $this, 'status' ), 'permission_callback' => array( $this->verifier, 'verify' ), ) ); } /** * Create or update the order. * * @param WP_REST_Request $request Signed request from the cloud. * @return WP_REST_Response|WP_Error */ public function upsert( WP_REST_Request $request ) { $data = json_decode( $request->get_body(), true ); try { $snapshot = InboundOrder::from_array( is_array( $data ) ? $data : array() ); } catch ( \Throwable $e ) { return new WP_Error( 'storebridge_invalid_body', 'The order is invalid.', array( 'status' => 400 ) ); } try { $result = $this->importer->upsert( $snapshot ); } catch ( \Throwable $e ) { wc_get_logger()->error( 'Storebridge could not import TikTok order ' . $snapshot->tiktok_order_id . ': ' . $e->getMessage(), array( 'source' => 'storebridge' ) ); return new WP_Error( 'storebridge_import_failed', 'The order could not be created in WooCommerce.', array( 'status' => 500 ) ); } if ( null === $result ) { // Another request is importing this order right now; the cloud retries. return new WP_Error( 'storebridge_busy', 'This order is being imported.', array( 'status' => 409 ) ); } $body = new InboundOrderResult( woo_order_id: $result['order']->get_id(), created: $result['created'], woo_status: $result['order']->get_status(), warnings: $result['warnings'], ); return new WP_REST_Response( $body->to_array(), $result['created'] ? 201 : 200 ); } /** * Record a return or cancellation request on the order. * * @param WP_REST_Request $request Signed request from the cloud. * @return WP_REST_Response|WP_Error */ public function status( WP_REST_Request $request ) { // get_param() prefers the JSON body over the URL; the id must come from the signed path. $order_id = (int) ( $request->get_url_params()['woo_order_id'] ?? 0 ); $data = json_decode( $request->get_body(), true ); try { $update = OrderStatusUpdate::from_array( is_array( $data ) ? $data : array() ); } catch ( \Throwable $e ) { return new WP_Error( 'storebridge_invalid_body', 'The order update is invalid.', array( 'status' => 400 ) ); } $order = wc_get_order( $order_id ); if ( ! $order instanceof WC_Order || $order->get_meta( OrderMeta::ORDER_ID ) !== $update->tiktok_order_id ) { return new WP_Error( 'storebridge_not_found', 'Unknown order.', array( 'status' => 404 ) ); } if ( null !== $update->return_request ) { $returns = json_decode( (string) $order->get_meta( OrderMeta::RETURNS ), true ); $returns = is_array( $returns ) ? $returns : array(); $known = $returns[ $update->return_request->tiktok_return_id ] ?? null; if ( ! is_array( $known ) || strtotime( (string) ( $known['occurred_at'] ?? '' ) ) <= strtotime( $update->occurred_at ) ) { $returns[ $update->return_request->tiktok_return_id ] = array_merge( $update->return_request->to_array(), array( 'occurred_at' => $update->occurred_at ) ); $order->update_meta_data( OrderMeta::RETURNS, (string) wp_json_encode( $returns ) ); $order->save(); } } if ( null !== $update->note && '' !== $update->note ) { $order->add_order_note( $update->note ); } return new WP_REST_Response( null, 204 ); } }