$scenario A mailingScenario. * @return list * @throws ApiException When Canada Post cannot be asked, or says no for a reason other than "no services". */ public function prices( array $scenario ): array { for ( $attempt = 0; ; ++$attempt ) { try { $data = $this->client->postJson( self::PATH, $scenario ); break; } catch ( ApiException $e ) { if ( $e->hasErrorCode( '9111' ) ) { return array(); } $services = isset( $scenario['services'] ) && is_array( $scenario['services'] ) ? $scenario['services'] : array(); $left = $e->hasErrorCode( '9112' ) ? self::withoutNamedServices( $services, $e->getMessage() ) : $services; if ( $attempt >= self::MAX_RETRIES || count( $left ) === count( $services ) ) { throw $e; } if ( array() === $left ) { return array(); } $scenario['services'] = $left; } } $quotes = array(); foreach ( $data as $row ) { $quote = is_array( $row ) ? Quote::fromApi( $row ) : null; if ( null !== $quote ) { $quotes[] = $quote; } } return $quotes; } /** * $services minus every service code named in $message. * * @param array $services * @return list */ public static function withoutNamedServices( array $services, string $message ): array { preg_match_all( '/\b(?:DOM|USA|INT)\.[A-Z]+(?:\.[A-Z]+)*/', $message, $match ); return array_values( array_diff( array_map( 'strval', $services ), $match[0] ) ); } }