AbstractProvider.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. <?php
  2. declare(strict_types=1);
  3. namespace Yansongda\Pay\Provider;
  4. use GuzzleHttp\Psr7\Utils;
  5. use Psr\Http\Client\ClientInterface;
  6. use Psr\Http\Message\MessageInterface;
  7. use Throwable;
  8. use Yansongda\Pay\Contract\HttpClientInterface;
  9. use Yansongda\Pay\Contract\PluginInterface;
  10. use Yansongda\Pay\Contract\ProviderInterface;
  11. use Yansongda\Pay\Contract\ShortcutInterface;
  12. use Yansongda\Pay\Direction\ArrayDirection;
  13. use Yansongda\Pay\Event;
  14. use Yansongda\Pay\Exception\ContainerException;
  15. use Yansongda\Pay\Exception\Exception;
  16. use Yansongda\Pay\Exception\InvalidConfigException;
  17. use Yansongda\Pay\Exception\InvalidParamsException;
  18. use Yansongda\Pay\Exception\InvalidResponseException;
  19. use Yansongda\Pay\Exception\ServiceNotFoundException;
  20. use Yansongda\Pay\Logger;
  21. use Yansongda\Pay\Pay;
  22. use Yansongda\Pay\Rocket;
  23. use Yansongda\Supports\Collection;
  24. use Yansongda\Supports\Pipeline;
  25. use function Yansongda\Pay\should_do_http_request;
  26. abstract class AbstractProvider implements ProviderInterface
  27. {
  28. /**
  29. * @return null|array|Collection|MessageInterface
  30. *
  31. * @throws ContainerException
  32. * @throws InvalidParamsException
  33. * @throws ServiceNotFoundException
  34. */
  35. public function call(string $plugin, array $params = [])
  36. {
  37. if (!class_exists($plugin) || !in_array(ShortcutInterface::class, class_implements($plugin))) {
  38. throw new InvalidParamsException(Exception::SHORTCUT_NOT_FOUND, "[{$plugin}] is not incompatible");
  39. }
  40. /* @var ShortcutInterface $money */
  41. $money = Pay::get($plugin);
  42. $plugins = $money->getPlugins($params);
  43. if (empty($params['_no_common_plugins'])) {
  44. $plugins = $this->mergeCommonPlugins($plugins);
  45. }
  46. return $this->pay($plugins, $params);
  47. }
  48. /**
  49. * @return null|array|Collection|MessageInterface
  50. *
  51. * @throws ContainerException
  52. * @throws InvalidParamsException
  53. */
  54. public function pay(array $plugins, array $params)
  55. {
  56. Logger::info('[AbstractProvider] 即将进行 pay 操作', func_get_args());
  57. Event::dispatch(new Event\PayStarted($plugins, $params, null));
  58. $this->verifyPlugin($plugins);
  59. /* @var Pipeline $pipeline */
  60. $pipeline = Pay::make(Pipeline::class);
  61. /* @var Rocket $rocket */
  62. $rocket = $pipeline
  63. ->send((new Rocket())->setParams($params)->setPayload(new Collection()))
  64. ->through($plugins)
  65. ->via('assembly')
  66. ->then(fn ($rocket) => $this->ignite($rocket))
  67. ;
  68. Event::dispatch(new Event\PayFinish($rocket));
  69. $destination = $rocket->getDestination();
  70. if (ArrayDirection::class === $rocket->getDirection() && $destination instanceof Collection) {
  71. return $destination->toArray();
  72. }
  73. return $destination;
  74. }
  75. /**
  76. * @throws ContainerException
  77. * @throws ServiceNotFoundException
  78. * @throws InvalidResponseException
  79. * @throws InvalidConfigException
  80. */
  81. public function ignite(Rocket $rocket): Rocket
  82. {
  83. if (!should_do_http_request($rocket->getDirection())) {
  84. return $rocket;
  85. }
  86. /* @var HttpClientInterface $http */
  87. $http = Pay::get(HttpClientInterface::class);
  88. if (!$http instanceof ClientInterface) {
  89. throw new InvalidConfigException(Exception::HTTP_CLIENT_CONFIG_ERROR);
  90. }
  91. Logger::info('[AbstractProvider] 准备请求支付服务商 API', $rocket->toArray());
  92. Event::dispatch(new Event\ApiRequesting($rocket));
  93. try {
  94. $response = $http->sendRequest($rocket->getRadar());
  95. $contents = (string) $response->getBody();
  96. $rocket->setDestination($response->withBody(Utils::streamFor($contents)))
  97. ->setDestinationOrigin($response->withBody(Utils::streamFor($contents)))
  98. ;
  99. } catch (Throwable $e) {
  100. Logger::error('[AbstractProvider] 请求支付服务商 API 出错', ['message' => $e->getMessage(), 'rocket' => $rocket->toArray(), 'trace' => $e->getTrace()]);
  101. throw new InvalidResponseException(Exception::REQUEST_RESPONSE_ERROR, $e->getMessage(), [], $e);
  102. }
  103. Logger::info('[AbstractProvider] 请求支付服务商 API 成功', ['response' => $response, 'rocket' => $rocket->toArray()]);
  104. Event::dispatch(new Event\ApiRequested($rocket));
  105. return $rocket;
  106. }
  107. abstract public function mergeCommonPlugins(array $plugins): array;
  108. /**
  109. * @throws InvalidParamsException
  110. */
  111. protected function verifyPlugin(array $plugins): void
  112. {
  113. foreach ($plugins as $plugin) {
  114. if (is_callable($plugin)) {
  115. continue;
  116. }
  117. if ((is_object($plugin)
  118. || (is_string($plugin) && class_exists($plugin)))
  119. && in_array(PluginInterface::class, class_implements($plugin))) {
  120. continue;
  121. }
  122. throw new InvalidParamsException(Exception::PLUGIN_ERROR, "[{$plugin}] is not incompatible");
  123. }
  124. }
  125. }