infrastructure/PayboxDirect/src/Paybox.php line 64

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of the Nexylan packages.
  5. *
  6. * (c) Nexylan SAS <contact@nexylan.com>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Nexy\PayboxDirect;
  12. use Doctrine\Common\Annotations\AnnotationRegistry;
  13. use Exception;
  14. use InvalidArgumentException;
  15. use Nexy\PayboxDirect\Enum\Activity;
  16. use Nexy\PayboxDirect\Enum\Currency;
  17. use Nexy\PayboxDirect\Enum\Version;
  18. use Nexy\PayboxDirect\Exception\InvalidRequestPropertiesException;
  19. use Nexy\PayboxDirect\Exception\PayboxException;
  20. use Nexy\PayboxDirect\HttpClient\AbstractHttpClient;
  21. use Nexy\PayboxDirect\HttpClient\GuzzleHttpClient;
  22. use Nexy\PayboxDirect\Request\InquiryRequest;
  23. use Nexy\PayboxDirect\Request\RequestInterface;
  24. use Nexy\PayboxDirect\Response\DirectPlusResponse;
  25. use Nexy\PayboxDirect\Response\DirectResponse;
  26. use Nexy\PayboxDirect\Response\InquiryResponse;
  27. use Nexy\PayboxDirect\Response\ResponseInterface;
  28. use RuntimeException;
  29. use Symfony\Component\OptionsResolver\OptionsResolver;
  30. use Symfony\Component\Validator\Validation;
  31. use Symfony\Component\Validator\Validator\ValidatorInterface;
  32. /**
  33. * @author Sullivan Senechal <soullivaneuh@gmail.com>
  34. *
  35. * @see http://www1.paybox.com/espace-integrateur-documentation/les-solutions-paybox-direct-et-paybox-direct-plus/les-operations-de-caisse-direct-plus/
  36. * @see http://www1.paybox.com/espace-integrateur-documentation/dictionnaire-des-donnees/paybox-direct-et-direct-plus/
  37. */
  38. class Paybox
  39. {
  40. public const API_URL_PRODUCTION = 'https://ppps.paybox.com/PPPS.php';
  41. public const API_URL_RESCUE = 'https://ppps1.paybox.com/PPPS.php';
  42. public const API_URL_TEST = 'https://preprod-ppps.paybox.com/PPPS.php';
  43. public const URL_3DS_PRODUCTION = 'https://tpeweb.paybox.com/cgi/RemoteMPI.cgi';
  44. public const URL_3DS_RESCUE = 'https://tpeweb1.paybox.com/cgi/RemoteMPI.cgi';
  45. public const URL_3DS_TEST = 'https://preprod-tpeweb.paybox.com/cgi/RemoteMPI.cgi';
  46. public const INVALID_CREDENTIALS_MESSAGE = 'Paybox SDK: invalid change of credentials';
  47. private readonly ValidatorInterface $validator;
  48. private readonly AbstractHttpClient $httpClient;
  49. private array $options;
  50. public function __construct(array $options = [], AbstractHttpClient $httpClient = null)
  51. {
  52. $resolver = new OptionsResolver();
  53. $this->configureOptions($resolver);
  54. $this->options = $resolver->resolve($options);
  55. AnnotationRegistry::registerLoader('class_exists');
  56. $this->validator = Validation::createValidatorBuilder()
  57. ->enableAnnotationMapping(true)
  58. ->addDefaultDoctrineAnnotationReader()
  59. ->getValidator();
  60. $this->httpClient = $httpClient instanceof \Nexy\PayboxDirect\HttpClient\AbstractHttpClient ? $httpClient : new GuzzleHttpClient();
  61. $this->httpClient->setOptions($this->options);
  62. $this->httpClient->init();
  63. }
  64. public function sendDirectRequest(RequestInterface $request): ResponseInterface
  65. {
  66. if ($request->getRequestType() >= RequestInterface::SUBSCRIBER_AUTHORIZE) {
  67. throw new InvalidArgumentException(
  68. 'Direct Plus requests must be passed onto ' . static::class . '::sendDirectPlusRequest method.'
  69. );
  70. }
  71. if ($request instanceof InquiryRequest) {
  72. throw new InvalidArgumentException(
  73. 'Inquiry requests must be passed onto ' . static::class . '::sendInquiryRequest method.'
  74. );
  75. }
  76. return $this->request($request);
  77. }
  78. public function sendDirectPlusRequest(RequestInterface $request): ResponseInterface
  79. {
  80. if ($request->getRequestType() < RequestInterface::SUBSCRIBER_AUTHORIZE) {
  81. throw new InvalidArgumentException(
  82. 'Direct requests must be passed onto ' . static::class . '::sendDirectRequest method.'
  83. );
  84. }
  85. return $this->request($request, DirectPlusResponse::class);
  86. }
  87. public function sendInquiryRequest(InquiryRequest $request): ResponseInterface
  88. {
  89. return $this->request($request, InquiryResponse::class);
  90. }
  91. /** *
  92. * @throws InvalidRequestPropertiesException
  93. * @throws PayboxException
  94. */
  95. private function request(
  96. RequestInterface $request,
  97. string $responseClass = DirectResponse::class
  98. ): ResponseInterface
  99. {
  100. $errors = $this->validator->validate($request);
  101. if ($errors->count() > 0) {
  102. throw new InvalidRequestPropertiesException($request, $errors);
  103. }
  104. return $this->httpClient->call($request->getRequestType(), $request->getParameters(), $responseClass);
  105. }
  106. /**
  107. * Paybox base options validation
  108. */
  109. private function configureOptions(OptionsResolver $resolver): void
  110. {
  111. $resolver->setDefaults([
  112. 'timeout' => 10,
  113. 'production' => false,
  114. 'paybox_default_currency' => Currency::EURO,
  115. ]);
  116. $resolver->setDefined([
  117. 'paybox_default_activity',
  118. ]);
  119. $resolver->setRequired([
  120. 'paybox_version', // Paybox Direct Plus protocol
  121. 'paybox_site',
  122. 'paybox_rank',
  123. 'paybox_identifier',
  124. 'paybox_key',
  125. ]);
  126. $resolver->setAllowedTypes('timeout', 'int');
  127. $resolver->setAllowedTypes('production', 'bool');
  128. $resolver->setAllowedTypes('paybox_version', 'string');
  129. $resolver->setAllowedTypes('paybox_default_currency', 'int');
  130. $resolver->setAllowedTypes('paybox_site', 'string');
  131. $resolver->setAllowedTypes('paybox_rank', 'string');
  132. $resolver->setAllowedTypes('paybox_identifier', 'string');
  133. $resolver->setAllowedTypes('paybox_key', 'string');
  134. $resolver->setAllowedValues('paybox_version', Version::getConstants());
  135. $resolver->setAllowedValues('paybox_default_activity', Activity::getConstants());
  136. }
  137. /**
  138. * @throws Exception
  139. */
  140. public function setCredentials(
  141. ?string $site,
  142. ?string $rank,
  143. ?string $identifier,
  144. ?string $key,
  145. ?string $version = null
  146. ): void
  147. {
  148. if (empty($site) || empty($rank) || empty($identifier) || empty($key)) {
  149. throw new Exception(self::INVALID_CREDENTIALS_MESSAGE);
  150. }
  151. $this->options['paybox_site'] = $site;
  152. $this->options['paybox_rank'] = $rank;
  153. $this->options['paybox_identifier'] = $identifier;
  154. $this->options['paybox_key'] = $key;
  155. if (!empty($version)) {
  156. $version = strtoupper($version);
  157. $allowedVersions = Version::getConstants();
  158. if (empty($allowedVersions[$version])) {
  159. throw new RuntimeException(self::INVALID_CREDENTIALS_MESSAGE);
  160. }
  161. $this->options['paybox_version'] = $allowedVersions[$version];
  162. }
  163. $this->httpClient->setOptions($this->options);
  164. }
  165. /**
  166. * Get parameters that have been set for passed request object
  167. */
  168. public function getParametersSet(RequestInterface $request): array
  169. {
  170. return $this->httpClient->getParameters($request->getRequestType(), $request->getParameters());
  171. }
  172. /**
  173. * Get 3DSecure URL for specified environment
  174. */
  175. public function get3dsUrl(): string
  176. {
  177. if (true === $this->options['production']) {
  178. return self::URL_3DS_PRODUCTION;
  179. }
  180. return self::URL_3DS_TEST;
  181. }
  182. }