Handler.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. <?php
  2. /*
  3. * This file is part of the overtrue/wechat.
  4. *
  5. * (c) overtrue <i@overtrue.me>
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace EasyWeChat\Payment\Notify;
  11. use Closure;
  12. use EasyWeChat\Kernel\Exceptions\Exception;
  13. use EasyWeChat\Kernel\Support;
  14. use EasyWeChat\Kernel\Support\XML;
  15. use EasyWeChat\Payment\Kernel\Exceptions\InvalidSignException;
  16. use Symfony\Component\HttpFoundation\Response;
  17. abstract class Handler
  18. {
  19. public const SUCCESS = 'SUCCESS';
  20. public const FAIL = 'FAIL';
  21. /**
  22. * @var \EasyWeChat\Payment\Application
  23. */
  24. protected $app;
  25. /**
  26. * @var array
  27. */
  28. protected $message;
  29. /**
  30. * @var string|null
  31. */
  32. protected $fail;
  33. /**
  34. * @var array
  35. */
  36. protected $attributes = [];
  37. /**
  38. * Check sign.
  39. * If failed, throws an exception.
  40. *
  41. * @var bool
  42. */
  43. protected $check = true;
  44. /**
  45. * Respond with sign.
  46. *
  47. * @var bool
  48. */
  49. protected $sign = false;
  50. /**
  51. * @param \EasyWeChat\Payment\Application $app
  52. */
  53. public function __construct($app)
  54. {
  55. $this->app = $app;
  56. }
  57. /**
  58. * Handle incoming notify.
  59. *
  60. * @return \Symfony\Component\HttpFoundation\Response
  61. */
  62. abstract public function handle(Closure $closure);
  63. public function fail(string $message)
  64. {
  65. $this->fail = $message;
  66. }
  67. /**
  68. * @return $this
  69. */
  70. public function respondWith(array $attributes, bool $sign = false)
  71. {
  72. $this->attributes = $attributes;
  73. $this->sign = $sign;
  74. return $this;
  75. }
  76. /**
  77. * Build xml and return the response to WeChat.
  78. *
  79. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  80. */
  81. public function toResponse(): Response
  82. {
  83. $base = [
  84. 'return_code' => is_null($this->fail) ? static::SUCCESS : static::FAIL,
  85. 'return_msg' => $this->fail,
  86. ];
  87. $attributes = array_merge($base, $this->attributes);
  88. if ($this->sign) {
  89. $attributes['sign'] = Support\generate_sign($attributes, $this->app->getKey());
  90. }
  91. return new Response(XML::build($attributes));
  92. }
  93. /**
  94. * Return the notify message from request.
  95. *
  96. * @throws \EasyWeChat\Kernel\Exceptions\Exception
  97. */
  98. public function getMessage(): array
  99. {
  100. if (!empty($this->message)) {
  101. return $this->message;
  102. }
  103. try {
  104. $message = XML::parse(strval($this->app['request']->getContent()));
  105. } catch (\Throwable $e) {
  106. throw new Exception('Invalid request XML: '.$e->getMessage(), 400);
  107. }
  108. if (!is_array($message) || empty($message)) {
  109. throw new Exception('Invalid request XML.', 400);
  110. }
  111. if ($this->check) {
  112. $this->validate($message);
  113. }
  114. return $this->message = $message;
  115. }
  116. /**
  117. * Decrypt message.
  118. *
  119. * @return string|null
  120. *
  121. * @throws \EasyWeChat\Kernel\Exceptions\Exception
  122. */
  123. public function decryptMessage(string $key)
  124. {
  125. $message = $this->getMessage();
  126. if (empty($message[$key])) {
  127. return null;
  128. }
  129. return Support\AES::decrypt(
  130. base64_decode($message[$key], true),
  131. md5($this->app['config']->key),
  132. '',
  133. OPENSSL_RAW_DATA,
  134. 'AES-256-ECB'
  135. );
  136. }
  137. /**
  138. * Validate the request params.
  139. *
  140. * @throws \EasyWeChat\Payment\Kernel\Exceptions\InvalidSignException
  141. * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
  142. */
  143. protected function validate(array $message)
  144. {
  145. $sign = $message['sign'];
  146. unset($message['sign']);
  147. if (Support\generate_sign($message, $this->app->getKey()) !== $sign) {
  148. throw new InvalidSignException();
  149. }
  150. }
  151. /**
  152. * @param mixed $result
  153. */
  154. protected function strict($result)
  155. {
  156. if (true !== $result && is_null($this->fail)) {
  157. $this->fail(strval($result));
  158. }
  159. }
  160. }