diff --git a/WeChat/Contracts/BasicAliPay.php b/WeChat/Contracts/BasicAliPay.php index 2f60801..77fe358 100644 --- a/WeChat/Contracts/BasicAliPay.php +++ b/WeChat/Contracts/BasicAliPay.php @@ -15,6 +15,7 @@ namespace WeChat\Contracts; use WeChat\Exceptions\InvalidArgumentException; +use WeChat\Exceptions\InvalidResponseException; /** * 支付宝支付基类 @@ -91,7 +92,7 @@ abstract class BasicAliPay * 查询支付宝订单状态 * @param string $out_trade_no * @return array|boolean - * @throws \WeChat\Exceptions\InvalidResponseException + * @throws InvalidResponseException */ public function query($out_trade_no = '') { @@ -104,7 +105,7 @@ abstract class BasicAliPay * @param array|string $options 退款参数或退款商户订单号 * @param null $refund_amount 退款金额 * @return array|boolean - * @throws \WeChat\Exceptions\InvalidResponseException + * @throws InvalidResponseException */ public function refund($options, $refund_amount = null) { @@ -117,7 +118,7 @@ abstract class BasicAliPay * 关闭支付宝进行中的订单 * @param array|string $options * @return array|boolean - * @throws \WeChat\Exceptions\InvalidResponseException + * @throws InvalidResponseException */ public function close($options) { @@ -126,23 +127,42 @@ abstract class BasicAliPay return $this->getResult($options); } + /** + * 获取通知数据 + * @param boolean $needSignType 是否需要sign_type字段 + * @return boolean|array + * @throws InvalidResponseException + */ + public function notify($needSignType = false) + { + $data = $_POST; + if (empty($data) || empty($data['sign'])) { + throw new InvalidResponseException('Illegal push request.', 0, $data); + } + $string = $this->getSignContent($data, $needSignType); + $content = wordwrap($this->config->get('public_key'), 64, "\n", true); + $res = "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----"; + if (openssl_verify($string, base64_decode($data['sign']), $res, OPENSSL_ALGO_SHA256) !== 1) { + throw new InvalidResponseException('Data signature verification failed.', 0, $data); + } + return $data; + } + /** * 验证支付宝支付宝通知 * @param array $data 通知数据 - * @param null $sign 数据签名 - * @param boolean $sync - * @return array|bool + * @param null|string $sign 数据签名 + * @return array|boolean + * @throws InvalidResponseException */ - public function verify($data, $sign = null, $sync = false) + protected function verify($data, $sign) { - if (is_null($this->config->get('public_key'))) { - throw new InvalidArgumentException('Missing Config -- [public_key]'); - } - $sign = is_null($sign) ? $data['sign'] : $sign; $content = wordwrap($this->config->get('public_key'), 64, "\n", true); - $string = $sync ? json_encode($data) : $this->getSignContent($data, true); $res = "-----BEGIN PUBLIC KEY-----\n{$content}\n-----END PUBLIC KEY-----"; - return openssl_verify($string, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1 ? $data : false; + if (openssl_verify(json_encode($data, 256), base64_decode($sign), $res, OPENSSL_ALGO_SHA256) !== 1) { + throw new InvalidResponseException('Data signature verification failed.'); + } + return $data; } /** @@ -151,30 +171,28 @@ abstract class BasicAliPay */ protected function getSign() { - if (is_null($this->config->get('private_key'))) { - throw new InvalidArgumentException('Missing Config -- [private_key]'); - } $content = wordwrap($this->config->get('private_key'), 64, "\n", true); $string = "-----BEGIN RSA PRIVATE KEY-----\n{$content}\n-----END RSA PRIVATE KEY-----"; - openssl_sign($this->getSignContent($this->options->get()), $sign, $string, OPENSSL_ALGO_SHA256); + openssl_sign($this->getSignContent($this->options->get(), true), $sign, $string, OPENSSL_ALGO_SHA256); return base64_encode($sign); } /** * 数据签名处理 - * @param array $data - * @param boolean $verify - * @param array $strs + * @param array $data 需要进行签名数据 + * @param boolean $needSignType 是否需要sign_type字段 * @return bool|string */ - private function getSignContent(array $data, $verify = false, $strs = []) + private function getSignContent(array $data, $needSignType = false) { - ksort($data); - foreach ($data as $k => $v) if ($v !== '') { - if ($verify && $k != 'sign' && $k != 'sign_type') array_push($strs, "{$k}={$v}"); - if (!$verify && $v !== '' && !is_null($v) && $k != 'sign' && '@' != substr($v, 0, 1)) array_push($strs, "{$k}={$v}"); + list($attrs,) = [[], ksort($data)]; + if (isset($data['sign'])) unset($data['sign']); + if (empty($needSignType)) unset($data['sign_type']); + foreach ($data as $key => $value) { + if ($value === '' || is_null($value)) continue; + array_push($attrs, "{$key}={$value}"); } - return join('&', $strs); + return join('&', $attrs); } /** @@ -183,15 +201,15 @@ abstract class BasicAliPay */ protected function applyData($options) { - $this->options['biz_content'] = json_encode($this->params->merge($options), JSON_UNESCAPED_UNICODE); - $this->options['sign'] = $this->getSign(); + $this->options->set('biz_content', json_encode($this->params->merge($options), 256)); + $this->options->set('sign', $this->getSign()); } /** * 请求接口并验证访问数据 * @param array $options * @return array|boolean - * @throws \WeChat\Exceptions\InvalidResponseException + * @throws InvalidResponseException */ protected function getResult($options) { @@ -199,18 +217,18 @@ abstract class BasicAliPay $method = str_replace('.', '_', $this->options['method']) . '_response'; $data = json_decode(Tools::get($this->gateway, $this->options->get()), true); if (!isset($data[$method]['code']) || $data[$method]['code'] !== '10000') { - throw new \WeChat\Exceptions\InvalidResponseException( + throw new InvalidResponseException( "Error: " . (empty($data[$method]['code']) ? '' : "{$data[$method]['msg']} [{$data[$method]['code']}]\r\n") . (empty($data[$method]['sub_code']) ? '' : "{$data[$method]['sub_msg']} [{$data[$method]['sub_code']}]\r\n"), $data[$method]['code'], $data ); } - return $this->verify($data[$method], $data['sign'], true); + return $this->verify($data[$method], $data['sign']); } /** - * 生成支付html代码 + * 生成支付HTML代码 * @return string */ protected function buildPayHtml() diff --git a/_test/alipay-app.php b/_test/alipay-app.php index a905328..bbfe18e 100644 --- a/_test/alipay-app.php +++ b/_test/alipay-app.php @@ -22,6 +22,7 @@ try { // 实例支付对象 $pay = \We::AliPayApp($config); // $pay = new \AliPay\App($config); + // 请参考(请求参数):https://docs.open.alipay.com/api_1/alipay.trade.app.pay $result = $pay->apply([ 'out_trade_no' => time(), // 商户订单号 @@ -30,7 +31,7 @@ try { ]); echo '
'; var_export($result); -} catch (Exception $e) { +} catch (\Exception $e) { echo $e->getMessage(); } diff --git a/_test/alipay-notify.php b/_test/alipay-notify.php index 9e9b84b..5802728 100644 --- a/_test/alipay-notify.php +++ b/_test/alipay-notify.php @@ -18,20 +18,21 @@ include "../include.php"; // 2. 准备公众号配置参数 $config = include "./alipay.php"; -// 实例支付对象 -$pay = \We::AliPayApp($config); -// $pay = new \AliPay\App($config); -if ($pay->verify($_POST)) { - file_put_contents('notify.txt', "收到来自支付宝的异步通知\r\n", FILE_APPEND); - file_put_contents('notify.txt', '订单号:' . $_POST['out_trade_no'] . "\r\n", FILE_APPEND); - file_put_contents('notify.txt', '订单金额:' . $_POST['total_amount'] . "\r\n\r\n", FILE_APPEND); -} else { - file_put_contents('notify.txt', "收到异步通知\r\n", FILE_APPEND); -} - -// 下面是支付通知处理 -$pay = new \AliPay\App($config); -$notify = $pay->verify($_POST); -if (in_array($notify['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) { - // @todo 更新订单状态,支付完成 +try { + // 实例支付对象 + $pay = \We::AliPayApp($config); + // $pay = new \AliPay\App($config); + if ($data = $pay->notify()) { + if (in_array($data['trade_status'], ['TRADE_SUCCESS', 'TRADE_FINISHED'])) { + // @todo 更新订单状态,支付完成 + } + file_put_contents('notify.txt', "收到来自支付宝的异步通知\r\n", FILE_APPEND); + file_put_contents('notify.txt', '订单号:' . $data['out_trade_no'] . "\r\n", FILE_APPEND); + file_put_contents('notify.txt', '订单金额:' . $data['total_amount'] . "\r\n\r\n", FILE_APPEND); + } else { + file_put_contents('notify.txt', "收到异步通知\r\n", FILE_APPEND); + } +} catch (\Exception $e) { + // 异常处理 + echo $e->getMessage(); } diff --git a/_test/alipay.php b/_test/alipay.php index b803253..4686507 100644 --- a/_test/alipay.php +++ b/_test/alipay.php @@ -1,4 +1,17 @@ true,