errors = []; } protected function add_error( array $error ): void { $this->errors[] = $error; } protected function add_errors( array $errors ): void { $this->errors = array_merge( $this->errors, $errors ); } protected function get_errors(): array { return $this->errors; } final public static function register_endpoints(): void { CreateOffersEndpoint::register_endpoint(); GetOffersEndpoint::register_endpoint(); DeleteOffersEndpoint::register_endpoint(); } private static function register_endpoint() { add_action( 'rest_api_init', function () { register_rest_route( self::API_NAMESPACE, self::ROUTE, [ 'methods' => static::get_method(), 'callback' => [ static::class, 'execute_static' ], 'permission_callback' => '__return_true', ], ); } ); } abstract protected static function get_method(): string; abstract protected function execute_endpoint( array $params ): array; final public static function execute_static( WP_REST_Request $request ): WP_REST_Response { $endpoint = new static(); return $endpoint->execute_with_validation( $request ); } private function execute_with_validation( WP_REST_Request $request ): WP_REST_Response { $fb_integration = facebook_for_woocommerce()->get_integration(); $offer_management_enabled_by_merchant = $fb_integration->is_facebook_managed_coupons_enabled(); if ( ! $offer_management_enabled_by_merchant ) { $this->add_error( self::get_error_response_data( self::ERROR_OFFER_MANAGEMENT_DISABLED, 'Not enabled by seller' ) ); return $this->get_request_response( [], self::HTTP_FORBIDDEN ); } $offer_management_enabled_by_fb = facebook_for_woocommerce()->get_rollout_switches()->is_switch_enabled( RolloutSwitches::SWITCH_OFFER_MANAGEMENT_ENABLED ); if ( ! $offer_management_enabled_by_fb ) { $this->add_error( self::get_error_response_data( self::ERROR_OFFER_MANAGEMENT_DISABLED, 'Not enabled by Meta' ) ); return $this->get_request_response( [], self::HTTP_FORBIDDEN ); } $jwt = $request->get_params()['jwt_params'] ?? null; if ( null === $jwt ) { $this->add_error( self::get_error_response_data( self::ERROR_JWT_NOT_FOUND ) ); return $this->get_request_response( [], self::HTTP_BAD_REQUEST ); } $params = $this->get_decoded_request_params( $jwt ); if ( null === $params ) { return $this->get_request_response( [], self::HTTP_UNAUTHORIZED ); } $jwt_catalog_id = $params['aud'] ?? ''; if ( empty( $jwt_catalog_id ) || $jwt_catalog_id !== $fb_integration->get_product_catalog_id() ) { $this->add_error( self::get_error_response_data( self::ERROR_CATALOG_ID_MISMATCH, sprintf( 'Platform Catalog ID: %s, Request Catalog ID: %s', $fb_integration->get_product_catalog_id(), $jwt_catalog_id ) ) ); return $this->get_request_response( [], self::HTTP_FORBIDDEN ); } try { $response_data = $this->execute_endpoint( $params['payload'] ); return $this->get_request_response( $response_data ); } catch ( \Exception $ex ) { $this->add_error( self::get_error_response_data( self::ERROR_OFFER_MANAGEMENT_ERROR, $ex->getMessage() ) ); return $this->get_request_response( [], self::HTTP_ERROR ); } } protected function get_decoded_request_params( string $jwt ): ?array { try { $decoded_params = RequestVerification::decode_jwt_with_retries( $jwt ); } catch ( JWTExpiredException $ex ) { $this->add_error( self::get_error_response_data( self::ERROR_JWT_EXPIRED ) ); return null; } catch ( \Exception $ex ) { $this->add_error( self::get_error_response_data( self::ERROR_JWT_DECODE_FAILURE, $ex->getMessage() ) ); return null; } return $decoded_params; } protected static function get_offer_response_data( WC_Coupon $coupon ): array { $is_percent_off = 'percent' === $coupon->get_discount_type(); $percent_off = $is_percent_off ? (int) $coupon->get_amount() : null; $fixed_amount_off = $is_percent_off ? null : [ 'amount' => $coupon->get_amount(), 'currency' => get_woocommerce_currency(), ]; $offer_class = 'order'; return array( 'offer_id' => $coupon->get_id(), 'code' => $coupon->get_code(), 'percent_off' => $percent_off, 'fixed_amount_off' => $fixed_amount_off, 'offer_class' => $offer_class, 'end_time' => $coupon->get_date_expires() ? $coupon->get_date_expires()->getTimestamp() : 0, 'usage_limit' => $coupon->get_usage_limit() ?? 0, 'usage_count' => $coupon->get_usage_count() ?? 0, ); } protected static function get_error_response_data( string $error_type, string $error_message = '', ?string $offer_code = null ): array { return [ 'error_type' => $error_type, 'error_message' => $error_message, 'offer_code' => $offer_code, ]; } protected static function get_params_value_enforced( string $field_name, array $params ) { if ( array_key_exists( $field_name, $params ) ) { return $params[ $field_name ]; } throw new \OutOfBoundsException( sprintf( 'Field: %s does not exist in request params. Params fields: %s', $field_name, wp_json_encode( array_keys( $params ) ) ) ); } private function get_request_response( array $response_data, int $status_code = self::HTTP_OK ): WP_REST_Response { $response_data = [ 'data' => $response_data, 'errors' => $this->errors, ]; return new WP_REST_Response( $response_data, $status_code ); } }