namespace, '/' . $this->rest_base, [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'upload_file' ], 'permission_callback' => [ $this, 'check_permission' ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P\w+)/details', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_file_detail' ], 'permission_callback' => [ $this, 'check_permission' ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P\w+)/content', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_file_content' ], 'permission_callback' => [ $this, 'check_permission' ], ] ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P\w+)', [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_file' ], 'permission_callback' => [], ] ); } /** * Create file and respond with file object via API. * * @param WP_REST_Request $request Full data about the request. */ public function upload_file( $request ) { return $this->forward_request( 'upload_file', [ $request ] ); } /** * Retrieve a file content via API. * * @param WP_REST_Request $request - request object. * * @return WP_Error|WP_HTTP_Response */ public function get_file( WP_REST_Request $request ) { $file_id = $request->get_param( 'file_id' ); $as_account = (bool) $request->get_param( 'as_account' ); $file_service = new WC_Payments_File_Service(); $purpose = get_transient( WC_Payments_File_Service::CACHE_KEY_PREFIX_PURPOSE . $file_id . '_' . ( $as_account ? '1' : '0' ) ); if ( ! $purpose ) { $file = $this->forward_request( 'get_file', [ $file_id, $as_account ] ); if ( is_wp_error( $file ) ) { return $this->file_error_response( $file ); } $purpose = $file->get_data()['purpose']; set_transient( WC_Payments_File_Service::CACHE_KEY_PREFIX_PURPOSE . $file_id, $purpose, WC_Payments_File_Service::CACHE_PERIOD ); } if ( ! $file_service->is_file_public( $purpose ) && ! $this->check_permission() ) { return new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to do that.', 'woocommerce-payments' ), [ 'status' => rest_authorization_required_code() ] ); } $result = $this->forward_request( 'get_file_contents', [ $file_id, $as_account ] ); if ( is_wp_error( $result ) ) { return $this->file_error_response( $result ); } /** * WP_REST_Server will convert the response data to JSON prior to output it. * Using this filter to prevent it, and output the data from WP_HTTP_Response instead. */ add_filter( 'rest_pre_serve_request', function ( bool $served, WP_HTTP_Response $response ): bool { echo $response->get_data(); // @codingStandardsIgnoreLine return true; }, 10, 2 ); return new WP_HTTP_Response( base64_decode( $result->get_data()['file_content'] ), // @codingStandardsIgnoreLine 200, [ 'Content-Type' => $result->get_data()['content_type'], 'Content-Disposition' => 'inline', ] ); } /** * Retrieve file details via the API. * * Example response: * { * "id": "file_1Np1S5J5cIRIG92xknlr0iND", * "object": "file", * "created": 1694405421, * "expires_at": 1717733421, * "filename": "Screenshot 2023-09-04 at 5.08.31\u202fPM.png", * "purpose": "dispute_evidence", * "size": 21444, * "title": null, * "type": "png", * } * * @param WP_REST_Request $request Full data about the request. * * @return mixed|WP_Error */ public function get_file_detail( WP_REST_Request $request ) { $file_id = $request->get_param( 'file_id' ); $as_account = (bool) $request->get_param( 'as_account' ); return $this->forward_request( 'get_file', [ $file_id, $as_account ] ); } /** * Retrieve file contents via the API as a base64 encoded string. * * Example response: * { * "content_type": "image\/png", * "file_content": "iVBORw.......", * } * * @param WP_REST_Request $request Full data about the request. * * @return mixed|WP_Error */ public function get_file_content( WP_REST_Request $request ) { $file_id = $request->get_param( 'file_id' ); $as_account = (bool) $request->get_param( 'as_account' ); return $this->forward_request( 'get_file_contents', [ $file_id, $as_account ] ); } /** * Convert error response * * @param WP_Error $error - error. * * @return WP_Error */ private function file_error_response( WP_Error $error ): WP_Error { $error_status_code = 'resource_missing' === $error->get_error_code() ? WP_Http::NOT_FOUND : WP_Http::INTERNAL_SERVER_ERROR; return new WP_Error( $error->get_error_code(), $error->get_error_message(), [ 'status' => $error_status_code ] ); } }