Completed
Push — master ( 158640...6228b6 )
by Carlos
02:38 queued 01:15
created

src/Payment/Notify/Handler.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of the overtrue/wechat.
5
 *
6
 * (c) overtrue <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace EasyWeChat\Payment\Notify;
13
14
use EasyWeChat\Kernel\Exceptions\Exception;
15
use EasyWeChat\Kernel\Support;
16
use EasyWeChat\Kernel\Support\XML;
17
use EasyWeChat\Payment\Kernel\Exceptions\InvalidSignException;
18
use Symfony\Component\HttpFoundation\Response;
19
20
abstract class Handler
21
{
22
    const SUCCESS = 'SUCCESS';
23
    const FAIL = 'FAIL';
24
25
    /**
26
     * @var \EasyWeChat\Payment\Application
27
     */
28
    protected $app;
29
30
    /**
31
     * @var array
32
     */
33
    protected $message;
34
35
    /**
36
     * @var string|null
37
     */
38
    protected $fail;
39
40
    /**
41
     * @var array
42
     */
43
    protected $attributes = [];
44
45
    /**
46
     * Check sign.
47
     * If failed, throws an exception.
48
     *
49
     * @var bool
50
     */
51
    protected $check = true;
52
53
    /**
54
     * Respond with sign.
55
     *
56
     * @var bool
57
     */
58
    protected $sign = false;
59
60
    /**
61
     * @param \EasyWeChat\Payment\Application $app
62
     */
63
    public function __construct($app)
64
    {
65
        $this->app = $app;
66
    }
67
68
    /**
69
     * Handle incoming notify.
70
     *
71
     * @param callable $callback
72
     *
73
     * @return \Symfony\Component\HttpFoundation\Response
74
     */
75
    abstract public function handle(callable $callback): Response;
76
77
    /**
78
     * @param string $message
79
     */
80
    public function fail(string $message)
81
    {
82
        $this->fail = $message;
83
    }
84
85
    /**
86
     * @param array $attributes
87
     * @param bool  $sign
88
     *
89
     * @return $this
90
     */
91
    public function respondWith(array $attributes, bool $sign = false)
92
    {
93
        $this->attributes = $attributes;
94
        $this->sign = $sign;
95
96
        return $this;
97
    }
98
99
    /**
100
     * Build xml and return the response to WeChat.
101
     *
102
     * @return \Symfony\Component\HttpFoundation\Response
103
     */
104
    public function toResponse(): Response
105
    {
106
        $base = [
107
            'return_code' => is_null($this->fail) ? static::SUCCESS : static::FAIL,
108
            'return_msg' => $this->fail,
109
        ];
110
111
        $attributes = array_merge($base, $this->attributes);
112
113
        if ($this->sign) {
114
            $attributes['sign'] = Support\generate_sign($attributes, $this->app['config']->key);
115
        }
116
117
        return new Response(XML::build($attributes));
118
    }
119
120
    /**
121
     * Return the notify message from request.
122
     *
123
     * @return array
124
     *
125
     * @throws \EasyWeChat\Kernel\Exceptions\Exception
126
     */
127
    public function getMessage(): array
128
    {
129
        if ($this->message) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->message of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
130
            return $this->message;
131
        }
132
133
        try {
134
            $message = XML::parse(strval($this->app['request']->getContent()));
135
        } catch (\Throwable $e) {
136
            throw new Exception('Invalid request XML: '.$e->getMessage(), 400);
137
        }
138
139
        if (!is_array($message) || empty($message)) {
140
            throw new Exception('Invalid request XML.', 400);
141
        }
142
143
        if ($this->check) {
144
            $this->validate($message);
145
        }
146
147
        return $this->message = $message;
148
    }
149
150
    /**
151
     * Decrypt message.
152
     *
153
     * @param string $key
154
     *
155
     * @return string
156
     */
157
    public function decryptMessage(string $key): string
158
    {
159
        $message = $this->getMessage();
160
        if (empty($message[$key])) {
161
            return null;
162
        }
163
164
        return Support\AES::decrypt(
165
            base64_decode($message[$key]), md5($this->app['config']->key), '', OPENSSL_RAW_DATA, 'AES-256-ECB'
166
        );
167
    }
168
169
    /**
170
     * Validate the request params.
171
     *
172
     * @param array $message
173
     *
174
     * @throws \EasyWeChat\Payment\Kernel\Exceptions\InvalidSignException
175
     */
176
    protected function validate(array $message)
177
    {
178
        $sign = $message['sign'];
179
        unset($message['sign']);
180
181
        if (Support\generate_sign($message, $this->app['config']->key) !== $sign) {
182
            throw new InvalidSignException();
183
        }
184
    }
185
186
    /**
187
     * @param mixed $result
188
     */
189
    protected function strict($result)
190
    {
191
        if ($result !== true && is_null($this->fail)) {
192
            $this->fail(strval($result));
193
        }
194
    }
195
}
196