id = 'dots_ausm'; $this->method_title = __( 'Advanced USPS Shipping Method', 'advanced-usps-shipping-method' ); $this->method_description = __( 'The USPS extension obtains rates dynamically from the USPS API during cart/checkout.', 'advanced-usps-shipping-method' ); $this->services = include ADVANCED_USPS_SHIPPING_METHOD_DIR . '/includes/data-ausm-services.php'; $this->api_services = include ADVANCED_USPS_SHIPPING_METHOD_DIR . '/includes/data-ausm-api-services.php'; $this->init(); } public function init(){ $this->ausm_admin_object = new Advanced_Usps_Shipping_Method_Admin( ADVANCED_USPS_SHIPPING_METHOD_SLUG, ADVANCED_USPS_SHIPPING_METHOD_VERSION ); $this->ausm_config = !empty(get_option('ausm_config')) ? get_option('ausm_config') : array(); $this->enabled = isset( $this->ausm_config['ausm_status'] ) && 'on' === $this->ausm_config['ausm_status'] ? 'yes' : 'no'; $this->request_mode = isset( $this->ausm_config['ausm_request_mode'] ) && !empty( $this->ausm_config['ausm_request_mode'] ) ? $this->ausm_config['ausm_request_mode'] : 'legacy'; $this->api_mode = isset( $this->ausm_config['ausm_api_mode'] ) && !empty( $this->ausm_config['ausm_api_mode'] ) ? $this->ausm_config['ausm_api_mode'] : 'sandbox'; $this->client_id = isset( $this->ausm_config['ausm_client_id'] ) && !empty( $this->ausm_config['ausm_client_id'] ) ? $this->ausm_config['ausm_client_id'] : ''; $this->client_secret = isset( $this->ausm_config['ausm_client_secret'] ) && !empty( $this->ausm_config['ausm_client_secret'] ) ? $this->ausm_config['ausm_client_secret'] : ''; $this->debug = isset( $this->ausm_config['ausm_debug_mode'] ) && 'on' === $this->ausm_config['ausm_debug_mode'] ? true : false; $this->origin = isset( $this->ausm_config['ausm_origin'] ) && !empty( $this->ausm_config['ausm_origin'] ) ? $this->ausm_config['ausm_origin'] : (new WC_Countries())->get_base_postcode(); $this->user_id = isset( $this->ausm_config['ausm_user_id'] ) && !empty( $this->ausm_config['ausm_user_id'] ) ? $this->ausm_config['ausm_user_id'] : $this->default_user_id; $this->custom_services = isset( $this->ausm_config['ausm_services'] ) && !empty( $this->ausm_config['ausm_services'] ) ? $this->ausm_config['ausm_services'] : array(); $this->api_custom_services = isset( $this->ausm_config['ausm_api_services'] ) && !empty( $this->ausm_config['ausm_api_services'] ) ? $this->ausm_config['ausm_api_services'] : array(); $this->title = isset( $this->ausm_config['ausm_default_title'] ) && !empty( $this->ausm_config['ausm_default_title'] ) ? $this->ausm_config['ausm_default_title'] : ''; $this->default_rate = isset( $this->ausm_config['ausm_default_rate'] ) && !empty( $this->ausm_config['ausm_default_rate'] ) ? $this->ausm_config['ausm_default_rate'] : ''; $this->cheapest_rate = isset( $this->ausm_config['ausm_cheapest_rate'] ) && ( 'on' === $this->ausm_config['ausm_cheapest_rate'] ) ? true : false; $this->packing_method = 'per_item'; $this->rates_preference = isset( $this->ausm_config['ausm_rates_preference'] ) && !empty( $this->ausm_config['ausm_rates_preference'] ) ? $this->ausm_config['ausm_rates_preference'] : 'commercial'; $this->disable_commercial_rates = false; } /** * Calculate_shipping function. * * @accesss public * @param mixed $package * @return void */ public function calculate_shipping( $package = array() ) { if( $this->request_mode === 'api-base' ) { $found_rates = $this->ausm_api_base_calculate_shipping( $package ); } else { $found_rates = $this->ausm_legacy_calculate_shipping( $package ); } // Add rates if ( $found_rates ) { if ( $this->cheapest_rate ) { $cheapest_rate = ''; foreach ( $found_rates as $key => $rate ) { if ( ! $cheapest_rate || $cheapest_rate['cost'] > $rate['cost'] ) { $cheapest_rate = $rate; } } // phpcs:ignore // $cheapest_rate['label'] = $this->method_title; $this->add_rate( $cheapest_rate ); } else { uasort( $found_rates, array( $this, 'sort_rates' ) ); foreach ( $found_rates as $key => $rate ) { $this->add_rate( $rate ); } } } } /** * Prepare request for legacy SOAP call. * * @accesss public * @param mixed $package * @return Array found rates */ public function ausm_legacy_calculate_shipping( $package = array() ) { global $woocommerce; $domestic = in_array( $package['destination']['country'], $this->domestic, true ) ? true : false; $this->debug( __( 'Advanced USPS debug mode is on - to hide these messages, turn debug mode off in the settings.', 'advanced-usps-shipping-method' ) ); $package_requests = $this->get_package_requests($package); $api = $domestic ? 'RateV4' : 'IntlRateV2'; libxml_use_internal_errors( true ); if( $package_requests ) { $request = '<' . $api . 'Request USERID="' . $this->user_id . '">' . "\n"; $request .= '2' . "\n"; foreach ( $package_requests as $key => $package_request ) { $request .= $package_request['request_data']; } $request .= '' . "\n"; $request = 'API=' . $api . '&XML=' . str_replace( array( "\n", "\r" ), '', $request ); // phpcs:disable // $transient = 'ausm_quote_' . md5( $request ); // $cached_response = get_transient( $transient ); $this->debug( 'Advanced USPS REQUEST:
' . print_r( htmlspecialchars( $request ), true ) . '
' ); // phpcs:enable $response = wp_remote_post( $this->endpoint, array( 'timeout' => 3, 'sslverify' => 0, 'body' => $request, ) ); if ( is_wp_error( $response ) ) { $error_string = $response->get_error_message(); $this->debug( 'Advanced USPS REQUEST FAILED' . $error_string ); if ( $this->default_rate && ! empty( $this->custom_services ) ) { $default_title = !empty($this->title) ? $this->title : $this->method_title; $this->add_rate( array( 'id' => $this->id . '_default_rate', 'label' => $default_title, 'cost' => $this->default_rate, 'sort' => 0, ) ); } $response = false; } else { $response = $response['body']; // phpcs:ignore $this->debug( 'Advanced USPS RESPONSE:
' . print_r( htmlspecialchars( $response ), true ) . '
' ); // phpcs:ignore // set_transient( $transient, $response, DAY_IN_SECONDS * 30 ); } if ( $response ) { $usps_packages = simplexml_load_string( $response ); if ( ! ( $usps_packages ) ) { $this->debug( 'Advanced Failed loading XML', 'error' ); } if ( ! is_object( $usps_packages ) && ! is_a( $usps_packages, 'SimpleXMLElement' ) ) { $this->debug( 'Advanced Invalid XML response format', 'error' ); } if ( ! empty( $usps_packages ) ) { foreach ( $usps_packages as $usps_package ) { if ( ! $usps_package || ! is_object( $usps_package ) ) { continue; } // Get package data $data_parts = explode( ':', $usps_package->attributes()->ID ); if ( count( $data_parts ) < 6 ) { $valid_response = false; // when the request has invalid ID or no valid address was found. continue; } list( $package_item_id, $cart_item_qty, $package_length, $package_width, $package_height, $package_weight ) = $data_parts; $quotes = $usps_package->children(); if ( $this->debug ) { $found_quotes = array(); foreach ( $quotes as $quote ) { // phpcs:disable if ( $domestic ) { $code = strval( $quote->attributes()->CLASSID ); $name = strip_tags( htmlspecialchars_decode( (string) $quote->{'MailService'} ) ); } else { $code = strval( $quote->attributes()->ID ); $name = strip_tags( htmlspecialchars_decode( (string) $quote->{'SvcDescription'} ) ); } // phpcs:enable if ( $name && $code ) { $found_quotes[ $code ] = $name; } elseif ( $name ) { $found_quotes[ $code . '-' . sanitize_title( $name ) ] = $name; } } if ( $found_quotes ) { ksort( $found_quotes ); $found_quotes_html = ''; foreach ( $found_quotes as $code => $name ) { if ( ! strstr( $name, 'Flat Rate' ) ) { $found_quotes_html .= '
  • ' . $code . ' - ' . $name . '
  • '; } } $this->debug( 'Advanced The following quotes were returned by USPS: If any of these do not display, they may not be enabled in USPS settings.', 'success' ); } } // Loop our known services foreach ( $this->services as $service => $values ) { if ( $domestic && strpos( $service, 'D_' ) !== 0 ) { continue; } if ( ! $domestic && strpos( $service, 'I_' ) !== 0 ) { continue; } $default_title = ( !empty($this->title) ) ? ' (' . $this->title . ')' : ''; $rate_code = (string) $service; $rate_id = $this->id . ':' . $rate_code; $rate_name = (string) $values['name'] . $default_title; $rate_cost = null; $svc_commitment = null; foreach ( $quotes as $quote ) { if ( $domestic ) { $code = strval( $quote->attributes()->CLASSID ); } else { $code = strval( $quote->attributes()->ID ); } if ( '' !== $code && in_array( $code, array_keys( $values['services'] ) ) ) { // phpcs:ignore if ( $domestic ) { if ( $this->disable_commercial_rates ) { if ( ( (float) $quote->{'Rate'} ) > 0.0 ) { $cost = (float) $quote->{'Rate'} * $cart_item_qty; } else { continue; } } else { if ( ! empty( $quote->{'CommercialRate'} ) ) { $cost = (float) $quote->{'CommercialRate'} * $cart_item_qty; } else { $cost = (float) $quote->{'Rate'} * $cart_item_qty; } } } else { if ( ! empty( $quote->{'CommercialPostage'} ) ) { $cost = (float) $quote->{'CommercialPostage'} * $cart_item_qty; } else { $cost = (float) $quote->{'Postage'} * $cart_item_qty; } } // Cost percentage adjustment if ( ! empty( $this->custom_services[ $rate_code ][ $code ]['percentage'] ) ) { $cost = round( $cost + ( $cost * ( floatval( $this->custom_services[ $rate_code ][ $code ]['percentage'] ) / 100 ) ), wc_get_price_decimals() ); } // Cost price adjustment if ( ! empty( $this->custom_services[ $rate_code ][ $code ]['price'] ) ) { $cost = round( $cost + floatval( $this->custom_services[ $rate_code ][ $code ]['price'] ), wc_get_price_decimals() ); } // Enabled check if ( ! isset( $this->custom_services[ $rate_code ][ $code ] ) || empty( $this->custom_services[ $rate_code ][ $code ]['enabled'] ) ) { continue; } if ( is_null( $rate_cost ) ) { $rate_cost = $cost; $svc_commitment = $quote->SvcCommitments; } elseif ( $cost < $rate_cost ) { $rate_cost = $cost; $svc_commitment = $quote->SvcCommitments; } } } if ( $rate_cost ) { if ( ! empty( $svc_commitment ) && strstr( $svc_commitment, 'days' ) ) { $rate_name .= ' (' . current( explode( 'days', $svc_commitment ) ) . ' days)'; } $this->prepare_rate( $rate_code, $rate_id, $rate_name, $rate_cost ); } } } } else { // No rates $this->debug( 'Invalid request; no rates returned', 'error' ); } } } // Ensure rates were found for all packages if ( $this->found_rates ) { foreach ( $this->found_rates as $key => $value ) { if ( $value['packages'] < count( $package_requests ) ) { $this->debug( "Unsetting {$key} - too few packages.", 'error' ); unset( $this->found_rates[ $key ] ); } } } return $this->found_rates; } /** * Prepare API package requests function. * * @accesss public * @param mixed $package * @return Array found rates */ public function ausm_api_base_calculate_shipping( $package = array() ) { $domestic = in_array( $package['destination']['country'], $this->domestic, true ) ? true : false; $this->debug( __( 'Advanced USPS debug mode is on - to hide these messages, turn debug mode off in the settings.', 'advanced-usps-shipping-method' ) ); $package_requests = $this->prepare_auth_package_requests( $package ); $stored_access_tokens = get_transient( 'ausm_api_auth_tokens' ); $token_hash = hash( 'sha256', $this->client_secret.$this->client_id ); $access_token = ''; if ( $stored_access_tokens && !empty($stored_access_tokens) && isset($stored_access_tokens[$token_hash]) && isset($stored_access_tokens[$token_hash][$this->api_mode]) && isset($stored_access_tokens[$token_hash][$this->api_mode]['access_token']) && !empty($stored_access_tokens[$token_hash][$this->api_mode]['access_token']) ) { $access_token = $stored_access_tokens[$token_hash][$this->api_mode]['access_token']; } else { $access_token = $this->ausm_admin_object->ausm_fetch_usps_access_token( $this->client_id, $this->client_secret, $this->api_mode ); if( is_wp_error( $access_token ) ) { $this->debug( 'Advanced USPS API REQUEST FAILED' . $access_token->get_error_message(), 'error' ); return false; } $stored_access_tokens = array(); $stored_access_tokens[$token_hash][$this->api_mode] = array( 'access_token' => $access_token, 'created_at' => time(), ); set_transient( 'ausm_api_auth_tokens', $stored_access_tokens, 8 * HOUR_IN_SECONDS ); } if ( 'production' === $this->api_mode ) { $rates_endpoint_api = $domestic ? 'https://apis.usps.com/prices/v3/total-rates/search' : 'https://apis.usps.com/international-prices/v3/base-rates-list/search'; } else { $rates_endpoint_api = $domestic ? 'https://apis-tem.usps.com/prices/v3/total-rates/search' : 'https://apis-tem.usps.com/international-prices/v3/base-rates-list/search'; } $usps_package_total_weight = 0; if ( $package_requests ) { $usps_api_packages = array(); foreach ( $package_requests as $key => $package_request ) { $usps_package_total_weight += $package_request['request_data']['weight']; try { $send_usps_request_encode = wp_json_encode( $package_request['request_data'] ); $response = wp_remote_post( $rates_endpoint_api, array( 'method' => 'POST', 'timeout' => 60, // phpcs:ignore 'blocking' => true, 'body' => $send_usps_request_encode, 'headers' => array( 'Authorization' => 'Bearer ' . $access_token, 'Content-Type' => 'application/json', ), ) ); if ( is_wp_error( $response ) ) { $error_string = $response->get_error_message(); $this->debug( 'Advanced USPS API REQUEST FAILED' . $error_string, 'error' ); if ( $this->default_rate && ! empty( $this->api_services ) ) { $default_title = !empty($this->title) ? $this->title : $this->method_title; $this->add_rate( array( 'id' => $this->id . '_default_rate', 'label' => $default_title, 'cost' => $this->default_rate, 'sort' => 0, ) ); } $response = false; throw new Exception( 'USPS response Failed' ); } $response_body = json_decode( $response['body'] ); if ( isset( $response_body->fault ) ) { throw new Exception( $response_body->fault->faultstring ); } if ( isset( $response_body->error ) ) { throw new Exception( $response_body->error->message ); } $usps_api_packages[] = array( 'rates_response' => $response_body, 'product_info' => $package_request['package_info'], ); } catch ( Exception $e ) { $this->debug( 'Advanced USPS API REQUEST FAILED' . $e->getMessage(), 'error' ); if ( $this->default_rate && ! empty( $this->api_services ) ) { $default_title = !empty($this->title) ? $this->title : $this->method_title; $this->add_rate( array( 'id' => $this->id . '_default_rate', 'label' => $default_title, 'cost' => $this->default_rate, 'sort' => 0, ) ); } } } $delivery_response = null; if ( $domestic && isset( $this->estimated_delivery_date ) && 'yes' === $this->estimated_delivery_date ) { $delivery_response = $this->prepare_auth_estimated_delivery_date_request( $package, $usps_package_total_weight ); $this->debug( 'USPS - Estimated Delivery Response: ' . print_r( $delivery_response, true ), 'notice' ); // phpcs:ignore } if ( ! empty( $usps_api_packages ) ) { foreach ( $usps_api_packages as $usps_package ) { $quotes = $usps_package['rates_response']->rateOptions; foreach ( $this->api_services as $service => $values ) { if ( $domestic && strpos( $service, 'D_' ) !== 0 ) { continue; } if ( ! $domestic && strpos( $service, 'I_' ) !== 0 ) { continue; } $rate_code = (string) $service; $rate_id = $this->id . ':' . $rate_code; $rate_name = (string) $values['name'] . ' (' . $this->title . ')'; $rate_cost = null; $svc_commitment = null; $rate_delivery = null; $cart_item_qty = $usps_package['product_info']['items']['qty']; foreach ( $quotes as $quote ) { $code_zone = $quote->rates[0]->SKU; $code = substr( $code_zone, 0, 9 ); if ( 'DUXR0XXXX' === $code || 'DPXR0XXXX' === $code || 'DEXR0XXXX' === $code ) { $description = $quote->rates[0]->description; if ( strpos( $description, 'Nonrectangular' ) > 0 ) { $code .= '_1'; } else { $code .= '_2'; } } if ( 'DUXP0XXXU' === $code ) { $code = 'DUXP0XXXX'; } if ( '' !== $code && in_array( $code, array_keys( $values['services'] ), true ) ) { $cost = (float) $quote->rates[0]->price * $cart_item_qty; $mailClass = $quote->rates[0]->mailClass; $delivery_mailclasses = array( 'BOUND_PRINTED_MATTER', 'FIRST-CLASS_PACKAGE_SERVICE', 'LIBRARY_MAIL', 'MEDIA_MAIL', 'PARCEL_SELECT', 'PARCEL_SELECT_LIGHTWEIGHT', 'PRIORITY_MAIL', 'PRIORITY_MAIL_EXPRESS', 'USPS_GROUND_ADVANTAGE' ); if ( ! empty( $delivery_response ) ) { if ( in_array( $mailClass, $delivery_mailclasses, true ) ) { foreach ( $delivery_response as $delivery_data ) { if ( $mailClass === $delivery_data->mailClass ) { $rate_delivery = $delivery_data->delivery->scheduledDeliveryDateTime; } } } } if ( isset( $this->api_custom_services[ $rate_code ][ $code ] ) && empty( $this->api_custom_services[ $rate_code ][ $code ]['enabled'] ) ) { continue; } // Cost percentage adjustment if ( ! empty( $this->api_custom_services[ $rate_code ][ $code ]['percentage'] ) ) { $cost = round( $cost + ( $cost * ( floatval( $this->api_custom_services[ $rate_code ][ $code ]['percentage'] ) / 100 ) ), wc_get_price_decimals() ); } // Cost price adjustment if ( ! empty( $this->api_custom_services[ $rate_code ][ $code ]['price'] ) ) { $cost = round( $cost + floatval( $this->api_custom_services[ $rate_code ][ $code ]['price'] ), wc_get_price_decimals() ); } // phpcs:disable // if ( $domestic ) { // if ( 'DMXX0XXXU' == $code || 'DMXX0XXX5' == $code || 'DMXX0XXXB' == $code ) { // if ( count( $this->mediamail_restriction ) > 0 ) { // $invalid = false; // foreach ( $package['contents'] as $package_item ) { // if ( ! in_array( $package_item['data']->get_shipping_class_id(), $this->mediamail_restriction ) ) { // // Checking if product is virutal. If it is, // // then don't skip media mail. // if ( ! $package_item['data']->is_virtual() ) { // $invalid = true; // } // } // } // if ( $invalid ) { // $this->debug( 'Skipping media mail', 'error' ); // continue 2; // } // } // } // } // phpcs:enable if ( is_null( $rate_cost ) ) { $rate_cost = $cost; } elseif ( $cost < $rate_cost ) { $rate_cost = $cost; } } } if ( $rate_cost ) { $this->prepare_rate_auth_api( $rate_code, $rate_id, $rate_name, $rate_cost, $rate_delivery ); } } } } else { $this->debug( 'Invalid request; no rates returned', 'error' ); } if ( $this->found_rates ) { foreach ( $this->found_rates as $key => $value ) { if ( $value['packages'] < count( $package_requests ) ) { $this->debug( sprintf( 'Unsetting {' . $key . '} - too few packages.' ), 'error' ); unset( $this->found_rates[ $key ] ); } // phpcs:disable // if ( $this->unpacked_item_costs && ! empty( $this->found_rates[ $key ] ) ) { // $this->debug( sprintf( 'Adding unpacked item costs to rate %s', $key ), 'error' ); // $this->found_rates[ $key ]['cost'] += $this->unpacked_item_costs; // } // phpcs:enable } } } return $this->found_rates; } /** * Prepare_rate function. * * @accesss private * @param mixed $rate_code * @param mixed $rate_id * @param mixed $rate_name * @param mixed $rate_cost * @return void */ private function prepare_rate( $rate_code, $rate_id, $rate_name, $rate_cost ) { // Name adjustment if ( ! empty( $this->custom_services[ $rate_code ]['name'] ) ) { $rate_name = $this->custom_services[ $rate_code ]['name']; } // Merging if ( isset( $this->found_rates[ $rate_id ] ) ) { $rate_cost = $rate_cost + $this->found_rates[ $rate_id ]['cost']; $packages = 1 + $this->found_rates[ $rate_id ]['packages']; } else { $packages = 1; } // Sort if ( isset( $this->custom_services[ $rate_code ]['order'] ) ) { $sort = $this->custom_services[ $rate_code ]['order']; } else { $sort = 999; } $this->found_rates[ $rate_id ] = array( 'id' => $rate_id, 'label' => $rate_name, 'cost' => $rate_cost, 'sort' => $sort, 'packages' => $packages, ); } /** * Sort_rates function. * * @accesss public * @param mixed $a * @param mixed $b * @return void */ public function sort_rates( $a, $b ) { if ( $a['sort'] === $b['sort'] ) { return 0; } return ( $a['sort'] < $b['sort'] ) ? -1 : 1; } public function debug( $message, $type = 'notice' ) { if ( $this->debug && ! is_admin() ) { if ( version_compare( WOOCOMMERCE_VERSION, '2.1', '>=' ) ) { wc_add_notice( $message, $type ); } else { global $woocommerce; $woocommerce->add_message( $message ); } } } public function get_package_requests( $package ) { // Choose selected packing switch ( $this->packing_method ) { case 'box_packing': //premium $requests = $this->box_shipping__premium_only( $package ); // @phpstan-ignore-line break; case 'weight_based': //premium $requests = $this->weight_based_shipping__premium_only( $package ); // @phpstan-ignore-line break; case 'per_item': default: $requests = $this->per_item_shipping( $package ); break; } return $requests; } /** * Per_item_shipping function. * * @accesss private * @param mixed $package * @return array */ private function per_item_shipping( $package ) { global $woocommerce; $requests = array(); $domestic = in_array( $package['destination']['country'], $this->domestic, true ) ? true : false; // Get weight of order foreach ( $package['contents'] as $item_id => $values ) { if ( ! $values['data']->needs_shipping() ) { $this->debug( sprintf( wp_kses_post( '(#%s - %s) is virtual. Skipping...', 'advanced-usps-shipping-method' ), $values['data']->get_id(), $values['data']->get_title() ) ); continue; } $item_weight = !empty($values['data']->get_weight()) && $values['data']->get_weight() > 0 ? $values['data']->get_weight() : ''; if ( ! $item_weight ) { $this->debug( sprintf( wp_kses_post( '(#%s - %s) is missing weight. We are assuming 1lb.', 'advanced-usps-shipping-method' ), $values['data']->get_id(), $values['data']->get_title() ) ); $weight = 1; } else { $weight = wc_get_weight( $item_weight, 'lbs' ); } //Default we will use regular size $size = 'REGULAR'; $item_length = $values['data']->get_length() ? $values['data']->get_length() : 0; $item_width = $values['data']->get_width() ? $values['data']->get_width() : 0; $item_height = $values['data']->get_height() ? $values['data']->get_height() : 0; if( $item_length <= 0 ) { $this->debug( sprintf( wp_kses_post( '(#%s - %s) is missing length. We are assuming 0.', 'advanced-usps-shipping-method' ), $values['data']->get_id(), $values['data']->get_title() ) ); } if( $item_width <= 0 ) { $this->debug( sprintf( wp_kses_post( '(#%s - %s) is missing width. We are assuming 0.', 'advanced-usps-shipping-method' ), $values['data']->get_id(), $values['data']->get_title() ) ); } if( $item_height <= 0 ) { $this->debug( sprintf( wp_kses_post( '(#%s - %s) is missing height. We are assuming 0.', 'advanced-usps-shipping-method' ), $values['data']->get_id(), $values['data']->get_title() ) ); } if( $item_length > 0 || $item_width > 0 || $item_height > 0 ){ $dimensions = array( wc_get_dimension( $item_length, 'in' ), wc_get_dimension( $item_width, 'in' ), wc_get_dimension( $item_height, 'in' ) ); //we have converted these data from cm to inches if ( max( $dimensions ) > 12 ) { $size = 'LARGE'; } } else { $dimensions = array( 0, 0, 0 ); } if ( $domestic ) { $request = '' . "\n"; $request .= ' ' . ( $this->ausm_config['ausm_service_type'] ? $this->ausm_config['ausm_service_type'] : 'ONLINE' ) . '' . "\n"; $request .= ' ' . str_replace( ' ', '', strtoupper( $this->origin ) ) . '' . "\n"; $request .= ' ' . strtoupper( substr( $package['destination']['postcode'], 0, 5 ) ) . '' . "\n"; $request .= ' ' . floor( $weight ) . '' . "\n"; $request .= ' ' . number_format( wc_get_weight( $weight - floor( $weight ), 'oz', 'lbs'), 2 ) . '' . "\n"; if ( 'LARGE' === $size ) { $request .= ' RECTANGULAR' . "\n"; } else { $request .= ' ' . "\n"; } $request .= ' ' . $size . '' . "\n"; $request .= ' ' . $dimensions[1] . '' . "\n"; $request .= ' ' . $dimensions[0] . '' . "\n"; $request .= ' ' . $dimensions[2] . '' . "\n"; $request .= ' ' . "\n"; $request .= ' true ' . "\n"; $request .= ' ' . gmdate( 'd-M-Y', ( current_time( 'timestamp' ) + ( 60 * 60 * 24 ) ) ) . '' . "\n"; $request .= '' . "\n"; } else { $request = '' . "\n"; $request .= ' ' . floor( $weight ) . '' . "\n"; $request .= ' ' . number_format( wc_get_weight( $weight - floor( $weight ), 'oz', 'lbs'), 2 ) . '' . "\n"; $request .= ' true ' . "\n"; $request .= ' Package' . "\n"; $request .= ' ' . $values['data']->get_price() . '' . "\n"; $request .= ' ' . strtoupper( WC()->countries->countries[ $package['destination']['country'] ] ) . '' . "\n"; $request .= ' RECTANGULAR' . "\n"; $request .= ' ' . $size . '' . "\n"; $request .= ' ' . $dimensions[1] . '' . "\n"; $request .= ' ' . $dimensions[0] . '' . "\n"; $request .= ' ' . $dimensions[2] . '' . "\n"; $request .= ' ' . "\n"; $request .= ' ' . str_replace( ' ', '', strtoupper( $this->origin ) ) . '' . "\n"; $request .= ' ' . ( 'ONLINE' === $this->ausm_config['ausm_service_type'] ? 'Y' : 'N' ) . '' . "\n"; $request .= '' . "\n"; } $item_data = $values['data'] ? $values['data'] : array(); $packed_items = array(); if ( $item_data ) {// Front-end price call doesn't need this data $item_id = $item_data->get_id(); $packed_items[ $item_id ] = array( 'product_name' => $item_data->get_title(), 'qty' => 1, ); if ( 'simple' !== $item_data->get_type() ) { $packed_items[ $item_id ]['variation_text'] = $this->ausm_get_variation_data_from_variation_id( $item_data->get_id() ); } } $package_info = array( 'items' => $packed_items, 'dimension' => array( 'length' => $dimensions[0], 'width' => $dimensions[1], 'height' => $dimensions[2], 'weight' => $weight, ), 'units' => array( 'dimension' => 'in', 'weight' => 'lbs', ), ); $requests[] = array( 'request_data' => $request, 'package_info' => $package_info, ); } return $requests; } /** * Generate a package ID for the request * * Contains qty and dimension info so we can look at it again later when it comes back from USPS if needed * * @return string */ public function generate_package_id( $id, $qty, $length, $width, $height, $weight ) { return implode( ':', array( $id, $qty, $length, $width, $height, $weight ) ); } /** * Return product variation details from variation ID * * Contains qty and dimension info so we can look at it again later when it comes back from USPS if needed * * @return string */ public function ausm_get_variation_data_from_variation_id( $item_id ) { $_product = new WC_Product_Variation( $item_id ); $variation_data = $_product->get_variation_attributes(); $variation_detail = wc_get_formatted_variation( $variation_data, true ); // this will give all variation detail in one line // $variation_detail = woocommerce_get_formatted_variation( $variation_data, false); // this will give all variation detail one by one return $variation_detail; // $variation_detail will return string containing variation detail which can be used to print on website // return $variation_data; // $variation_data will return only the data which can be used to store variation data } /** * Prepare package requests function. * * @accesss public * @param mixed $package * @return Array request body */ public function prepare_auth_package_requests( $package ) { $requests = array(); $domestic = in_array( $package['destination']['country'], $this->domestic, true ) ? true : false; foreach ( $package['contents'] as $item_id => $values ) { $packed_items = array(); $item_product_object = wc_get_product( $values['data'] ); if ( ! $item_product_object->needs_shipping() ) { continue; } $weight = ! $item_product_object->get_weight() ? 1 : wc_get_weight( $item_product_object->get_weight(), 'lbs' ); $lenght = $item_product_object->get_length() ? wc_get_dimension( $item_product_object->get_length(), 'in' ) : 0; $height = $item_product_object->get_height() ? wc_get_dimension( $item_product_object->get_height(), 'in' ) : 0; $width = $item_product_object->get_width() ? wc_get_dimension( $item_product_object->get_width(), 'in' ) : 0; $request_body = array(); if ( $domestic ) { $request_body = array( 'originZIPCode' => str_replace( ' ', '', strtoupper( $this->origin ) ), 'destinationZIPCode' => strtoupper( substr( $package['destination']['postcode'], 0, 5 ) ), 'weight' => $weight, 'length' => floor( $lenght ), 'width' => floor( $width ), 'height' => floor( $height ), 'priceType' => ( 'commercial' === $this->rates_preference ? 'COMMERCIAL' : 'RETAIL' ), 'mailingDate' => $this->get_estimated_shipping_date(), ); } else { $request_body = array( 'originZIPCode' => str_replace( ' ', '', strtoupper( $this->origin ) ), 'foreignPostalCode' => strtoupper( substr( $package['destination']['postcode'], 0, 5 ) ), 'destinationCountryCode' => strtoupper( $package['destination']['country'] ), 'mailingDate' => $this->get_estimated_shipping_date(), 'weight' => $weight, 'length' => floor( $lenght ), 'width' => floor( $width ), 'height' => floor( $height ), ); } $item_data = wc_get_product( $values['product_id'] ); $packed_items = array( 'product_name' => $item_data->get_title(), 'product_id' => $values['product_id'], 'qty' => $values['quantity'], ); $package_info = array( 'items' => $packed_items, 'dimension' => array( 'length' => $lenght, 'width' => $width, 'height' => $height, 'weight' => $weight, ), 'units' => array( 'dimension' => 'in', 'weight' => 'lbs', ), ); $requests[] = array( 'request_data' => $request_body, 'package_info' => $package_info, ); } return $requests; } /** * Get estimated shipping date function. * * @accesss public * @return string */ public function get_estimated_shipping_date() { //Estimated day and cut off time. $timezone = new DateTimeZone( -5 ); // USPS is Working in central time (GMT-05:00) $request_shipdate = wp_date( 'Y-m-d', null, $timezone ); return $request_shipdate; } public function prepare_auth_estimated_delivery_date_request( $package, $weight ) { $stored_access_tokens = get_transient( 'ausm_api_auth_tokens' ); $token_hash = hash( 'sha256', $this->client_secret.$this->client_id ); $access_token = ''; if ( $stored_access_tokens && ! empty($stored_access_tokens) && isset($stored_access_tokens[$token_hash]) && isset($stored_access_tokens[$token_hash][$this->api_mode]) && isset($stored_access_tokens[$token_hash][$this->api_mode]['access_token']) && !empty($stored_access_tokens[$token_hash][$this->api_mode]['access_token']) ) { $access_token = $stored_access_tokens[$token_hash][$this->api_mode]['access_token']; } else { $access_token = $this->ausm_admin_object->ausm_fetch_usps_access_token( $this->client_id, $this->client_secret, $this->api_mode ); if( is_wp_error( $access_token ) ) { $this->debug( 'Advanced USPS API REQUEST FAILED' . $access_token->get_error_message(), 'error' ); return false; } $stored_access_tokens = array(); $stored_access_tokens[$token_hash][$this->api_mode] = array( 'access_token' => $access_token, 'created_at' => time(), ); set_transient( 'ausm_api_auth_tokens', $stored_access_tokens, 8 * HOUR_IN_SECONDS ); } $mailing_date = $this->get_estimated_shipping_date(); $delivery_request_body = array( 'originZIPCode' => str_replace( ' ', '', strtoupper( $this->origin ) ), 'destinationZIPCode' => strtoupper( substr( $package['destination']['postcode'], 0, 5 ) ), 'acceptanceDate' => $mailing_date, 'mailClass' => 'ALL', 'destinationType' => 'STREET', 'destinationEntryFacilityType' => 'NONE', 'weight' => $weight, ); $delivery_usps_request_encode = http_build_query( $delivery_request_body ); if ( 'production' === $this->api_mode ) { $delivery_endpoint_api = 'https://apis.usps.com/service-standards/v3/standards?' . $delivery_usps_request_encode ; } else { $delivery_endpoint_api = 'https://apis-tem.usps.com/service-standards/v3/estimates?' . $delivery_usps_request_encode; } $response = wp_remote_post( $delivery_endpoint_api, array( 'method' => 'GET', 'timeout' => 60, // phpcs:ignore 'blocking' => true, 'headers' => array( 'Authorization' => 'Bearer ' . $access_token, 'Content-Type' => 'application/json', ), ) ); if ( is_wp_error( $response ) ) { return null; } return json_decode( $response['body'] ); } /** * Prepare rate for API function. * * @accesss protected * @param mixed $rate_code * @param mixed $rate_id * @param mixed $rate_name * @param mixed $rate_cost * @param mixed $delivery_date * @return void */ protected function prepare_rate_auth_api( $rate_code, $rate_id, $rate_name, $rate_cost, $delivery_date ) { // Name adjustment if ( ! empty( $this->api_services[ $rate_code ]['name'] ) ) { $rate_name = $this->api_services[ $rate_code ]['name']; } // Merging if ( isset( $this->found_rates[ $rate_id ] ) ) { $rate_cost = $rate_cost + $this->found_rates[ $rate_id ]['cost']; $packages = 1 + $this->found_rates[ $rate_id ]['packages']; } else { $packages = 1; } // Sort if ( isset( $this->api_services[ $rate_code ]['order'] ) ) { $sort = $this->api_services[ $rate_code ]['order']; } else { $sort = 999; } $this->found_rates[ $rate_id ] = array( 'id' => $rate_id, 'label' => $rate_name, 'cost' => $rate_cost, 'sort' => $sort, 'meta_data' => array( 'usps_est_delivery_time' => $delivery_date, ), 'packages' => $packages, ); } }