| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- <?php
- namespace Ycpay;
- class Weixin implements PayInterface
- {
- private $orderParam;
- private $orderH5Param;
- private $appid;
- private $secret;
- private $mch_id;
- private $mch_key;
- private $notify_url;
- private $trade_type;
- private $key_pem;
- private $cert_pem;
- private $openid;
- private $codedUrl = 'https://api.weixin.qq.com/sns/jscode2session?';
- private $appCodeUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?";
- private $tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=';
- protected $payUrl = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; //支付
- protected $query = 'https://api.mch.weixin.qq.com/pay/orderquery'; //查询
- protected $refundUrl = 'https://api.mch.weixin.qq.com/secapi/pay/refund'; //退款
- protected $sendMsgUrl = 'https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=';
- protected $payOrder;
- public static function init($config)
- {
- if (empty($config['appid'])) {
- throw new \Exception('not empty appid');
- }
- if (empty($config['secret'])) {
- throw new \Exception('not empty secret');
- }
- $class = new self();
- $class->secret = $config['secret'];
- $class->appid = $config['appid'];
- $class->mch_id = isset($config['mch_id']) ? $config['mch_id'] : "";
- $class->mch_key = isset($config['mch_key']) ? $config['mch_key'] : "";
- $class->notify_url = isset($config['notify_url']) ? $config['notify_url'] : "";
- $class->trade_type = isset($config['trade_type']) ? $config['trade_type'] : "JSAPI";
- $class->key_pem = isset($config['key_pem']) ? $config['key_pem'] : "";
- $class->cert_pem = isset($config['cert_pem']) ? $config['cert_pem'] : "";
- return $class;
- }
- public function create_nonce_str($length = 32)
- {
- $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
- $str = "";
- for ($i = 0; $i < $length; $i++) {
- $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
- }
- return $str;
- }
- public function getParam()
- {
- return $this->orderParam;
- }
- public function orderH5Param()
- {
- return $this->orderH5Param;
- }
- public function getNotifyOrder()
- {
- $xml = file_get_contents("php://input");
- return $this->xmlToArray($xml);
- }
- /**
- * 设置订单号 金额 描述
- * @param string $rder_no 平台订单号
- * @param int $money 订单金额
- * @param string $title 描述
- * @param string $openid 描述
- *
- */
- public function set($out_trade_no, $total_fee, $title, $openid = '')
- {
- $order['appid'] = $this->appid;
- $order['mch_id'] = $this->mch_id;
- $order['trade_type'] = $this->trade_type;
- $order['nonce_str'] = $this->create_nonce_str();
- $order['body'] = $title;
- $order['out_trade_no'] = $out_trade_no;
- $order['total_fee'] = $total_fee;
- $order['notify_url'] = $this->notify_url;
- if ($this->trade_type == "JSAPI") {
- $order['openid'] = $openid; //设置用户openid
- }
- $order['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
- $order['sign'] = $this->sign($order);
- $data = $this->arrayToXml($order);
- $xml_data = $this->curl_post($this->payUrl, $data);
- $prepay_id = $this->xmlToArray($xml_data);
- if ($this->trade_type == "MWEB") {
- $this->orderH5Param = $prepay_id;
- } else {
- $this->orderParam = array(
- 'appId' => $this->appid, //小程序ID
- 'timeStamp' => '' . time() . '', //时间戳
- 'nonceStr' => $this->create_nonce_str(), //随机串
- 'package' => 'prepay_id=' . $prepay_id['prepay_id'], //数据包
- 'signType' => 'MD5', //签名方式
- );
- $this->orderParam['paySign'] = $this->sign($this->orderParam);
- }
- return $this;
- }
- public function get_client_ip()
- {
- $cip = "unknown";
- if ($_SERVER['REMOTE_ADDR']) {
- $cip = $_SERVER['REMOTE_ADDR'];
- } else if (getenv("REMOTE_ADDR")) {
- $cip = getenv("REMOTE_ADDR");
- }
- return $cip;
- }
- /**
- * 作用:将xml转为array
- */
- public function xmlToArray($xml)
- {
- //将XML转为array
- $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
- return $array_data;
- }
- /**
- * @param array $map
- * @return string
- */
- public function sign(array $map)
- {
- foreach ($map as $k => $v) {
- $Parameters[$k] = $v;
- }
- //签名步骤一:按字典序排序参数
- ksort($Parameters);
- $String = $this->formatBizQueryParaMap($Parameters, false);
- //签名步骤二:在string后加入KEY
- $String = $String . "&key=" . $this->mch_key;
- //签名步骤三:MD5加密
- $String = md5($String);
- //签名步骤四:所有字符转为大写
- $result_ = strtoupper($String);
- return $result_;
- }
- /**
- * 作用:格式化参数,签名过程需要使用
- */
- public function formatBizQueryParaMap($paraMap, $urlencode)
- {
- $buff = "";
- ksort($paraMap);
- foreach ($paraMap as $k => $v) {
- if ($urlencode) {
- $v = urlencode($v);
- }
- //$buff .= strtolower($k) . "=" . $v . "&";
- $buff .= $k . "=" . $v . "&";
- }
- $reqPar = '';
- if (strlen($buff) > 0) {
- $reqPar = substr($buff, 0, strlen($buff) - 1);
- }
- return $reqPar;
- }
- /**
- * 作用:array转xml
- */
- public function arrayToXml($arr)
- {
- $xml = "<xml>";
- foreach ($arr as $key => $val) {
- if (is_numeric($val)) {
- $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
- } else {
- $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
- }
- }
- $xml .= "</xml>";
- return $xml;
- }
- /**
- * 获取token
- */
- public function getToken()
- {
- $url = $this->tokenUrl . $this->appid . "&secret=" . $this->secret;
- return json_decode($this->curl_get($url), true);
- }
- /**
- * 获取openid
- * @param string $code
- * @return array 成功返回数组 失败为空
- */
- public function getOpenid($code)
- {
- $url = $this->codedUrl . "appid=" . $this->appid . "&secret=" . $this->secret . "&js_code=" . $code . "&grant_type=authorization_code";
- return json_decode($this->curl_get($url), true);
- }
- /**
- * 获取微信app openid
- *
- * @param [type] $code
- * @return void
- */
- public function getAppOpenid($code)
- {
- $url = $this->appCodeUrl . "appid=" . $this->appid . "&secret=" . $this->secret . "&code=" . $code . "&grant_type=authorization_code";
- return json_decode($this->curl_get($url), true);
- }
- /**
- * 发送模版消息
- *
- * @param [type] $data
- * @param [type] $token
- * @return void
- */
- public function sendMsg($data, $token)
- {
- return json_decode($this->curl_post($this->sendMsgUrl . $token, json_encode($data)), true);
- }
- /**
- * 异步回调
- * @param array $order 回调数据
- * @return bool true 验签通过|false 验签不通过
- */
- public function notifyCheck()
- {
- $order = $this->getNotifyOrder();
- $uorder = $order;
- unset($uorder['sign']);
- $sign = $this->sign($uorder); //本地签名
- if ($order['sign'] == $sign) {
- if (isset($order['return_code']) && $order['return_code'] == "SUCCESS" && $order['result_code'] && $order['result_code'] = "SUCCESS") {
- return true;
- } else {
- return false;
- }
- }
- return false;
- }
- /**
- * 申请退款
- * @param array $order
- * @param string $out_trade_no 平台订单号
- * @param int $total_fee 订单金额
- * @param string $out_refund_no 自定义退款单号
- * @parma int @refund_fee 退款金额
- */
- public function applyOrderRefund($order)
- {
- $order['appid'] = $this->appid;
- $order['mch_id'] = $this->mch_id;
- $order['nonce_str'] = $this->create_nonce_str();
- $order['sign'] = $this->sign($order);
- $xml_data = $this->arrayToXml($order);
- $data = $this->curl_post_ssl($this->refundUrl, $xml_data);
- return $this->xmlToArray($data);
- }
- /**
- * 订单查询
- * @param string $out_trade_no 订单号
- * @return array 订单信息
- */
- public function findOrder($out_trade_no)
- {
- $order['out_trade_no'] = $out_trade_no;
- $order['appid'] = $this->appid;
- $order['mch_id'] = $this->mch_id;
- $order['nonce_str'] = $this->create_nonce_str();
- $order['sign'] = $this->sign($order);
- $xml_data = $this->arrayToXml($order);
- $data = $this->curl_post_ssl($this->query, $xml_data);
- return $this->xmlToArray($data);
- }
- protected static function curl_get($url)
- {
- $headerArr = array("Content-type:application/x-www-form-urlencoded");
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
- curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArr);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- $output = curl_exec($ch);
- if (!$output) {
- throw new \Exception(curl_error($ch));
- }
- curl_close($ch);
- return $output;
- }
- /**
- * @desc post 用于退款
- */
- protected static function curl_post($url, $data)
- {
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, $url);
- if (!empty($data)) {
- curl_setopt($ch, CURLOPT_POST, 1);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
- }
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/xml'));
- $output = curl_exec($ch);
- if (!$output) {
- throw new \Exception(curl_error($ch));
- }
- curl_close($ch);
- return $output;
- }
- public function curl_post_ssl($url, $vars, $second = 30, $aHeader = array())
- {
- $ch = curl_init();
- //超时时间
- curl_setopt($ch, CURLOPT_TIMEOUT, $second);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
- curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
- curl_setopt($ch, CURLOPT_SSLCERT, $this->cert_pem);
- curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
- curl_setopt($ch, CURLOPT_SSLKEY, $this->key_pem);
- if (count($aHeader) >= 1) {
- curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
- }
- curl_setopt($ch, CURLOPT_POST, 1);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);
- $data = curl_exec($ch);
- if ($data) {
- curl_close($ch);
- return $data;
- } else {
- $error = curl_errno($ch);
- curl_close($ch);
- return false;
- }
- }
- /**
- * 解密手机号
- *
- * @param string $session_key 前端传递的session_key
- * @param string $iv 前端传递的iv
- * @param string $encryptedData 前端传递的encryptedData
- */
- public function decryptPhone($session_key, $iv, $encryptedData)
- {
- if (strlen($session_key) != 24) {
- return false;
- }
- $aesKey = base64_decode($session_key);
- if (strlen($iv) != 24) {
- return false;
- }
- $aesIV = base64_decode($iv);
- $aesCipher = base64_decode($encryptedData);
- $result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV);
- $dataObj = json_decode($result);
- if ($dataObj == null) {
- return false;
- }
- if ($dataObj->watermark->appid != $this->appid) {
- return false;
- }
- return json_decode($result, true);
- }
- }
|