PreparePlugin.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. <?php
  2. declare(strict_types=1);
  3. namespace Yansongda\Pay\Plugin\Alipay;
  4. use Closure;
  5. use Yansongda\Pay\Contract\ConfigInterface;
  6. use Yansongda\Pay\Contract\PluginInterface;
  7. use Yansongda\Pay\Exception\ContainerException;
  8. use Yansongda\Pay\Exception\Exception;
  9. use Yansongda\Pay\Exception\InvalidConfigException;
  10. use Yansongda\Pay\Exception\ServiceNotFoundException;
  11. use Yansongda\Pay\Logger;
  12. use Yansongda\Pay\Pay;
  13. use Yansongda\Pay\Rocket;
  14. use function Yansongda\Pay\get_alipay_config;
  15. use function Yansongda\Pay\get_tenant;
  16. class PreparePlugin implements PluginInterface
  17. {
  18. /**
  19. * @throws ContainerException
  20. * @throws ServiceNotFoundException
  21. * @throws InvalidConfigException
  22. */
  23. public function assembly(Rocket $rocket, Closure $next): Rocket
  24. {
  25. Logger::debug('[alipay][PreparePlugin] 插件开始装载', ['rocket' => $rocket]);
  26. $rocket->mergePayload($this->getPayload($rocket->getParams()));
  27. Logger::info('[alipay][PreparePlugin] 插件装载完毕', ['rocket' => $rocket]);
  28. return $next($rocket);
  29. }
  30. /**
  31. * @throws ContainerException
  32. * @throws ServiceNotFoundException
  33. * @throws InvalidConfigException
  34. */
  35. protected function getPayload(array $params): array
  36. {
  37. $tenant = get_tenant($params);
  38. $config = get_alipay_config($params);
  39. $payload = [
  40. 'app_id' => $config['app_id'] ?? '',
  41. 'method' => '',
  42. 'format' => 'JSON',
  43. 'return_url' => $this->getReturnUrl($params, $config),
  44. 'charset' => 'utf-8',
  45. 'sign_type' => 'RSA2',
  46. 'sign' => '',
  47. 'timestamp' => date('Y-m-d H:i:s'),
  48. 'version' => '1.0',
  49. 'notify_url' => $this->getNotifyUrl($params, $config),
  50. 'app_auth_token' => $this->getAppAuthToken($params, $config),
  51. 'biz_content' => [],
  52. ];
  53. if (!empty($config['app_cert_public_key']) && !empty($config['alipay_root_cert'])) {
  54. $payload = array_merge($payload, ['app_cert_sn' => $this->getAppCertSn($tenant, $config), 'alipay_root_cert_sn' => $this->getAlipayRootCertSn($tenant, $config)]);
  55. }
  56. return $payload;
  57. }
  58. protected function getReturnUrl(array $params, array $config): string
  59. {
  60. if (!empty($params['_return_url'])) {
  61. return $params['_return_url'];
  62. }
  63. return $config['return_url'] ?? '';
  64. }
  65. protected function getNotifyUrl(array $params, array $config): string
  66. {
  67. if (!empty($params['_notify_url'])) {
  68. return $params['_notify_url'];
  69. }
  70. return $config['notify_url'] ?? '';
  71. }
  72. protected function getAppAuthToken(array $params, array $config): string
  73. {
  74. if (!empty($params['_app_auth_token'])) {
  75. return $params['_app_auth_token'];
  76. }
  77. return $config['app_auth_token'] ?? '';
  78. }
  79. /**
  80. * @throws ContainerException
  81. * @throws InvalidConfigException
  82. * @throws ServiceNotFoundException
  83. */
  84. protected function getAppCertSn(string $tenant, array $config): string
  85. {
  86. if (!empty($config['app_public_cert_sn'])) {
  87. return $config['app_public_cert_sn'];
  88. }
  89. $path = $config['app_public_cert_path'] ?? null;
  90. if (is_null($path)) {
  91. throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [app_public_cert_path]');
  92. }
  93. $cert = file_get_contents($path);
  94. $ssl = openssl_x509_parse($cert);
  95. if (false === $ssl) {
  96. throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Parse `app_public_cert_path` Error');
  97. }
  98. $result = $this->getCertSn($ssl['issuer'] ?? [], $ssl['serialNumber'] ?? '');
  99. Pay::get(ConfigInterface::class)->set('alipay.'.$tenant.'.app_public_cert_sn', $result);
  100. return $result;
  101. }
  102. /**
  103. * @throws ContainerException
  104. * @throws InvalidConfigException
  105. * @throws ServiceNotFoundException
  106. */
  107. protected function getAlipayRootCertSn(string $tenant, array $config): string
  108. {
  109. if (!empty($config['alipay_root_cert_sn'])) {
  110. return $config['alipay_root_cert_sn'];
  111. }
  112. $path = $config['alipay_root_cert_path'] ?? null;
  113. if (is_null($path)) {
  114. throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Missing Alipay Config -- [alipay_root_cert_path]');
  115. }
  116. $sn = '';
  117. $exploded = explode('-----END CERTIFICATE-----', file_get_contents($path));
  118. foreach ($exploded as $cert) {
  119. if (empty(trim($cert))) {
  120. continue;
  121. }
  122. $ssl = openssl_x509_parse($cert.'-----END CERTIFICATE-----');
  123. if (false === $ssl) {
  124. throw new InvalidConfigException(Exception::ALIPAY_CONFIG_ERROR, 'Invalid alipay_root_cert');
  125. }
  126. $detail = $this->formatCert($ssl);
  127. if ('sha1WithRSAEncryption' == $detail['signatureTypeLN'] || 'sha256WithRSAEncryption' == $detail['signatureTypeLN']) {
  128. $sn .= $this->getCertSn($detail['issuer'], $detail['serialNumber']).'_';
  129. }
  130. }
  131. $result = substr($sn, 0, -1);
  132. Pay::get(ConfigInterface::class)->set('alipay.'.$tenant.'.alipay_root_cert_sn', $result);
  133. return $result;
  134. }
  135. protected function getCertSn(array $issuer, string $serialNumber): string
  136. {
  137. return md5(
  138. $this->array2string(array_reverse($issuer)).$serialNumber
  139. );
  140. }
  141. protected function array2string(array $array): string
  142. {
  143. $string = [];
  144. foreach ($array as $key => $value) {
  145. $string[] = $key.'='.$value;
  146. }
  147. return implode(',', $string);
  148. }
  149. protected function formatCert(array $ssl): array
  150. {
  151. if (0 === strpos($ssl['serialNumber'] ?? '', '0x')) {
  152. $ssl['serialNumber'] = $this->hex2dec($ssl['serialNumberHex'] ?? '');
  153. }
  154. return $ssl;
  155. }
  156. protected function hex2dec(string $hex): string
  157. {
  158. $dec = '0';
  159. $len = strlen($hex);
  160. for ($i = 1; $i <= $len; ++$i) {
  161. $dec = bcadd(
  162. $dec,
  163. bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i), 0), 0),
  164. 0
  165. );
  166. }
  167. return $dec;
  168. }
  169. }