add_meta_data( self::IS_FACEBOOK_MANAGED_METADATA_KEY, 'yes', true ); $coupon->save(); $created_offers[] = self::get_offer_response_data( $coupon ); } catch ( \Exception $ex ) { $this->add_error( self::get_error_response_data( self::ERROR_OFFER_CREATE_FAILURE, $ex->getMessage(), $create_offer_data['code'] ) ); } } return [ 'created_offers' => $created_offers ]; } /** * @param array $create_offer_data * @return ?WC_Coupon A WC_Coupon object if one was able to be created. */ private function create_coupon_from_offer_data( array $create_offer_data ): ?WC_Coupon { $coupon = null; $errors = []; $code = $create_offer_data['code']; $coupon_id = wc_get_coupon_id_by_code( $code ); if ( 0 !== $coupon_id ) { $errors[] = self::get_error_response_data( self::ERROR_OFFER_CODE_ALREADY_EXISTS, '', $code ); } // A target_type of line_item indicates the coupon applies to products. $offer_class = $create_offer_data['offer_class']; if ( 'order' !== $offer_class ) { $errors[] = self::get_error_response_data( self::ERROR_OFFER_CONFIGURATION_NOT_SUPPORTED, 'Only product targeted coupons are supported', $code ); } $percent_off = $create_offer_data['percent_off'] ?? 0; $fixed_amount_off_input = $create_offer_data['fixed_amount_off'] ?? null; // Exclusive or (XOR) on presence of percent off and fixed amount off. if ( ( 0 === $percent_off ) === empty( $fixed_amount_off_input ) ) { $errors[] = self::get_error_response_data( self::ERROR_OFFER_CREATE_FAILURE, 'Exactly one of fixed amount off or percent off is required', $code ); } if ( 0 !== $percent_off ) { if ( ! is_numeric( $percent_off ) || $percent_off < 0 ) { $errors[] = self::get_error_response_data( self::ERROR_OFFER_CREATE_FAILURE, sprintf( 'Invalid percent off: %s', $percent_off ), $code ); } $discount_type = 'percent'; $amount = $percent_off; } else { // Pass errors reference to add currency parsing errors. $discount_type = 'fixed_cart'; $amount = $this->parse_currency_amount( $fixed_amount_off_input, $errors ); } // Validation errors for the creation of this coupon. if ( empty( $errors ) ) { $coupon = new WC_Coupon( $code ); $coupon->set_props( array( 'discount_type' => $discount_type, 'amount' => $amount, 'usage_limit' => $create_offer_data['usage_limit'] ?? 1, ) ); $coupon->set_date_expires( $create_offer_data['end_time'] ?? null ); $tags = $create_offer_data['tags'] ?? []; $coupon->add_meta_data( self::OFFER_TAGS, $tags ); if ( isset( $create_offer_data['email'] ) ) { $coupon->set_email_restrictions( [ $create_offer_data['email'] ] ); } } foreach ( $errors as &$error ) { $error['offer_code'] = $code; } $this->add_errors( $errors ); return $coupon; } public static function parse_currency_amount( ?array $amount_with_currency, array &$errors ): ?string { if ( null === $amount_with_currency ) { return null; } // Amount with decimal separator $amount = $amount_with_currency['amount'] ?? ''; $currency = $amount_with_currency['currency'] ?? ''; if ( ! is_numeric( $amount ) ) { $errors[] = self::get_error_response_data( self::ERROR_OFFER_CREATE_FAILURE, sprintf( 'Invalid amount string: %s', $amount ) ); return null; } $woocommerce_currency = get_woocommerce_currency(); if ( strtolower( $currency ) !== strtolower( $woocommerce_currency ) ) { $errors[] = self::get_error_response_data( self::ERROR_OFFER_CREATE_FAILURE, sprintf( 'Provided currency (%s) does not match store currency (%s)', $currency, $woocommerce_currency ) ); return null; } return $amount; } }