123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354 |
- <?php
- namespace Yansongda\Pay\Gateways\Wechat;
- use Yansongda\Pay\Contracts\GatewayInterface;
- use Yansongda\Pay\Exceptions\GatewayException;
- use Yansongda\Pay\Exceptions\InvalidArgumentException;
- use Yansongda\Pay\Support\Config;
- use Yansongda\Pay\Traits\HasHttpRequest;
- abstract class Wechat implements GatewayInterface
- {
- use HasHttpRequest;
- /**
- * @var string
- */
- protected $endpoint = 'https://api.mch.weixin.qq.com/';
- /**
- * @var string
- */
- protected $gateway_order = 'pay/unifiedorder';
- /**
- * @var string
- */
- protected $gateway_query = 'pay/orderquery';
- /**
- * @var string
- */
- protected $gateway_close = 'pay/closeorder';
- /**
- * @var string
- */
- protected $gateway_refund = 'secapi/pay/refund';
- /**
- * @var array
- */
- protected $config;
- /**
- * @var \Yansongda\Pay\Support\Config
- */
- protected $user_config;
- /**
- * [__construct description].
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param array $config
- */
- public function __construct(array $config)
- {
- $this->user_config = new Config($config);
- $this->config = [
- 'appid' => $this->user_config->get('app_id', ''),
- 'mch_id' => $this->user_config->get('mch_id', ''),
- 'nonce_str' => $this->createNonceStr(),
- 'sign_type' => 'MD5',
- 'notify_url' => $this->user_config->get('notify_url', ''),
- 'trade_type' => $this->getTradeType(),
- ];
- if ($endpoint = $this->user_config->get('endpoint_url')) {
- $this->endpoint = $endpoint;
- }
- }
- /**
- * pay a order.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param array $config_biz
- *
- * @return mixed
- */
- abstract public function pay(array $config_biz = []);
- /**
- * refund.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @return string|bool
- */
- public function refund($config_biz = [])
- {
- if (isset($config_biz['miniapp'])) {
- $this->config['appid'] = $this->user_config->get('miniapp_id');
- unset($config_biz['miniapp']);
- }
- $this->config = array_merge($this->config, $config_biz);
- $this->unsetTradeTypeAndNotifyUrl();
- return $this->getResult($this->gateway_refund, true);
- }
- /**
- * close a order.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @return array|bool
- */
- public function close($out_trade_no = '')
- {
- $this->config['out_trade_no'] = $out_trade_no;
- $this->unsetTradeTypeAndNotifyUrl();
- return $this->getResult($this->gateway_close);
- }
- /**
- * find a order.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param string $out_trade_no
- *
- * @return array|bool
- */
- public function find($out_trade_no = '')
- {
- $this->config['out_trade_no'] = $out_trade_no;
- $this->unsetTradeTypeAndNotifyUrl();
- return $this->getResult($this->gateway_query);
- }
- /**
- * verify the notify.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param string $data
- * @param string $sign
- * @param bool $sync
- *
- * @return array|bool
- */
- public function verify($data, $sign = null, $sync = false)
- {
- $data = $this->fromXml($data);
- $sign = is_null($sign) ? $data['sign'] : $sign;
- return $this->getSign($data) === $sign ? $data : false;
- }
- /**
- * get trade type config.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @return string
- */
- abstract protected function getTradeType();
- /**
- * pre order.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param array $config_biz
- *
- * @return array
- */
- protected function preOrder($config_biz = [])
- {
- $this->config = array_merge($this->config, $config_biz);
- return $this->getResult($this->gateway_order);
- }
- /**
- * get api result.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param string $path
- * @param bool $cert
- *
- * @return array
- */
- protected function getResult($path, $cert = false)
- {
- $this->config['sign'] = $this->getSign($this->config);
- if ($cert) {
- $data = $this->fromXml($this->post(
- $this->endpoint.$path,
- $this->toXml($this->config),
- [
- 'cert' => $this->user_config->get('cert_client', ''),
- 'ssl_key' => $this->user_config->get('cert_key', ''),
- ]
- ));
- } else {
- $data = $this->fromXml($this->post($this->endpoint.$path, $this->toXml($this->config)));
- }
- if (!isset($data['return_code']) || $data['return_code'] !== 'SUCCESS' || $data['result_code'] !== 'SUCCESS') {
- $error = 'getResult error:'.$data['return_msg'];
- $error .= isset($data['err_code_des']) ? ' - '.$data['err_code_des'] : '';
- }
- if (!isset($error) && $this->getSign($data) !== $data['sign']) {
- $error = 'getResult error: return data sign error';
- }
- if (isset($error)) {
- throw new GatewayException(
- $error,
- 20000,
- $data);
- }
- return $data;
- }
- /**
- * sign.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param array $data
- *
- * @return string
- */
- protected function getSign($data)
- {
- if (is_null($this->user_config->get('key'))) {
- throw new InvalidArgumentException('Missing Config -- [key]');
- }
- ksort($data);
- $string = md5($this->getSignContent($data).'&key='.$this->user_config->get('key'));
- return strtoupper($string);
- }
- /**
- * get sign content.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param array $data
- *
- * @return string
- */
- protected function getSignContent($data)
- {
- $buff = '';
- foreach ($data as $k => $v) {
- $buff .= ($k != 'sign' && $v != '' && !is_array($v)) ? $k.'='.$v.'&' : '';
- }
- return trim($buff, '&');
- }
- /**
- * create random string.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param int $length
- *
- * @return string
- */
- protected function createNonceStr($length = 16)
- {
- $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
- $str = '';
- for ($i = 0; $i < $length; $i++) {
- $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
- }
- return $str;
- }
- /**
- * convert to xml.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param array $data
- *
- * @return string
- */
- protected function toXml($data)
- {
- if (!is_array($data) || count($data) <= 0) {
- throw new InvalidArgumentException('convert to xml error!invalid array!');
- }
- $xml = '<xml>';
- foreach ($data as $key => $val) {
- $xml .= is_numeric($val) ? '<'.$key.'>'.$val.'</'.$key.'>' :
- '<'.$key.'><![CDATA['.$val.']]></'.$key.'>';
- }
- $xml .= '</xml>';
- return $xml;
- }
- /**
- * convert to array.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @param string $xml
- *
- * @return array
- */
- protected function fromXml($xml)
- {
- if (!$xml) {
- throw new InvalidArgumentException('convert to array error !invalid xml');
- }
- libxml_disable_entity_loader(true);
- return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
- }
- /**
- * delete trade_type and notify_url.
- *
- * @author yansongda <me@yansongda.cn>
- *
- * @return bool
- */
- protected function unsetTradeTypeAndNotifyUrl()
- {
- unset($this->config['notify_url']);
- unset($this->config['trade_type']);
- return true;
- }
- }
|