%PDF- %PDF-
Direktori : /home/tradesc/www/relax/wp-content/plugins/woocommerce/src/StoreApi/Schemas/V1/ |
Current File : /home/tradesc/www/relax/wp-content/plugins/woocommerce/src/StoreApi/Schemas/V1/CheckoutSchema.php |
<?php namespace Automattic\WooCommerce\StoreApi\Schemas\V1; use Automattic\WooCommerce\StoreApi\SchemaController; use Automattic\WooCommerce\StoreApi\Payments\PaymentResult; use Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema; use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields; use Automattic\WooCommerce\Blocks\Package; use Automattic\WooCommerce\StoreApi\Utilities\SanitizationUtils; /** * CheckoutSchema class. */ class CheckoutSchema extends AbstractSchema { /** * The schema item name. * * @var string */ protected $title = 'checkout'; /** * The schema item identifier. * * @var string */ const IDENTIFIER = 'checkout'; /** * Billing address schema instance. * * @var BillingAddressSchema */ protected $billing_address_schema; /** * Shipping address schema instance. * * @var ShippingAddressSchema */ protected $shipping_address_schema; /** * Image Attachment schema instance. * * @var ImageAttachmentSchema */ protected $image_attachment_schema; /** * Additional fields controller. * * @var CheckoutFields */ protected $additional_fields_controller; /** * Constructor. * * @param ExtendSchema $extend Rest Extending instance. * @param SchemaController $controller Schema Controller instance. */ public function __construct( ExtendSchema $extend, SchemaController $controller ) { parent::__construct( $extend, $controller ); $this->billing_address_schema = $this->controller->get( BillingAddressSchema::IDENTIFIER ); $this->shipping_address_schema = $this->controller->get( ShippingAddressSchema::IDENTIFIER ); $this->image_attachment_schema = $this->controller->get( ImageAttachmentSchema::IDENTIFIER ); $this->additional_fields_controller = Package::container()->get( CheckoutFields::class ); } /** * Checkout schema properties. * * @return array */ public function get_properties() { $additional_field_schema = $this->get_additional_fields_schema(); return [ 'order_id' => [ 'description' => __( 'The order ID to process during checkout.', 'woocommerce' ), 'type' => 'integer', 'context' => [ 'view', 'edit' ], 'readonly' => true, ], 'status' => [ 'description' => __( 'Order status. Payment providers will update this value after payment.', 'woocommerce' ), 'type' => 'string', 'context' => [ 'view', 'edit' ], 'readonly' => true, ], 'order_key' => [ 'description' => __( 'Order key used to check validity or protect access to certain order data.', 'woocommerce' ), 'type' => 'string', 'context' => [ 'view', 'edit' ], 'readonly' => true, ], 'order_number' => [ 'description' => __( 'Order number used for display.', 'woocommerce' ), 'type' => 'string', 'context' => [ 'view', 'edit' ], 'readonly' => true, ], 'customer_note' => [ 'description' => __( 'Note added to the order by the customer during checkout.', 'woocommerce' ), 'type' => 'string', 'context' => [ 'view', 'edit' ], ], 'customer_id' => [ 'description' => __( 'Customer ID if registered. Will return 0 for guests.', 'woocommerce' ), 'type' => 'integer', 'context' => [ 'view', 'edit' ], 'readonly' => true, ], 'billing_address' => [ 'description' => __( 'Billing address.', 'woocommerce' ), 'type' => 'object', 'context' => [ 'view', 'edit' ], 'properties' => $this->billing_address_schema->get_properties(), 'arg_options' => [ 'sanitize_callback' => [ $this->billing_address_schema, 'sanitize_callback' ], 'validate_callback' => [ $this->billing_address_schema, 'validate_callback' ], ], 'required' => true, ], 'shipping_address' => [ 'description' => __( 'Shipping address.', 'woocommerce' ), 'type' => 'object', 'context' => [ 'view', 'edit' ], 'properties' => $this->shipping_address_schema->get_properties(), 'arg_options' => [ 'sanitize_callback' => [ $this->shipping_address_schema, 'sanitize_callback' ], 'validate_callback' => [ $this->shipping_address_schema, 'validate_callback' ], ], ], 'payment_method' => [ 'description' => __( 'The ID of the payment method being used to process the payment.', 'woocommerce' ), 'type' => 'string', 'context' => [ 'view', 'edit' ], // Validation may be based on cart contents which is not available here; this returns all enabled // gateways. Further validation occurs during the request. 'enum' => array_values( WC()->payment_gateways->get_payment_gateway_ids() ), ], 'create_account' => [ 'description' => __( 'Whether to create a new user account as part of order processing.', 'woocommerce' ), 'type' => 'boolean', 'context' => [ 'view', 'edit' ], ], 'payment_result' => [ 'description' => __( 'Result of payment processing, or false if not yet processed.', 'woocommerce' ), 'type' => 'object', 'context' => [ 'view', 'edit' ], 'readonly' => true, 'properties' => [ 'payment_status' => [ 'description' => __( 'Status of the payment returned by the gateway. One of success, pending, failure, error.', 'woocommerce' ), 'readonly' => true, 'type' => 'string', ], 'payment_details' => [ 'description' => __( 'An array of data being returned from the payment gateway.', 'woocommerce' ), 'readonly' => true, 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => [ 'key' => [ 'type' => 'string', ], 'value' => [ 'type' => 'string', ], ], ], ], 'redirect_url' => [ 'description' => __( 'A URL to redirect the customer after checkout. This could be, for example, a link to the payment processors website.', 'woocommerce' ), 'readonly' => true, 'type' => 'string', ], ], ], 'additional_fields' => [ 'description' => __( 'Additional fields to be persisted on the order.', 'woocommerce' ), 'type' => 'object', 'context' => [ 'view', 'edit' ], 'properties' => $additional_field_schema, 'arg_options' => [ 'sanitize_callback' => [ $this, 'sanitize_additional_fields' ], 'validate_callback' => [ $this, 'validate_additional_fields' ], ], 'required' => $this->schema_has_required_property( $additional_field_schema ), ], self::EXTENDING_KEY => $this->get_extended_schema( self::IDENTIFIER ), ]; } /** * Return the response for checkout. * * @param object $item Results from checkout action. * @return array */ public function get_item_response( $item ) { return $this->get_checkout_response( $item->order, $item->payment_result ); } /** * Get the checkout response based on the current order and any payments. * * @param \WC_Order $order Order object. * @param PaymentResult $payment_result Payment result object. * @return array */ protected function get_checkout_response( \WC_Order $order, PaymentResult $payment_result = null ) { return [ 'order_id' => $order->get_id(), 'status' => $order->get_status(), 'order_key' => $order->get_order_key(), 'order_number' => $order->get_order_number(), 'customer_note' => $order->get_customer_note(), 'customer_id' => $order->get_customer_id(), 'billing_address' => (object) $this->billing_address_schema->get_item_response( $order ), 'shipping_address' => (object) $this->shipping_address_schema->get_item_response( $order ), 'payment_method' => $order->get_payment_method(), 'payment_result' => [ 'payment_status' => $payment_result->status, 'payment_details' => $this->prepare_payment_details_for_response( $payment_result->payment_details ), 'redirect_url' => $payment_result->redirect_url, ], 'additional_fields' => $this->get_additional_fields_response( $order ), self::EXTENDING_KEY => $this->get_extended_data( self::IDENTIFIER ), ]; } /** * This prepares the payment details for the response so it's following the * schema where it's an array of objects. * * @param array $payment_details An array of payment details from the processed payment. * * @return array An array of objects where each object has the key and value * as distinct properties. */ protected function prepare_payment_details_for_response( array $payment_details ) { return array_map( function ( $key, $value ) { return (object) [ 'key' => $key, 'value' => $value, ]; }, array_keys( $payment_details ), $payment_details ); } /** * Get the additional fields response. * * @param \WC_Order $order Order object. * @return array */ protected function get_additional_fields_response( \WC_Order $order ) { $fields = wp_parse_args( $this->additional_fields_controller->get_all_fields_from_object( $order, 'other' ), $this->additional_fields_controller->get_all_fields_from_object( wc()->customer, 'other' ) ); $additional_field_schema = $this->get_additional_fields_schema(); foreach ( $fields as $key => $value ) { if ( ! isset( $additional_field_schema[ $key ] ) ) { unset( $fields[ $key ] ); continue; } // This makes sure we're casting checkboxes from "1" and "0" to boolean. In the frontend, "0" is treated as truthy. if ( isset( $additional_field_schema[ $key ]['type'] ) && 'boolean' === $additional_field_schema[ $key ]['type'] ) { $fields[ $key ] = (bool) $value; } else { $fields[ $key ] = $this->prepare_html_response( $value ); } } return $fields; } /** * Get the schema for additional fields. * * @return array */ protected function get_additional_fields_schema() { return $this->generate_additional_fields_schema( $this->additional_fields_controller->get_fields_for_location( 'contact' ), $this->additional_fields_controller->get_fields_for_location( 'order' ) ); } /** * Generate the schema for additional fields. * * @param array[] ...$args One or more arrays of additional fields. * @return array */ protected function generate_additional_fields_schema( ...$args ) { $additional_fields = array_merge( ...$args ); $schema = []; foreach ( $additional_fields as $key => $field ) { $field_schema = [ 'description' => $field['label'], 'type' => 'string', 'context' => [ 'view', 'edit' ], 'required' => $field['required'], ]; if ( 'select' === $field['type'] ) { $field_schema['enum'] = array_map( function ( $option ) { return $option['value']; }, $field['options'] ); if ( true !== $field['required'] ) { $field_schema['enum'][] = ''; } } if ( 'checkbox' === $field['type'] ) { $field_schema['type'] = 'boolean'; } $schema[ $key ] = $field_schema; } return $schema; } /** * Check if any additional field is required, so that the parent item is required as well. * * @param array $additional_fields_schema Additional fields schema. * @return bool */ protected function schema_has_required_property( $additional_fields_schema ) { return array_reduce( array_keys( $additional_fields_schema ), function ( $carry, $key ) use ( $additional_fields_schema ) { return $carry || $additional_fields_schema[ $key ]['required']; }, false ); } /** * Sanitize and format additional fields object. * * @param array $fields Values being sanitized. * @return array */ public function sanitize_additional_fields( $fields ) { $properties = $this->get_additional_fields_schema(); $sanitization_utils = new SanitizationUtils(); $fields = $sanitization_utils->wp_kses_array( array_reduce( array_keys( $fields ), function ( $carry, $key ) use ( $fields, $properties ) { if ( ! isset( $properties[ $key ] ) ) { return $carry; } $field_schema = $properties[ $key ]; $rest_sanitized = rest_sanitize_value_from_schema( wp_unslash( $fields[ $key ] ), $field_schema, $key ); $rest_sanitized = $this->additional_fields_controller->sanitize_field( $key, $rest_sanitized ); $carry[ $key ] = $rest_sanitized; return $carry; }, [] ) ); return $sanitization_utils->wp_kses_array( $fields ); } /** * Validate additional fields object. * * @see rest_validate_value_from_schema * * @param array $fields Value being sanitized. * @param \WP_REST_Request $request The Request. * @return true|\WP_Error */ public function validate_additional_fields( $fields, $request ) { $errors = new \WP_Error(); $fields = $this->sanitize_additional_fields( $fields ); $additional_field_schema = $this->get_additional_fields_schema(); // Loop over the schema instead of the fields. This is to ensure missing fields are validated. foreach ( $additional_field_schema as $key => $schema ) { if ( ! isset( $fields[ $key ] ) && ! $schema['required'] ) { // Optional fields can go missing. continue; } $field_value = isset( $fields[ $key ] ) ? $fields[ $key ] : null; $result = rest_validate_value_from_schema( $field_value, $schema, $key ); // Only allow custom validation on fields that pass the schema validation. if ( true === $result ) { $result = $this->additional_fields_controller->validate_field( $key, $field_value ); } if ( is_wp_error( $result ) && $result->has_errors() ) { $location = $this->additional_fields_controller->get_field_location( $key ); foreach ( $result->get_error_codes() as $code ) { $result->add_data( array( 'location' => $location, 'key' => $key, ), $code ); } $errors->merge_from( $result ); } } // Validate groups of properties per registered location. $locations = array( 'contact', 'order' ); foreach ( $locations as $location ) { $location_fields = $this->additional_fields_controller->filter_fields_for_location( $fields, $location ); $result = $this->additional_fields_controller->validate_fields_for_location( $location_fields, $location, 'other' ); if ( is_wp_error( $result ) && $result->has_errors() ) { $errors->merge_from( $result ); } } return $errors->has_errors() ? $errors : true; } }