ConfigurationValidator.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <?php
  2. /**
  3. * @copyright Vertex. All rights reserved. https://www.vertexinc.com/
  4. * @author Mediotype https://www.mediotype.com/
  5. */
  6. namespace Vertex\Tax\Model;
  7. use Vertex\Exception\ApiException\ConnectionFailureException;
  8. use Vertex\Exception\ConfigurationException;
  9. use Vertex\Services\TaxAreaLookup\RequestInterface;
  10. use Vertex\Services\TaxAreaLookup\RequestInterfaceFactory;
  11. use Vertex\Tax\Api\QuoteInterface;
  12. use Vertex\Tax\Api\TaxAreaLookupInterface;
  13. use Vertex\Tax\Model\ConfigurationValidator\Result;
  14. use Vertex\Tax\Model\ConfigurationValidator\ResultFactory;
  15. use Vertex\Tax\Model\ConfigurationValidator\ValidSampleRequestBuilder;
  16. /**
  17. * Validates the Credentials provided in the configuration
  18. */
  19. class ConfigurationValidator
  20. {
  21. /** @var \Vertex\Tax\Model\Api\Data\AddressBuilder */
  22. private $addressBuilder;
  23. /** @var Config */
  24. private $config;
  25. /** @var RequestInterfaceFactory */
  26. private $lookupRequestFactory;
  27. /** @var QuoteInterface */
  28. private $quote;
  29. /** @var ResultFactory */
  30. private $resultFactory;
  31. /** @var ValidSampleRequestBuilder */
  32. private $sampleRequestFactory;
  33. /** @var TaxAreaLookupInterface */
  34. private $taxAreaLookup;
  35. /**
  36. * @param Config $config
  37. * @param \Vertex\Tax\Model\Api\Data\AddressBuilder $addressBuilder
  38. * @param ValidSampleRequestBuilder $sampleRequestFactory
  39. * @param ResultFactory $resultFactory
  40. * @param QuoteInterface $quote
  41. * @param TaxAreaLookupInterface $taxAreaLookup
  42. * @param RequestInterfaceFactory $lookupRequestFactory
  43. */
  44. public function __construct(
  45. Config $config,
  46. Api\Data\AddressBuilder $addressBuilder,
  47. ValidSampleRequestBuilder $sampleRequestFactory,
  48. ResultFactory $resultFactory,
  49. QuoteInterface $quote,
  50. TaxAreaLookupInterface $taxAreaLookup,
  51. RequestInterfaceFactory $lookupRequestFactory
  52. ) {
  53. $this->config = $config;
  54. $this->addressBuilder = $addressBuilder;
  55. $this->sampleRequestFactory = $sampleRequestFactory;
  56. $this->resultFactory = $resultFactory;
  57. $this->quote = $quote;
  58. $this->taxAreaLookup = $taxAreaLookup;
  59. $this->lookupRequestFactory = $lookupRequestFactory;
  60. }
  61. /**
  62. * Validate configuration
  63. *
  64. * @param string $scopeType
  65. * @param string|int $scopeCode
  66. * @param bool $withoutCallValidation Skip validation that calls Vertex APIs
  67. * @return Result
  68. */
  69. public function execute($scopeType, $scopeCode, $withoutCallValidation = false)
  70. {
  71. /** @var Result $result */
  72. $result = $this->resultFactory->create();
  73. $this->validateConfigurationCompatibility($result, $scopeType, $scopeCode);
  74. if (!$result->isValid()) {
  75. return $result;
  76. }
  77. $this->validateConfigurationComplete($result, $scopeType, $scopeCode);
  78. if (!$result->isValid()) {
  79. return $result;
  80. }
  81. $this->validateAddressComplete($result, $scopeType, $scopeCode);
  82. if (!$result->isValid()) {
  83. return $result;
  84. }
  85. if ($withoutCallValidation) {
  86. return $result;
  87. }
  88. $this->validateAddressLookup($result, $scopeType, $scopeCode);
  89. if ($result->isValid()) {
  90. $this->validateCalculationService($result, $scopeType, $scopeCode);
  91. }
  92. return $result;
  93. }
  94. /**
  95. * Validates that the Company Address has been configured in the admin
  96. *
  97. * @param Result $result
  98. * @param string $scopeType
  99. * @param string|int $scopeCode
  100. * @return Result
  101. */
  102. private function validateAddressComplete(Result $result, $scopeType, $scopeCode)
  103. {
  104. if (!$this->config->getCompanyRegionId($scopeCode, $scopeType)) {
  105. $missing[] = 'Company State';
  106. }
  107. if (!$this->config->getCompanyCountry($scopeCode, $scopeType)) {
  108. $missing[] = 'Company Country';
  109. }
  110. if (!$this->config->getCompanyStreet1($scopeCode, $scopeType)) {
  111. $missing[] = 'Company Street';
  112. }
  113. if (!$this->config->getCompanyCity($scopeCode, $scopeType) ||
  114. !$this->config->getCompanyPostalCode($scopeCode, $scopeType)
  115. ) {
  116. $missing[] = 'one of Company City or Postcode';
  117. }
  118. if (!empty($missing)) {
  119. $result->setMessage('Address Incomplete, Missing: %1');
  120. $result->setArguments([implode(', ', $missing)]);
  121. $result->setValid(false);
  122. } else {
  123. $result->setValid(true);
  124. }
  125. return $result;
  126. }
  127. /**
  128. * Validates the Company Address against the Lookup API
  129. *
  130. * @param Result $result
  131. * @param string $scopeType
  132. * @param string|int $scopeCode
  133. * @return Result
  134. */
  135. private function validateAddressLookup(Result $result, $scopeType, $scopeCode)
  136. {
  137. $street = [
  138. $this->config->getCompanyStreet1($scopeCode, $scopeType),
  139. $this->config->getCompanyStreet2($scopeCode, $scopeType)
  140. ];
  141. $address = $this->addressBuilder->setStreet($street)
  142. ->setCity($this->config->getCompanyCity($scopeCode, $scopeType))
  143. ->setRegionId($this->config->getCompanyRegionId($scopeCode, $scopeType))
  144. ->setPostalCode($this->config->getCompanyPostalCode($scopeCode, $scopeType))
  145. ->setCountryCode($this->config->getCompanyCountry($scopeCode, $scopeType))
  146. ->build();
  147. if ($address->getCountry() !== 'USA') {
  148. // skip validation for non-US countries
  149. $result->setValid(true);
  150. return $result;
  151. }
  152. /** @var RequestInterface $request */
  153. $request = $this->lookupRequestFactory->create();
  154. $request->setPostalAddress($address);
  155. $result->setValid(false);
  156. try {
  157. $this->taxAreaLookup->lookup($request, $scopeCode, $scopeType);
  158. $result->setValid(true);
  159. } catch (ConfigurationException $e) {
  160. $result->setMessage('Unable to connect to Address Validation API');
  161. } catch (ConnectionFailureException $e) {
  162. $result->setMessage('Unable to connect to Address Validation API');
  163. } catch (\Exception $e) {
  164. $result->setMessage('Unable to validate address against API');
  165. }
  166. return $result;
  167. }
  168. /**
  169. * Verify Vertex API connectivity by performing a live tax calculation request.
  170. *
  171. * @param Result $result
  172. * @param string $scopeType
  173. * @param string|int $scopeCode
  174. * @return Result
  175. */
  176. private function validateCalculationService(Result $result, $scopeType, $scopeCode)
  177. {
  178. $request = $this->sampleRequestFactory
  179. ->setScopeType($scopeType)
  180. ->setScopeCode($scopeCode)
  181. ->build();
  182. $result->setValid(false);
  183. try {
  184. $this->quote->request($request, $scopeCode, $scopeType);
  185. $result->setValid(true);
  186. } catch (ConfigurationException $e) {
  187. $result->setMessage('Unable to connect to Calculation API');
  188. } catch (ConnectionFailureException $e) {
  189. $result->setMessage('Unable to connect to Calculation API');
  190. } catch (\Exception $e) {
  191. $result->setMessage('Unable to perform quote request');
  192. }
  193. return $result;
  194. }
  195. /**
  196. * Validates that Catalog Prices are not set to display including tax
  197. *
  198. * @param Result $result
  199. * @param string $scopeType
  200. * @param string|int $scopeCode
  201. * @return Result
  202. */
  203. private function validateConfigurationCompatibility(Result $result, $scopeType, $scopeCode)
  204. {
  205. if ($this->config->isVertexActive($scopeCode, $scopeType)
  206. && $this->config->isDisplayPriceInCatalogEnabled($scopeCode, $scopeType)) {
  207. $result->setValid(false);
  208. $result->setMessage('Automatically Disabled');
  209. } else {
  210. $result->setValid(true);
  211. }
  212. return $result;
  213. }
  214. /**
  215. * Validates that Vertex API, Lookup API, and Trusted ID have been configured in the admin
  216. *
  217. * @param Result $result
  218. * @param string $scopeType
  219. * @param string|int $scopeCode
  220. * @return Result
  221. */
  222. private function validateConfigurationComplete(Result $result, $scopeType, $scopeCode)
  223. {
  224. $missing = [];
  225. if (!$this->config->getVertexHost($scopeCode, $scopeType)) {
  226. $missing[] = 'Vertex API URL';
  227. }
  228. if (!$this->config->getVertexAddressHost($scopeCode, $scopeType)) {
  229. $missing[] = 'Address Lookup API URL';
  230. }
  231. if (!$this->config->getTrustedId($scopeCode, $scopeType)) {
  232. $missing[] = 'Trusted ID';
  233. }
  234. if (!empty($missing)) {
  235. $result->setMessage('Configuration Incomplete, Missing: %1');
  236. $result->setArguments([implode(', ', $missing)]);
  237. $result->setValid(false);
  238. } else {
  239. $result->setValid(true);
  240. }
  241. return $result;
  242. }
  243. }