<?php
namespace Payment\Common\Weixin; use GuzzleHttp\Client; use Payment\Common\BaseData; use Payment\Common\BaseStrategy; use Payment\Common\PayException; use Payment\Common\WxConfig; use Payment\Utils\ArrayUtil; use Payment\Utils\DataParser; abstract class WxBaseStrategy implements BaseStrategy { protected $reqUrl = 'https://api.mch.weixin.qq.com/{debug}/pay/unifiedorder'; protected $config; protected $reqData; public function __construct(array $config) { try { $this->config = new WxConfig($config); } catch (PayException $e) { throw $e; } } protected function sendReq($xml) { $url = $this->reqUrl; if (is_null($url)) { throw new PayException('目前不支持该接口。请联系开发者添加'); } if ($this->config->useSandbox) { $url = str_ireplace('{debug}', WxConfig::SANDBOX_PRE, $url); } else { $url = str_ireplace('{debug}/', '', $url); } $client = new Client([ 'timeout' => '10.0' ]); $options = [ 'body' => $xml, 'cert' => $this->config->appCertPem, 'ssl_key' => $this->config->appKeyPem, 'verify' => $this->config->cacertPath, 'http_errors' => false ]; $response = $client->request('POST', $url, $options); if ($response->getStatusCode() != '200') { throw new PayException('网络发生错误，请稍后再试curl返回码：' . $response->getReasonPhrase()); } $body = $response->getBody()->getContents(); $retData = DataParser::toArray($body); if (strtoupper($retData['return_code']) != 'SUCCESS') { throw new PayException('微信返回错误提示：' . $retData['return_msg']); } if (strtoupper($retData['result_code']) != 'SUCCESS') { $msg = $retData['err_code_des'] ? $retData['err_code_des'] : $retData['err_msg']; throw new PayException('微信返回错误提示：' . $msg); } return $retData; } public function handle(array $data) { $buildClass = $this->getBuildDataClass(); try { $this->reqData = new $buildClass($this->config, $data); } catch (PayException $e) { throw $e; } $this->reqData->setSign(); $xml = DataParser::toXml($this->reqData->getData()); try { $ret = $this->sendReq($xml); } catch (PayException $e) { throw $e; } $flag = $this->verifySign($ret); if (!$flag) { throw new PayException('微信返回数据被篡改。请检查网络是否安全！'); } return $this->retData($ret); } protected function retData(array $ret) { return $ret; } protected function verifySign(array $retData) { $retSign = $retData['sign']; $values = ArrayUtil::removeKeys($retData, ['sign', 'sign_type']); $values = ArrayUtil::paraFilter($values); $values = ArrayUtil::arraySort($values); $signStr = ArrayUtil::createLinkstring($values); $signStr .= '&key=' . $this->config->md5Key; switch ($this->config->signType) { case 'MD5': $sign = md5($signStr); break; case 'HMAC-SHA256': $sign = hash_hmac('sha256', $signStr, $this->config->md5Key); break; default: $sign = ''; } return strtoupper($sign) === $retSign; } } 