├── README.md ├── authorize.php ├── barcode.php ├── h5.php ├── jsapi.php ├── jsapi_v3.php ├── native.php ├── native_v3.php ├── notify.php ├── notify_v3.php ├── orderquery.php ├── redpack.php ├── refund.php ├── refund_query.php ├── reverse.php └── transfers.php /README.md: -------------------------------------------------------------------------------- 1 | # weixinPay 2 | 一个PHP文件搞定微信支付系列 3 | 4 | 网上的很多PHP微信支付接入教程都颇为复杂,且需要配置和引入较多的文件,本人通过整理后给出一个单文件版的(代码只有200行左右),每个文件独立运行,不依赖和引入其他文件,希望可以给各位想接入微信支付的带来些许帮助和借鉴意义。 5 | 6 | 一个PHP文件搞定支付宝系列请移步:https://github.com/dedemao/alipay 7 | 8 | # 在线演示 9 | https://www.dedemao.com/wx/demo.html 10 | 11 | # 环境依赖 12 | 13 | PHP5.0以上,且需要开启CURL服务、SSL服务。 14 | 15 | # 文件对应说明 16 | 17 | native.php 原生支付(扫码支付) 18 | 19 | barcode.php 刷卡支付(商户扫描用户收款码) 20 | 21 | jsapi.php 公众号支付 22 | 23 | H5.php H5支付 24 | 25 | redpack.php 现金红包 26 | 27 | transfers.php 企业付款到零钱 28 | 29 | notify.php   异步回调通知 30 | 31 | refund.php 退款 32 | 33 | # 注意事项 34 | 35 | 1.需要用到微信支付的哪一种支付方式,就只下载对应的单个文件即可。 36 | 37 | 2.文件开头的配置信息必须完善 38 | 39 | 3.文件需放到支付授权目录下,可以在微信支付商户平台->产品中心->开发配置中设置。 40 | 41 | 4.如提示签名错误可以通过微信支付签名验证工具进行验证:https://pay.weixin.qq.com/wiki/tools/signverify/ 42 | 43 | 44 | # 若对您有帮助,可以赞助并支持下作者哦,谢谢! 45 | 46 |

47 | 48 |

联系邮箱:884358@qq.com

49 |

50 | -------------------------------------------------------------------------------- /authorize.php: -------------------------------------------------------------------------------- 1 | 开发->基本配置->AppID 6 | $appKey = ''; //微信公众平台->开发->基本配置->AppSecret 7 | /* 配置结束 */ 8 | 9 | //①、获取用户openid 10 | $wxPay = new WxService($appid,$appKey); 11 | $data = $wxPay->GetOpenid(); //获取openid 12 | if(!$data['openid']) exit('获取openid失败'); 13 | //②、获取用户信息 14 | $user = $wxPay->getUserInfo($data['openid'],$data['access_token']); 15 | ?> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 微信获取用户信息demo 24 | 25 | 26 | 27 | 28 |
29 |
30 |

你的基本信息如下:

31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
openid
unionid
昵称
头像
性别
省份 / 城市
language
73 |
74 |
75 | 76 | 77 | appid = $appid; //微信支付申请对应的公众号的APPID 88 | $this->appKey = $appKey; //微信支付申请对应的公众号的APP Key 89 | } 90 | 91 | /** 92 | * 通过跳转获取用户的openid,跳转流程如下: 93 | * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize 94 | * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code 95 | * 96 | * @return 用户的openid 97 | */ 98 | public function GetOpenid() 99 | { 100 | //通过code获得openid 101 | if (!isset($_GET['code'])){ 102 | //触发微信返回code码 103 | $baseUrl = $this->getCurrentUrl(); 104 | $url = $this->__CreateOauthUrlForCode($baseUrl); 105 | Header("Location: $url"); 106 | exit(); 107 | } else { 108 | //获取code码,以获取openid 109 | $code = $_GET['code']; 110 | $openid = $this->getOpenidFromMp($code); 111 | return $openid; 112 | } 113 | } 114 | 115 | public function getCurrentUrl() 116 | { 117 | $scheme = $_SERVER['HTTPS']=='on' ? 'https://' : 'http://'; 118 | $uri = $_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']; 119 | if($_SERVER['REQUEST_URI']) $uri = $_SERVER['REQUEST_URI']; 120 | $baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$uri); 121 | return $baseUrl; 122 | } 123 | 124 | /** 125 | * 通过code从工作平台获取openid机器access_token 126 | * @param string $code 微信跳转回来带上的code 127 | * @return openid 128 | */ 129 | public function GetOpenidFromMp($code) 130 | { 131 | $url = $this->__CreateOauthUrlForOpenid($code); 132 | $res = self::curlGet($url); 133 | $data = json_decode($res,true); 134 | $this->data = $data; 135 | return $data; 136 | } 137 | 138 | /** 139 | * 构造获取open和access_toke的url地址 140 | * @param string $code,微信跳转带回的code 141 | * @return 请求的url 142 | */ 143 | private function __CreateOauthUrlForOpenid($code) 144 | { 145 | $urlObj["appid"] = $this->appid; 146 | $urlObj["secret"] = $this->appKey; 147 | $urlObj["code"] = $code; 148 | $urlObj["grant_type"] = "authorization_code"; 149 | $bizString = $this->ToUrlParams($urlObj); 150 | return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString; 151 | } 152 | 153 | /** 154 | * 构造获取code的url连接 155 | * @param string $redirectUrl 微信服务器回跳的url,需要url编码 156 | * @return 返回构造好的url 157 | */ 158 | private function __CreateOauthUrlForCode($redirectUrl) 159 | { 160 | $urlObj["appid"] = $this->appid; 161 | $urlObj["redirect_uri"] = "$redirectUrl"; 162 | $urlObj["response_type"] = "code"; 163 | $urlObj["scope"] = "snsapi_userinfo"; 164 | $urlObj["state"] = "STATE"; 165 | $bizString = $this->ToUrlParams($urlObj); 166 | return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString; 167 | } 168 | 169 | /** 170 | * 拼接签名字符串 171 | * @param array $urlObj 172 | * @return 返回已经拼接好的字符串 173 | */ 174 | private function ToUrlParams($urlObj) 175 | { 176 | $buff = ""; 177 | foreach ($urlObj as $k => $v) 178 | { 179 | if($k != "sign") $buff .= $k . "=" . $v . "&"; 180 | } 181 | $buff = trim($buff, "&"); 182 | return $buff; 183 | } 184 | 185 | /** 186 | * 获取用户信息 187 | * @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid 188 | * @return string 189 | */ 190 | public function getUserInfo($openid,$access_token) 191 | { 192 | 193 | $response = self::curlGet('https://api.weixin.qq.com/sns/userinfo?access_token='.$access_token.'&openid='.$openid.'&lang=zh_CN'); 194 | return json_decode($response,true); 195 | 196 | } 197 | 198 | public static function curlGet($url = '', $options = array()) 199 | { 200 | $ch = curl_init($url); 201 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 202 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 203 | if (!empty($options)) { 204 | curl_setopt_array($ch, $options); 205 | } 206 | //https请求 不验证证书和host 207 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 208 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 209 | $data = curl_exec($ch); 210 | curl_close($ch); 211 | return $data; 212 | } 213 | 214 | public static function curlPost($url = '', $postData = '', $options = array()) 215 | { 216 | if (is_array($postData)) { 217 | $postData = http_build_query($postData); 218 | } 219 | $ch = curl_init(); 220 | curl_setopt($ch, CURLOPT_URL, $url); 221 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 222 | curl_setopt($ch, CURLOPT_POST, 1); 223 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 224 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 225 | if (!empty($options)) { 226 | curl_setopt_array($ch, $options); 227 | } 228 | //https请求 不验证证书和host 229 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 230 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 231 | $data = curl_exec($ch); 232 | curl_close($ch); 233 | return $data; 234 | } 235 | 236 | } 237 | -------------------------------------------------------------------------------- /barcode.php: -------------------------------------------------------------------------------- 1 | setTotalFee($payAmount); 17 | $wxPay->setOutTradeNo($outTradeNo); 18 | $wxPay->setOrderName($orderName); 19 | $wxPay->setAuthCode($authCode); 20 | $arr = $wxPay->createJsBizPackage(); 21 | if($arr['return_code']=='SUCCESS'){ 22 | echo '付款成功!返回信息如下:

'; 23 | echo '
'.print_r($arr).'
'; 24 | exit(); 25 | } 26 | exit('error'); 27 | class WxpayService 28 | { 29 | protected $mchid; 30 | protected $appid; 31 | protected $apiKey; 32 | protected $totalFee; 33 | protected $outTradeNo; 34 | protected $orderName; 35 | protected $authCode; 36 | public function __construct($mchid='', $appid='', $key='') 37 | { 38 | $this->mchid = $mchid; 39 | $this->appid = $appid; 40 | $this->apiKey = $key; 41 | } 42 | 43 | public function setTotalFee($totalFee) 44 | { 45 | $this->totalFee = $totalFee; 46 | } 47 | 48 | public function setOutTradeNo($outTradeNo) 49 | { 50 | $this->outTradeNo = $outTradeNo; 51 | } 52 | 53 | public function setOrderName($orderName) 54 | { 55 | $this->orderName = $orderName; 56 | } 57 | 58 | public function setAuthCode($authCode) 59 | { 60 | $this->authCode = $authCode; 61 | } 62 | 63 | /** 64 | * 发起订单 65 | * @return array 66 | */ 67 | public function createJsBizPackage() 68 | { 69 | $config = array( 70 | 'mch_id' => $this->mchid, 71 | 'appid' => $this->appid, 72 | 'key' => $this->apiKey, 73 | 'total_fee' => $this->totalFee, 74 | 'out_trade_no' => $this->outTradeNo, 75 | 'order_name' => $this->orderName, 76 | 'auth_code' => $this->authCode, 77 | ); 78 | //$orderName = iconv('GBK','UTF-8',$orderName); 79 | $unified = array( 80 | 'appid' => $config['appid'], 81 | 'attach' => 'pay', //商家数据包,原样返回,如果填写中文,请注意转换为utf-8 82 | 'body' => $config['order_name'], 83 | 'mch_id' => $config['mch_id'], 84 | 'nonce_str' => self::createNonceStr(), 85 | 'out_trade_no' => $config['out_trade_no'], 86 | 'spbill_create_ip' => '127.0.0.1', 87 | 'total_fee' => floatval($config['total_fee']) * 100, //单位 转为分 88 | 'auth_code'=>$config['auth_code'], //收款码, 89 | 'device_info'=>'dedemao001', //终端设备号(商户自定义,如门店编号) 90 | // 'limit_pay'=>'no_credit' //指定支付方式 no_credit--指定不能使用信用卡支付 91 | ); 92 | $unified['sign'] = self::getSign($unified, $config['key']); 93 | $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/micropay', self::arrayToXml($unified)); 94 | $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 95 | if ($unifiedOrder === false) { 96 | die('parse xml error'); 97 | } 98 | if ($unifiedOrder->return_code != 'SUCCESS') { 99 | die('支付失败:错误码:'.$unifiedOrder->err_code.'。错误码说明:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1#7'); 100 | } 101 | if ($unifiedOrder->result_code != 'SUCCESS') { 102 | die('支付失败:错误码:'.$unifiedOrder->err_code.'。错误码说明:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1#7'); 103 | } 104 | return (array)$unifiedOrder; 105 | } 106 | public function notify() 107 | { 108 | $config = array( 109 | 'mch_id' => $this->mchid, 110 | 'appid' => $this->appid, 111 | 'key' => $this->apiKey, 112 | ); 113 | $postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; 114 | $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); 115 | if ($postObj === false) { 116 | die('parse xml error'); 117 | } 118 | if ($postObj->return_code != 'SUCCESS') { 119 | die($postObj->return_msg); 120 | } 121 | if ($postObj->result_code != 'SUCCESS') { 122 | die($postObj->err_code); 123 | } 124 | $arr = (array)$postObj; 125 | unset($arr['sign']); 126 | if (self::getSign($arr, $config['key']) == $postObj->sign) { 127 | echo ''; 128 | return $postObj; 129 | } 130 | } 131 | /** 132 | * curl get 133 | * 134 | * @param string $url 135 | * @param array $options 136 | * @return mixed 137 | */ 138 | public static function curlGet($url = '', $options = array()) 139 | { 140 | $ch = curl_init($url); 141 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 142 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 143 | if (!empty($options)) { 144 | curl_setopt_array($ch, $options); 145 | } 146 | //https请求 不验证证书和host 147 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 148 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 149 | $data = curl_exec($ch); 150 | curl_close($ch); 151 | return $data; 152 | } 153 | public static function curlPost($url = '', $postData = '', $options = array()) 154 | { 155 | if (is_array($postData)) { 156 | $postData = http_build_query($postData); 157 | } 158 | $ch = curl_init(); 159 | curl_setopt($ch, CURLOPT_URL, $url); 160 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 161 | curl_setopt($ch, CURLOPT_POST, 1); 162 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 163 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 164 | if (!empty($options)) { 165 | curl_setopt_array($ch, $options); 166 | } 167 | //https请求 不验证证书和host 168 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 169 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 170 | $data = curl_exec($ch); 171 | curl_close($ch); 172 | return $data; 173 | } 174 | public static function createNonceStr($length = 16) 175 | { 176 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 177 | $str = ''; 178 | for ($i = 0; $i < $length; $i++) { 179 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 180 | } 181 | return $str; 182 | } 183 | public static function arrayToXml($arr) 184 | { 185 | $xml = ""; 186 | foreach ($arr as $key => $val) { 187 | if (is_numeric($val)) { 188 | $xml .= "<" . $key . ">" . $val . ""; 189 | } else 190 | $xml .= "<" . $key . ">"; 191 | } 192 | $xml .= ""; 193 | return $xml; 194 | } 195 | /** 196 | * 获取签名 197 | */ 198 | public static function getSign($params, $key) 199 | { 200 | ksort($params, SORT_STRING); 201 | $unSignParaString = self::formatQueryParaMap($params, false); 202 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 203 | return $signStr; 204 | } 205 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 206 | { 207 | $buff = ""; 208 | ksort($paraMap); 209 | foreach ($paraMap as $k => $v) { 210 | if (null != $v && "null" != $v) { 211 | if ($urlEncode) { 212 | $v = urlencode($v); 213 | } 214 | $buff .= $k . "=" . $v . "&"; 215 | } 216 | } 217 | $reqPar = ''; 218 | if (strlen($buff) > 0) { 219 | $reqPar = substr($buff, 0, strlen($buff) - 1); 220 | } 221 | return $reqPar; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /h5.php: -------------------------------------------------------------------------------- 1 | setTotalFee($payAmount); 29 | $wxPay->setOutTradeNo($outTradeNo); 30 | $wxPay->setOrderName($orderName); 31 | $wxPay->setNotifyUrl($notifyUrl); 32 | $wxPay->setReturnUrl($returnUrl); 33 | $wxPay->setWapUrl($wapUrl); 34 | $wxPay->setWapName($wapName); 35 | 36 | $mwebUrl= $wxPay->createJsBizPackage($payAmount,$outTradeNo,$orderName,$notifyUrl); 37 | echo "

点击跳转至支付页面

"; 38 | exit(); 39 | class WxpayService 40 | { 41 | protected $mchid; 42 | protected $appid; 43 | protected $apiKey; 44 | protected $totalFee; 45 | protected $outTradeNo; 46 | protected $orderName; 47 | protected $notifyUrl; 48 | protected $returnUrl; 49 | protected $wapUrl; 50 | protected $wapName; 51 | 52 | public function __construct($mchid, $appid, $key) 53 | { 54 | $this->mchid = $mchid; 55 | $this->appid = $appid; 56 | $this->apiKey = $key; 57 | } 58 | 59 | public function setTotalFee($totalFee) 60 | { 61 | $this->totalFee = $totalFee; 62 | } 63 | public function setOutTradeNo($outTradeNo) 64 | { 65 | $this->outTradeNo = $outTradeNo; 66 | } 67 | public function setOrderName($orderName) 68 | { 69 | $this->orderName = $orderName; 70 | } 71 | public function setWapUrl($wapUrl) 72 | { 73 | $this->wapUrl = $wapUrl; 74 | } 75 | public function setWapName($wapName) 76 | { 77 | $this->wapName = $wapName; 78 | } 79 | public function setNotifyUrl($notifyUrl) 80 | { 81 | $this->notifyUrl = $notifyUrl; 82 | } 83 | public function setReturnUrl($returnUrl) 84 | { 85 | $this->returnUrl = $returnUrl; 86 | } 87 | 88 | /** 89 | * 发起订单 90 | * @return array 91 | */ 92 | public function createJsBizPackage() 93 | { 94 | $config = array( 95 | 'mch_id' => $this->mchid, 96 | 'appid' => $this->appid, 97 | 'key' => $this->apiKey, 98 | ); 99 | $scene_info = array( 100 | 'h5_info' =>array( 101 | 'type'=>'Wap', 102 | 'wap_url'=>$this->wapUrl, 103 | 'wap_name'=>$this->wapName, 104 | ) 105 | ); 106 | $unified = array( 107 | 'appid' => $config['appid'], 108 | 'attach' => 'pay', //商家数据包,原样返回,如果填写中文,请注意转换为utf-8 109 | 'body' => $this->orderName, 110 | 'mch_id' => $config['mch_id'], 111 | 'nonce_str' => self::createNonceStr(), 112 | 'notify_url' => $this->notifyUrl, 113 | 'out_trade_no' => $this->outTradeNo, 114 | 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'], 115 | 'total_fee' => floatval($this->totalFee) * 100, //单位 转为分 116 | 'trade_type' => 'MWEB', 117 | 'scene_info'=>json_encode($scene_info) 118 | ); 119 | $unified['sign'] = self::getSign($unified, $config['key']); 120 | $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified)); 121 | $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 122 | if ($unifiedOrder->return_code != 'SUCCESS') { 123 | die($unifiedOrder->return_msg); 124 | } 125 | if($unifiedOrder->mweb_url){ 126 | return $unifiedOrder->mweb_url.'&redirect_url='.urlencode($this->returnUrl); 127 | } 128 | exit('error'); 129 | } 130 | public static function curlPost($url = '', $postData = '', $options = array()) 131 | { 132 | if (is_array($postData)) { 133 | $postData = http_build_query($postData); 134 | } 135 | $ch = curl_init(); 136 | curl_setopt($ch, CURLOPT_URL, $url); 137 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 138 | curl_setopt($ch, CURLOPT_POST, 1); 139 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 140 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 141 | if (!empty($options)) { 142 | curl_setopt_array($ch, $options); 143 | } 144 | //https请求 不验证证书和host 145 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 146 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 147 | $data = curl_exec($ch); 148 | curl_close($ch); 149 | return $data; 150 | } 151 | public static function createNonceStr($length = 16) 152 | { 153 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 154 | $str = ''; 155 | for ($i = 0; $i < $length; $i++) { 156 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 157 | } 158 | return $str; 159 | } 160 | public static function arrayToXml($arr) 161 | { 162 | $xml = ""; 163 | foreach ($arr as $key => $val) { 164 | if (is_numeric($val)) { 165 | $xml .= "<" . $key . ">" . $val . ""; 166 | } else 167 | $xml .= "<" . $key . ">"; 168 | } 169 | $xml .= ""; 170 | return $xml; 171 | } 172 | /** 173 | * 获取签名 174 | */ 175 | public static function getSign($params, $key) 176 | { 177 | ksort($params, SORT_STRING); 178 | $unSignParaString = self::formatQueryParaMap($params, false); 179 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 180 | return $signStr; 181 | } 182 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 183 | { 184 | $buff = ""; 185 | ksort($paraMap); 186 | foreach ($paraMap as $k => $v) { 187 | if (null != $v && "null" != $v) { 188 | if ($urlEncode) { 189 | $v = urlencode($v); 190 | } 191 | $buff .= $k . "=" . $v . "&"; 192 | } 193 | } 194 | $reqPar = ''; 195 | if (strlen($buff) > 0) { 196 | $reqPar = substr($buff, 0, strlen($buff) - 1); 197 | } 198 | return $reqPar; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /jsapi.php: -------------------------------------------------------------------------------- 1 | GetOpenid(); //获取openid 10 | if(!$openId) exit('获取openid失败'); 11 | //②、统一下单 12 | $outTradeNo = uniqid(); //你自己的商品订单号 13 | $payAmount = 0.01; //付款金额,单位:元 14 | $orderName = '支付测试'; //订单标题 15 | $notifyUrl = 'https://www.xxx.com/wx/notify.php'; //付款成功后的回调地址(不要有问号) 16 | $payTime = time(); //付款时间 17 | $jsApiParameters = $wxPay->createJsBizPackage($openId,$payAmount,$outTradeNo,$orderName,$notifyUrl,$payTime); 18 | $jsApiParameters = json_encode($jsApiParameters); 19 | ?> 20 | 21 | 22 | 23 | 24 | 微信支付样例-支付 25 | 56 | 57 | 58 |
59 | 该笔订单支付金额为

60 |
61 | 62 |
63 | 64 | 65 | mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号 76 | $this->appid = $appid; //微信支付申请对应的公众号的APPID 77 | $this->appKey = $appKey; //微信支付申请对应的公众号的APP Key 78 | $this->apiKey = $key; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥 79 | } 80 | /** 81 | * 通过跳转获取用户的openid,跳转流程如下: 82 | * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize 83 | * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code 84 | * @return 用户的openid 85 | */ 86 | public function GetOpenid() 87 | { 88 | //通过code获得openid 89 | if (!isset($_GET['code'])){ 90 | //触发微信返回code码 91 | $scheme = $_SERVER['HTTPS']=='on' ? 'https://' : 'http://'; 92 | $uri = $_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']; 93 | if($_SERVER['REQUEST_URI']) $uri = $_SERVER['REQUEST_URI']; 94 | $baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$uri); 95 | $url = $this->__CreateOauthUrlForCode($baseUrl); 96 | Header("Location: $url"); 97 | exit(); 98 | } else { 99 | //获取code码,以获取openid 100 | $code = $_GET['code']; 101 | $openid = $this->getOpenidFromMp($code); 102 | return $openid; 103 | } 104 | } 105 | /** 106 | * 通过code从工作平台获取openid机器access_token 107 | * @param string $code 微信跳转回来带上的code 108 | * @return openid 109 | */ 110 | public function GetOpenidFromMp($code) 111 | { 112 | $url = $this->__CreateOauthUrlForOpenid($code); 113 | $res = self::curlGet($url); 114 | //取出openid 115 | $data = json_decode($res,true); 116 | $this->data = $data; 117 | $openid = $data['openid']; 118 | return $openid; 119 | } 120 | /** 121 | * 构造获取open和access_toke的url地址 122 | * @param string $code,微信跳转带回的code 123 | * @return 请求的url 124 | */ 125 | private function __CreateOauthUrlForOpenid($code) 126 | { 127 | $urlObj["appid"] = $this->appid; 128 | $urlObj["secret"] = $this->appKey; 129 | $urlObj["code"] = $code; 130 | $urlObj["grant_type"] = "authorization_code"; 131 | $bizString = $this->ToUrlParams($urlObj); 132 | return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString; 133 | } 134 | /** 135 | * 构造获取code的url连接 136 | * @param string $redirectUrl 微信服务器回跳的url,需要url编码 137 | * @return 返回构造好的url 138 | */ 139 | private function __CreateOauthUrlForCode($redirectUrl) 140 | { 141 | $urlObj["appid"] = $this->appid; 142 | $urlObj["redirect_uri"] = "$redirectUrl"; 143 | $urlObj["response_type"] = "code"; 144 | $urlObj["scope"] = "snsapi_base"; 145 | $urlObj["state"] = "STATE"."#wechat_redirect"; 146 | $bizString = $this->ToUrlParams($urlObj); 147 | return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString; 148 | } 149 | /** 150 | * 拼接签名字符串 151 | * @param array $urlObj 152 | * @return 返回已经拼接好的字符串 153 | */ 154 | private function ToUrlParams($urlObj) 155 | { 156 | $buff = ""; 157 | foreach ($urlObj as $k => $v) 158 | { 159 | if($k != "sign") $buff .= $k . "=" . $v . "&"; 160 | } 161 | $buff = trim($buff, "&"); 162 | return $buff; 163 | } 164 | /** 165 | * 统一下单 166 | * @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid 167 | * @param float $totalFee 收款总费用 单位元 168 | * @param string $outTradeNo 唯一的订单号 169 | * @param string $orderName 订单名称 170 | * @param string $notifyUrl 支付结果通知url 不要有问号 171 | * @param string $timestamp 支付时间 172 | * @return string 173 | */ 174 | public function createJsBizPackage($openid, $totalFee, $outTradeNo, $orderName, $notifyUrl, $timestamp) 175 | { 176 | $config = array( 177 | 'mch_id' => $this->mchid, 178 | 'appid' => $this->appid, 179 | 'key' => $this->apiKey, 180 | ); 181 | //$orderName = iconv('GBK','UTF-8',$orderName); 182 | $unified = array( 183 | 'appid' => $config['appid'], 184 | 'attach' => 'pay', //商家数据包,原样返回,如果填写中文,请注意转换为utf-8 185 | 'body' => $orderName, 186 | 'mch_id' => $config['mch_id'], 187 | 'nonce_str' => self::createNonceStr(), 188 | 'notify_url' => $notifyUrl, 189 | 'openid' => $openid, //rade_type=JSAPI,此参数必传 190 | 'out_trade_no' => $outTradeNo, 191 | 'spbill_create_ip' => '127.0.0.1', 192 | 'total_fee' => floatval($totalFee) * 100, //单位 转为分 193 | 'trade_type' => 'JSAPI', 194 | ); 195 | $unified['sign'] = self::getSign($unified, $config['key']); 196 | $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified)); 197 | //禁止引用外部xml实体 198 | libxml_disable_entity_loader(true); 199 | $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 200 | if ($unifiedOrder === false) { 201 | die('parse xml error'); 202 | } 203 | if ($unifiedOrder->return_code != 'SUCCESS') { 204 | die($unifiedOrder->return_msg); 205 | } 206 | if ($unifiedOrder->result_code != 'SUCCESS') { 207 | die($unifiedOrder->err_code); 208 | } 209 | $arr = array( 210 | "appId" => $config['appid'], 211 | "timeStamp" => "$timestamp", //这里是字符串的时间戳,不是int,所以需加引号 212 | "nonceStr" => self::createNonceStr(), 213 | "package" => "prepay_id=" . $unifiedOrder->prepay_id, 214 | "signType" => 'MD5', 215 | ); 216 | $arr['paySign'] = self::getSign($arr, $config['key']); 217 | return $arr; 218 | } 219 | public static function curlGet($url = '', $options = array()) 220 | { 221 | $ch = curl_init($url); 222 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 223 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 224 | if (!empty($options)) { 225 | curl_setopt_array($ch, $options); 226 | } 227 | //https请求 不验证证书和host 228 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 229 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 230 | $data = curl_exec($ch); 231 | curl_close($ch); 232 | return $data; 233 | } 234 | public static function curlPost($url = '', $postData = '', $options = array()) 235 | { 236 | if (is_array($postData)) { 237 | $postData = http_build_query($postData); 238 | } 239 | $ch = curl_init(); 240 | curl_setopt($ch, CURLOPT_URL, $url); 241 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 242 | curl_setopt($ch, CURLOPT_POST, 1); 243 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 244 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 245 | if (!empty($options)) { 246 | curl_setopt_array($ch, $options); 247 | } 248 | //https请求 不验证证书和host 249 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 250 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 251 | $data = curl_exec($ch); 252 | curl_close($ch); 253 | return $data; 254 | } 255 | public static function createNonceStr($length = 16) 256 | { 257 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 258 | $str = ''; 259 | for ($i = 0; $i < $length; $i++) { 260 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 261 | } 262 | return $str; 263 | } 264 | public static function arrayToXml($arr) 265 | { 266 | $xml = ""; 267 | foreach ($arr as $key => $val) { 268 | if (is_numeric($val)) { 269 | $xml .= "<" . $key . ">" . $val . ""; 270 | } else 271 | $xml .= "<" . $key . ">"; 272 | } 273 | $xml .= ""; 274 | return $xml; 275 | } 276 | public static function getSign($params, $key) 277 | { 278 | ksort($params, SORT_STRING); 279 | $unSignParaString = self::formatQueryParaMap($params, false); 280 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 281 | return $signStr; 282 | } 283 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 284 | { 285 | $buff = ""; 286 | ksort($paraMap); 287 | foreach ($paraMap as $k => $v) { 288 | if (null != $v && "null" != $v) { 289 | if ($urlEncode) { 290 | $v = urlencode($v); 291 | } 292 | $buff .= $k . "=" . $v . "&"; 293 | } 294 | } 295 | $reqPar = ''; 296 | if (strlen($buff) > 0) { 297 | $reqPar = substr($buff, 0, strlen($buff) - 1); 298 | } 299 | return $reqPar; 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /jsapi_v3.php: -------------------------------------------------------------------------------- 1 | GetOpenid(); //获取openid 19 | if(!$openId) exit('获取openid失败'); 20 | //②、统一下单 21 | $wxPay->setOrderName($orderName); 22 | $wxPay->setOutTradeNo($outTradeNo); 23 | $wxPay->setTotalFee($payAmount); 24 | $wxPay->setNotifyUrl($notifyUrl); 25 | $wxPay->setOpenid($openId); 26 | $result = $wxPay->doPay(); 27 | $jsApiParameters = json_encode($result); 28 | ?> 29 | 30 | 31 | 32 | 33 | 微信支付样例-支付 34 | 65 | 66 | 67 |
68 | 该笔订单支付金额为

69 |
70 | 71 |
72 | 73 | 74 | mchid = $mchid; 95 | $this->appid = $appid; 96 | $this->apiKey = $apikey; 97 | $this->appKey = $appkey; 98 | $this->privateKeyPath = $privateKeyPath; 99 | $this->serialNumber = $serialNumber; 100 | } 101 | 102 | public function setTotalFee($totalFee) 103 | { 104 | $this->totalFee = floatval($totalFee); 105 | } 106 | 107 | public function setOutTradeNo($outTradeNo) 108 | { 109 | $this->outTradeNo = $outTradeNo; 110 | } 111 | 112 | public function setOrderName($orderName) 113 | { 114 | $this->orderName = $orderName; 115 | } 116 | 117 | public function setNotifyUrl($notifyUrl) 118 | { 119 | $this->notifyUrl = $notifyUrl; 120 | } 121 | 122 | public function setOpenid($openid) 123 | { 124 | $this->openid = $openid; 125 | } 126 | 127 | /** 128 | * 发起支付 129 | */ 130 | public function doPay() 131 | { 132 | $reqParams = array( 133 | 'appid' => $this->appid, //公众号或移动应用appid 134 | 'mchid' => $this->mchid, //商户号 135 | 'description' => $this->orderName, //商品描述 136 | 'attach' => 'pay', //附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 137 | 'notify_url' => $this->notifyUrl, //通知URL必须为直接可访问的URL,不允许携带查询串。 138 | 'out_trade_no' => $this->outTradeNo, //商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,详见【商户订单号】。特殊规则:最小字符长度为6 139 | 'amount'=>array( 140 | 'total'=>intval($this->totalFee * 100), //订单总金额,单位为分 141 | 'currency'=>'CNY', //CNY:人民币,境内商户号仅支持人民币 142 | ), 143 | 'scene_info'=>array( //支付场景描述 144 | 'payer_client_ip'=>'127.0.0.1' //调用微信支付API的机器IP 145 | ), 146 | 'payer'=>array( //支付场景描述 147 | 'openid'=>$this->openid //调用微信支付API的机器IP 148 | ) 149 | ); 150 | $reqUrl = $this->gateWay.'/pay/transactions/jsapi'; 151 | $this->getAuthStr($reqUrl,$reqParams); 152 | $response = $this->curlPost($reqUrl,$reqParams); 153 | $response = json_decode($response,true); 154 | 155 | if(isset($response['code'])){ 156 | echo $response['code'].':'.$response['message'];exit(); 157 | } 158 | $timestamp = time(); 159 | $arr = array( 160 | "appId" => $this->appid, 161 | "timeStamp" => "$timestamp", //这里是字符串的时间戳,不是int,所以需加引号 162 | "nonceStr" => $this->getNonce(), 163 | "package" => "prepay_id=" . $response['prepay_id'], 164 | "signType" => 'RSA', 165 | ); 166 | $message = $this->appid . "\n" . 167 | $timestamp . "\n" . 168 | $arr['nonceStr'] . "\n" . 169 | "prepay_id=" . $response['prepay_id'] . "\n"; 170 | $arr['paySign'] = $this->sign($message, 'RSA'); 171 | return $arr; 172 | } 173 | 174 | private function sign($message,$signType='sha256WithRSAEncryption') 175 | { 176 | $res = file_get_contents($this->privateKeyPath); 177 | if($signType=='RSA'){ 178 | $result = openssl_sign($message, $sign, $res,version_compare(PHP_VERSION,'5.4.0', '<') ? SHA256 : OPENSSL_ALGO_SHA256); 179 | }else{ 180 | if (!in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { 181 | throw new \RuntimeException("当前PHP环境不支持SHA256withRSA"); 182 | } 183 | $result = openssl_sign($message, $sign, $res, 'sha256WithRSAEncryption'); 184 | } 185 | if (!$result) { 186 | throw new \UnexpectedValueException("签名验证过程发生了错误"); 187 | } 188 | return base64_encode($sign); 189 | } 190 | 191 | public function curlPost($url = '', $postData = array(), $options = array()) 192 | { 193 | if (is_array($postData)) { 194 | $postData = json_encode($postData); 195 | } 196 | $ch = curl_init(); 197 | curl_setopt($ch, CURLOPT_URL, $url); 198 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 199 | curl_setopt($ch, CURLOPT_POST, 1); 200 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 201 | curl_setopt($ch, CURLOPT_HTTPHEADER, array( 202 | 'Authorization:'.$this->auth, 203 | 'Content-Type:application/json', 204 | 'Accept:application/json', 205 | 'User-Agent:'.$_SERVER['HTTP_USER_AGENT'] 206 | )); 207 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 208 | if (!empty($options)) { 209 | curl_setopt_array($ch, $options); 210 | } 211 | //https请求 不验证证书和host 212 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 213 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 214 | $data = curl_exec($ch); 215 | curl_close($ch); 216 | return $data; 217 | } 218 | 219 | private function getSchema() 220 | { 221 | return 'WECHATPAY2-SHA256-RSA2048'; 222 | } 223 | 224 | public function getAuthStr($requestUrl,$reqParams=array()) 225 | { 226 | $schema = $this->getSchema(); 227 | $token = $this->getToken($requestUrl,$reqParams); 228 | $this->auth = $schema.' '.$token; 229 | return $this->auth; 230 | } 231 | 232 | private function getNonce() 233 | { 234 | static $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 235 | $charactersLength = strlen($characters); 236 | $randomString = ''; 237 | for ($i = 0; $i < 32; $i++) { 238 | $randomString .= $characters[rand(0, $charactersLength - 1)]; 239 | } 240 | return $randomString; 241 | } 242 | 243 | public function getToken($requestUrl,$reqParams=array()) 244 | { 245 | $body = $reqParams ? json_encode($reqParams) : ''; 246 | $nonce = $this->getNonce(); 247 | $timestamp = time(); 248 | $message = $this->buildMessage($nonce, $timestamp, $requestUrl,$body); 249 | $sign = $this->sign($message); 250 | $serialNo = $this->serialNumber; 251 | return sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', 252 | $this->mchid, $nonce, $timestamp, $serialNo, $sign 253 | ); 254 | } 255 | 256 | private function buildMessage($nonce, $timestamp, $requestUrl, $body = '') 257 | { 258 | $method = 'POST'; 259 | $urlParts = parse_url($requestUrl); 260 | $canonicalUrl = ($urlParts['path'] . (!empty($urlParts['query']) ? "?{$urlParts['query']}" : "")); 261 | return strtoupper($method) . "\n" . 262 | $canonicalUrl . "\n" . 263 | $timestamp . "\n" . 264 | $nonce . "\n" . 265 | $body . "\n"; 266 | } 267 | 268 | /** 269 | * 通过跳转获取用户的openid,跳转流程如下: 270 | * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize 271 | * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code 272 | * @return 用户的openid 273 | */ 274 | public function GetOpenid() 275 | { 276 | //通过code获得openid 277 | if (!isset($_GET['code'])){ 278 | //触发微信返回code码 279 | $scheme = $_SERVER['HTTPS']=='on' ? 'https://' : 'http://'; 280 | $baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']); 281 | $url = $this->__CreateOauthUrlForCode($baseUrl); 282 | Header("Location: $url"); 283 | exit(); 284 | } else { 285 | //获取code码,以获取openid 286 | $code = $_GET['code']; 287 | $openid = $this->getOpenidFromMp($code); 288 | return $openid; 289 | } 290 | } 291 | 292 | /** 293 | * 构造获取code的url连接 294 | * @param string $redirectUrl 微信服务器回跳的url,需要url编码 295 | * @return 返回构造好的url 296 | */ 297 | private function __CreateOauthUrlForCode($redirectUrl) 298 | { 299 | $urlObj["appid"] = $this->appid; 300 | $urlObj["redirect_uri"] = "$redirectUrl"; 301 | $urlObj["response_type"] = "code"; 302 | $urlObj["scope"] = "snsapi_base"; 303 | $urlObj["state"] = "STATE"."#wechat_redirect"; 304 | $bizString = $this->ToUrlParams($urlObj); 305 | return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString; 306 | } 307 | 308 | /** 309 | * 通过code从工作平台获取openid机器access_token 310 | * @param string $code 微信跳转回来带上的code 311 | * @return openid 312 | */ 313 | public function GetOpenidFromMp($code) 314 | { 315 | $url = $this->__CreateOauthUrlForOpenid($code); 316 | $res = self::curlGet($url); 317 | //取出openid 318 | $data = json_decode($res,true); 319 | // $this->data = $data; 320 | $openid = $data['openid']; 321 | return $openid; 322 | } 323 | 324 | public static function curlGet($url = '', $options = array()) 325 | { 326 | $ch = curl_init($url); 327 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 328 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 329 | if (!empty($options)) { 330 | curl_setopt_array($ch, $options); 331 | } 332 | //https请求 不验证证书和host 333 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 334 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 335 | $data = curl_exec($ch); 336 | curl_close($ch); 337 | return $data; 338 | } 339 | 340 | /** 341 | * 构造获取open和access_toke的url地址 342 | * @param string $code,微信跳转带回的code 343 | * @return 请求的url 344 | */ 345 | private function __CreateOauthUrlForOpenid($code) 346 | { 347 | $urlObj["appid"] = $this->appid; 348 | $urlObj["secret"] = $this->appKey; 349 | $urlObj["code"] = $code; 350 | $urlObj["grant_type"] = "authorization_code"; 351 | $bizString = $this->ToUrlParams($urlObj); 352 | return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString; 353 | } 354 | 355 | /** 356 | * 拼接签名字符串 357 | * @param array $urlObj 358 | * @return 返回已经拼接好的字符串 359 | */ 360 | private function ToUrlParams($urlObj) 361 | { 362 | $buff = ""; 363 | foreach ($urlObj as $k => $v) 364 | { 365 | if($k != "sign") $buff .= $k . "=" . $v . "&"; 366 | } 367 | $buff = trim($buff, "&"); 368 | return $buff; 369 | } 370 | } -------------------------------------------------------------------------------- /native.php: -------------------------------------------------------------------------------- 1 | createJsBizPackage($payAmount,$outTradeNo,$orderName,$notifyUrl,$payTime); 13 | //生成二维码 14 | $url = 'https://wenhairu.com/static/api/qr/?size=300&text='.$arr['code_url']; 15 | echo "
"; 16 | echo '二维码内容:'.$arr['code_url']; 17 | class WxpayService 18 | { 19 | protected $mchid; 20 | protected $appid; 21 | protected $apiKey; 22 | public function __construct($mchid, $appid, $key) 23 | { 24 | $this->mchid = $mchid; 25 | $this->appid = $appid; 26 | $this->apiKey = $key; 27 | } 28 | /** 29 | * 发起订单 30 | * @param float $totalFee 收款总费用 单位元 31 | * @param string $outTradeNo 唯一的订单号 32 | * @param string $orderName 订单名称 33 | * @param string $notifyUrl 支付结果通知url 不要有问号 34 | * @param string $timestamp 订单发起时间 35 | * @return array 36 | */ 37 | public function createJsBizPackage($totalFee, $outTradeNo, $orderName, $notifyUrl, $timestamp) 38 | { 39 | $config = array( 40 | 'mch_id' => $this->mchid, 41 | 'appid' => $this->appid, 42 | 'key' => $this->apiKey, 43 | ); 44 | //$orderName = iconv('GBK','UTF-8',$orderName); 45 | $unified = array( 46 | 'appid' => $config['appid'], 47 | 'attach' => 'pay', //商家数据包,原样返回,如果填写中文,请注意转换为utf-8 48 | 'body' => $orderName, 49 | 'mch_id' => $config['mch_id'], 50 | 'nonce_str' => self::createNonceStr(), 51 | 'notify_url' => $notifyUrl, 52 | 'out_trade_no' => $outTradeNo, 53 | 'spbill_create_ip' => '127.0.0.1', 54 | 'total_fee' => floatval($totalFee) * 100, //单位 转为分 55 | 'trade_type' => 'NATIVE', 56 | ); 57 | $unified['sign'] = self::getSign($unified, $config['key']); 58 | $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified)); 59 | //禁止引用外部xml实体 60 | libxml_disable_entity_loader(true); 61 | $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 62 | if ($unifiedOrder === false) { 63 | die('parse xml error'); 64 | } 65 | if ($unifiedOrder->return_code != 'SUCCESS') { 66 | die($unifiedOrder->return_msg); 67 | } 68 | if ($unifiedOrder->result_code != 'SUCCESS') { 69 | die($unifiedOrder->err_code); 70 | } 71 | $codeUrl = (array)($unifiedOrder->code_url); 72 | if(!$codeUrl[0]) exit('get code_url error'); 73 | $arr = array( 74 | "appId" => $config['appid'], 75 | "timeStamp" => $timestamp, 76 | "nonceStr" => self::createNonceStr(), 77 | "package" => "prepay_id=" . $unifiedOrder->prepay_id, 78 | "signType" => 'MD5', 79 | "code_url" => $codeUrl[0], 80 | ); 81 | $arr['paySign'] = self::getSign($arr, $config['key']); 82 | return $arr; 83 | } 84 | public function notify() 85 | { 86 | $config = array( 87 | 'mch_id' => $this->mchid, 88 | 'appid' => $this->appid, 89 | 'key' => $this->apiKey, 90 | ); 91 | $postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; 92 | $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); 93 | if ($postObj === false) { 94 | die('parse xml error'); 95 | } 96 | if ($postObj->return_code != 'SUCCESS') { 97 | die($postObj->return_msg); 98 | } 99 | if ($postObj->result_code != 'SUCCESS') { 100 | die($postObj->err_code); 101 | } 102 | $arr = (array)$postObj; 103 | unset($arr['sign']); 104 | if (self::getSign($arr, $config['key']) == $postObj->sign) { 105 | echo ''; 106 | return $postObj; 107 | } 108 | } 109 | /** 110 | * curl get 111 | * 112 | * @param string $url 113 | * @param array $options 114 | * @return mixed 115 | */ 116 | public static function curlGet($url = '', $options = array()) 117 | { 118 | $ch = curl_init($url); 119 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 120 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 121 | if (!empty($options)) { 122 | curl_setopt_array($ch, $options); 123 | } 124 | //https请求 不验证证书和host 125 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 126 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 127 | $data = curl_exec($ch); 128 | curl_close($ch); 129 | return $data; 130 | } 131 | public static function curlPost($url = '', $postData = '', $options = array()) 132 | { 133 | if (is_array($postData)) { 134 | $postData = http_build_query($postData); 135 | } 136 | $ch = curl_init(); 137 | curl_setopt($ch, CURLOPT_URL, $url); 138 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 139 | curl_setopt($ch, CURLOPT_POST, 1); 140 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 141 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 142 | if (!empty($options)) { 143 | curl_setopt_array($ch, $options); 144 | } 145 | //https请求 不验证证书和host 146 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 147 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 148 | $data = curl_exec($ch); 149 | curl_close($ch); 150 | return $data; 151 | } 152 | public static function createNonceStr($length = 16) 153 | { 154 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 155 | $str = ''; 156 | for ($i = 0; $i < $length; $i++) { 157 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 158 | } 159 | return $str; 160 | } 161 | public static function arrayToXml($arr) 162 | { 163 | $xml = ""; 164 | foreach ($arr as $key => $val) { 165 | if (is_numeric($val)) { 166 | $xml .= "<" . $key . ">" . $val . ""; 167 | } else 168 | $xml .= "<" . $key . ">"; 169 | } 170 | $xml .= ""; 171 | return $xml; 172 | } 173 | /** 174 | * 获取签名 175 | */ 176 | public static function getSign($params, $key) 177 | { 178 | ksort($params, SORT_STRING); 179 | $unSignParaString = self::formatQueryParaMap($params, false); 180 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 181 | return $signStr; 182 | } 183 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 184 | { 185 | $buff = ""; 186 | ksort($paraMap); 187 | foreach ($paraMap as $k => $v) { 188 | if (null != $v && "null" != $v) { 189 | if ($urlEncode) { 190 | $v = urlencode($v); 191 | } 192 | $buff .= $k . "=" . $v . "&"; 193 | } 194 | } 195 | $reqPar = ''; 196 | if (strlen($buff) > 0) { 197 | $reqPar = substr($buff, 0, strlen($buff) - 1); 198 | } 199 | return $reqPar; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /native_v3.php: -------------------------------------------------------------------------------- 1 | setTotalFee($payAmount); 17 | $wxPay->setOutTradeNo($outTradeNo); 18 | $wxPay->setOrderName($orderName); 19 | $wxPay->setNotifyUrl($notifyUrl); 20 | $result = $wxPay->doPay(); 21 | if(isset($result['code'])){ 22 | echo $result['code'].':'.$result['message'];exit(); 23 | } 24 | //生成二维码 25 | $url = 'https://wenhairu.com/static/api/qr/?size=300&text=' . $result['code_url']; 26 | echo "
"; 27 | echo '二维码内容:' . $result['code_url']; 28 | 29 | class WxpayService 30 | { 31 | protected $mchid; 32 | protected $appid; 33 | protected $apiKey; 34 | protected $privateKeyPath; 35 | protected $serialNumber; 36 | protected $totalFee; 37 | protected $outTradeNo; 38 | protected $orderName; 39 | protected $notifyUrl; 40 | protected $auth; 41 | protected $gateWay='https://api.mch.weixin.qq.com/v3'; 42 | 43 | public function __construct($mchid, $appid, $apikey, $privateKeyPath, $serialNumber) 44 | { 45 | $this->mchid = $mchid; 46 | $this->appid = $appid; 47 | $this->apiKey = $apikey; 48 | $this->privateKeyPath = $privateKeyPath; 49 | $this->serialNumber = $serialNumber; 50 | } 51 | 52 | public function setTotalFee($totalFee) 53 | { 54 | $this->totalFee = floatval($totalFee); 55 | } 56 | 57 | public function setOutTradeNo($outTradeNo) 58 | { 59 | $this->outTradeNo = $outTradeNo; 60 | } 61 | 62 | public function setOrderName($orderName) 63 | { 64 | $this->orderName = $orderName; 65 | } 66 | 67 | public function setNotifyUrl($notifyUrl) 68 | { 69 | $this->notifyUrl = $notifyUrl; 70 | } 71 | 72 | /** 73 | * 发起支付 74 | */ 75 | public function doPay() 76 | { 77 | $reqParams = array( 78 | 'appid' => $this->appid, //公众号或移动应用appid 79 | 'mchid' => $this->mchid, //商户号 80 | 'description' => $this->orderName, //商品描述 81 | 'attach' => 'pay', //附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用 82 | 'notify_url' => $this->notifyUrl, //通知URL必须为直接可访问的URL,不允许携带查询串。 83 | 'out_trade_no' => $this->outTradeNo, //商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一,详见【商户订单号】。特殊规则:最小字符长度为6 84 | 'amount'=>array( 85 | 'total'=> floatval($this->totalFee) * 100, //订单总金额,单位为分 86 | 'currency'=> 'CNY', //CNY:人民币,境内商户号仅支持人民币 87 | ), 88 | 'scene_info'=>array( //支付场景描述 89 | 'payer_client_ip'=>'127.0.0.1' //调用微信支付API的机器IP 90 | ) 91 | ); 92 | $reqUrl = $this->gateWay.'/pay/transactions/native'; 93 | $this->getAuthStr($reqUrl,$reqParams); 94 | $response = $this->curlPost($reqUrl,$reqParams); 95 | return json_decode($response,true); 96 | 97 | } 98 | 99 | public function curlPost($url = '', $postData = array(), $options = array()) 100 | { 101 | if (is_array($postData)) { 102 | $postData = json_encode($postData); 103 | } 104 | $ch = curl_init(); 105 | curl_setopt($ch, CURLOPT_URL, $url); 106 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 107 | curl_setopt($ch, CURLOPT_POST, 1); 108 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 109 | curl_setopt($ch, CURLOPT_HTTPHEADER, array( 110 | 'Authorization:'.$this->auth, 111 | 'Content-Type:application/json', 112 | 'Accept:application/json', 113 | 'User-Agent:'.$_SERVER['HTTP_USER_AGENT'] 114 | )); 115 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 116 | if (!empty($options)) { 117 | curl_setopt_array($ch, $options); 118 | } 119 | //https请求 不验证证书和host 120 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 121 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 122 | $data = curl_exec($ch); 123 | curl_close($ch); 124 | return $data; 125 | } 126 | 127 | private function getSchema() 128 | { 129 | return 'WECHATPAY2-SHA256-RSA2048'; 130 | } 131 | 132 | public function getAuthStr($requestUrl,$reqParams=array()) 133 | { 134 | $schema = $this->getSchema(); 135 | $token = $this->getToken($requestUrl,$reqParams); 136 | $this->auth = $schema.' '.$token; 137 | return $this->auth; 138 | } 139 | 140 | private function getNonce() 141 | { 142 | static $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 143 | $charactersLength = strlen($characters); 144 | $randomString = ''; 145 | for ($i = 0; $i < 32; $i++) { 146 | $randomString .= $characters[rand(0, $charactersLength - 1)]; 147 | } 148 | return $randomString; 149 | } 150 | 151 | public function getToken($requestUrl,$reqParams=array()) 152 | { 153 | $body = $reqParams ? json_encode($reqParams) : ''; 154 | $nonce = $this->getNonce(); 155 | $timestamp = time(); 156 | $message = $this->buildMessage($nonce, $timestamp, $requestUrl,$body); 157 | $sign = $this->sign($message); 158 | $serialNo = $this->serialNumber; 159 | return sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', 160 | $this->mchid, $nonce, $timestamp, $serialNo, $sign 161 | ); 162 | } 163 | 164 | private function buildMessage($nonce, $timestamp, $requestUrl, $body = '') 165 | { 166 | $method = 'POST'; 167 | $urlParts = parse_url($requestUrl); 168 | $canonicalUrl = ($urlParts['path'] . (!empty($urlParts['query']) ? "?{$urlParts['query']}" : "")); 169 | return strtoupper($method) . "\n" . 170 | $canonicalUrl . "\n" . 171 | $timestamp . "\n" . 172 | $nonce . "\n" . 173 | $body . "\n"; 174 | } 175 | 176 | private function sign($message) 177 | { 178 | if (!in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { 179 | throw new \RuntimeException("当前PHP环境不支持SHA256withRSA"); 180 | } 181 | $res = file_get_contents($this->privateKeyPath); 182 | if (!openssl_sign($message, $sign, $res, 'sha256WithRSAEncryption')) { 183 | throw new \UnexpectedValueException("签名验证过程发生了错误"); 184 | } 185 | return base64_encode($sign); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /notify.php: -------------------------------------------------------------------------------- 1 | notify(); 14 | if($result){ 15 | //完成你的逻辑 16 | //例如连接数据库,获取付款金额$result['cash_fee'],获取订单号$result['out_trade_no'],修改数据库中的订单状态等; 17 | //现金支付金额:$result['cash_fee'] 18 | //订单金额:$result['total_fee'] 19 | //商户订单号:$result['out_trade_no'] 20 | //付款银行:$result['bank_type'] 21 | //货币种类:$result['fee_type'] 22 | //是否关注公众账号:$result['is_subscribe'] 23 | //用户标识:$result['openid'] 24 | //业务结果:$result['result_code'] SUCCESS/FAIL 25 | //支付完成时间:$result['time_end'] 格式为yyyyMMddHHmmss 26 | //具体详细请看微信文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8 27 | }else{ 28 | echo 'pay error'; 29 | } 30 | class WxpayService 31 | { 32 | protected $mchid; 33 | protected $appid; 34 | protected $apiKey; 35 | public function __construct($mchid, $appid, $key) 36 | { 37 | $this->mchid = $mchid; 38 | $this->appid = $appid; 39 | $this->apiKey = $key; 40 | } 41 | 42 | public function notify() 43 | { 44 | $config = array( 45 | 'mch_id' => $this->mchid, 46 | 'appid' => $this->appid, 47 | 'key' => $this->apiKey, 48 | ); 49 | $postStr = file_get_contents('php://input'); 50 | //禁止引用外部xml实体 51 | libxml_disable_entity_loader(true); 52 | $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); 53 | if ($postObj === false) { 54 | die('parse xml error'); 55 | } 56 | if ($postObj->return_code != 'SUCCESS') { 57 | die($postObj->return_msg); 58 | } 59 | if ($postObj->result_code != 'SUCCESS') { 60 | die($postObj->err_code); 61 | } 62 | $arr = (array)$postObj; 63 | unset($arr['sign']); 64 | if (self::getSign($arr, $config['key']) == $postObj->sign) { 65 | echo ''; 66 | return $arr; 67 | } 68 | } 69 | 70 | /** 71 | * 获取签名 72 | */ 73 | public static function getSign($params, $key) 74 | { 75 | ksort($params, SORT_STRING); 76 | $unSignParaString = self::formatQueryParaMap($params, false); 77 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 78 | return $signStr; 79 | } 80 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 81 | { 82 | $buff = ""; 83 | ksort($paraMap); 84 | foreach ($paraMap as $k => $v) { 85 | if (null != $v && "null" != $v) { 86 | if ($urlEncode) { 87 | $v = urlencode($v); 88 | } 89 | $buff .= $k . "=" . $v . "&"; 90 | } 91 | } 92 | $reqPar = ''; 93 | if (strlen($buff) > 0) { 94 | $reqPar = substr($buff, 0, strlen($buff) - 1); 95 | } 96 | return $reqPar; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /notify_v3.php: -------------------------------------------------------------------------------- 1 | validate(); 10 | if($result===false){ 11 | //验证签名失败 12 | exit('sign error'); 13 | } 14 | $result = $wxPay->notify(); 15 | if ($result === false) { 16 | exit('pay error'); 17 | } 18 | if ($result['trade_state'] == 'SUCCESS') { 19 | //支付成功,完成你的逻辑 20 | //例如连接数据库,获取付款金额$result['amount']['total'],获取订单号$result['out_trade_no']修改数据库中的订单状态等; 21 | //订单总金额,单位为分:$result['amount']['total'] 22 | //用户支付金额,单位为分:$result['amount']['payer_total'] 23 | //商户订单号:$result['out_trade_no'] 24 | //微信支付订单号:$result['transaction_id'] 25 | //银行类型:$result['bank_type'] 26 | //支付完成时间:$result['success_time'] 格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE 27 | //用户标识:$result['payer']['openid'] 28 | //交易状态:$result['trade_state'] 29 | //具体详细请看微信文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transactions/chapter3_11.shtml 30 | echo 'success'; 31 | } 32 | 33 | 34 | class WxpayService 35 | { 36 | protected $apiKey; 37 | protected $publicKeyPath; 38 | protected $publicKey; 39 | 40 | public function __construct($apikey, $publicKeyPath) 41 | { 42 | $this->apiKey = $apikey; 43 | $this->publicKeyPath = $publicKeyPath; 44 | } 45 | 46 | public function getHeader($key = '') 47 | { 48 | $headers = getallheaders(); 49 | if ($key) { 50 | return $headers[$key]; 51 | } 52 | return $headers; 53 | } 54 | 55 | public function validate() 56 | { 57 | $serialNo = $this->getHeader('Wechatpay-Serial'); 58 | $sign = $this->getHeader('Wechatpay-Signature'); 59 | $timestamp = $this->getHeader('Wechatpay-Timestamp'); 60 | $nonce = $this->getHeader('Wechatpay-Nonce'); 61 | if (!isset($serialNo, $sign, $timestamp, $nonce)) { 62 | return false; 63 | } 64 | // if (!$this->checkTimestamp($timestamp)) { 65 | // return false; 66 | // } 67 | $body = file_get_contents('php://input'); 68 | $message = "$timestamp\n$nonce\n$body\n"; 69 | 70 | $certificate = openssl_x509_read(file_get_contents($this->publicKeyPath)); 71 | $_serialNo = $this->parseSerialNo($certificate); 72 | if ($serialNo !== $_serialNo) return false; 73 | $this->publicKey = openssl_get_publickey($certificate); 74 | return $this->verify($message, $sign); 75 | } 76 | 77 | private function verify($message, $signature) 78 | { 79 | if (!$this->publicKey) { 80 | return false; 81 | } 82 | if (!in_array('sha256WithRSAEncryption', openssl_get_md_methods(true))) { 83 | exit("当前PHP环境不支持SHA256withRSA"); 84 | } 85 | $signature = base64_decode($signature); 86 | return (bool)openssl_verify($message, $signature, $this->publicKey, 'sha256WithRSAEncryption'); 87 | } 88 | 89 | private function parseSerialNo($certificate) 90 | { 91 | $info = openssl_x509_parse($certificate); 92 | if (!isset($info['serialNumber']) && !isset($info['serialNumberHex'])) { 93 | exit('证书格式错误'); 94 | } 95 | 96 | $serialNo = ''; 97 | if (isset($info['serialNumberHex'])) { 98 | $serialNo = $info['serialNumberHex']; 99 | } else { 100 | if (strtolower(substr($info['serialNumber'], 0, 2)) == '0x') { // HEX format 101 | $serialNo = substr($info['serialNumber'], 2); 102 | } else { // DEC format 103 | $value = $info['serialNumber']; 104 | $hexvalues = ['0', '1', '2', '3', '4', '5', '6', '7', 105 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']; 106 | while ($value != '0') { 107 | $serialNo = $hexvalues[bcmod($value, '16')] . $serialNo; 108 | $value = bcdiv($value, '16', 0); 109 | } 110 | } 111 | } 112 | 113 | return strtoupper($serialNo); 114 | } 115 | 116 | protected function checkTimestamp($timestamp) 117 | { 118 | return abs((int)$timestamp - time()) <= 120; 119 | } 120 | 121 | public function notify() 122 | { 123 | $postStr = file_get_contents('php://input'); 124 | $postData = json_decode($postStr, true); 125 | if ($postData['resource']) { 126 | $data = $this->decryptToString($postData['resource']['associated_data'], $postData['resource']['nonce'], $postData['resource']['ciphertext']); 127 | $data = json_decode($data, true); 128 | return is_array($data) ? $data : false; 129 | } 130 | return false; 131 | } 132 | 133 | public function decryptToString($associatedData, $nonceStr, $ciphertext) 134 | { 135 | $ciphertext = base64_decode($ciphertext); 136 | if (strlen($ciphertext) <= 16) { 137 | return false; 138 | } 139 | 140 | // ext-sodium (default installed on >= PHP 7.2) 141 | if (function_exists('sodium_crypto_aead_aes256gcm_is_available') && 142 | sodium_crypto_aead_aes256gcm_is_available()) { 143 | return sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->apiKey); 144 | } 145 | 146 | // ext-libsodium (need install libsodium-php 1.x via pecl) 147 | if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && 148 | \Sodium\crypto_aead_aes256gcm_is_available()) { 149 | return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->apiKey); 150 | } 151 | 152 | // openssl (PHP >= 7.1 support AEAD) 153 | if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', openssl_get_cipher_methods())) { 154 | $ctext = substr($ciphertext, 0, -16); 155 | $authTag = substr($ciphertext, -16); 156 | 157 | return openssl_decrypt($ctext, 'aes-256-gcm', $this->apiKey, OPENSSL_RAW_DATA, $nonceStr, 158 | $authTag, $associatedData); 159 | } 160 | 161 | exit('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php'); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /orderquery.php: -------------------------------------------------------------------------------- 1 | orderquery($outTradeNo); 13 | echo json_encode($result);die; 14 | 15 | class WxpayService 16 | { 17 | protected $mchid; 18 | protected $appid; 19 | protected $apiKey; 20 | protected $returnUrl; 21 | public function __construct($mchid, $appid, $key) 22 | { 23 | $this->mchid = $mchid; 24 | $this->appid = $appid; 25 | $this->apiKey = $key; 26 | } 27 | 28 | public function setReturnUrl($returnUrl) 29 | { 30 | $this->returnUrl = $returnUrl; 31 | } 32 | 33 | public function orderquery($outTradeNo) 34 | { 35 | $config = array( 36 | 'mch_id' => $this->mchid, 37 | 'appid' => $this->appid, 38 | 'key' => $this->apiKey, 39 | ); 40 | //$orderName = iconv('GBK','UTF-8',$orderName); 41 | $unified = array( 42 | 'appid' => $config['appid'], 43 | 'mch_id' => $config['mch_id'], 44 | 'out_trade_no' => $outTradeNo, 45 | 'nonce_str' => self::createNonceStr(), 46 | ); 47 | $unified['sign'] = self::getSign($unified, $config['key']); 48 | $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/orderquery', self::arrayToXml($unified)); 49 | $queryResult = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 50 | if ($queryResult === false) { 51 | die('parse xml error'); 52 | } 53 | if ($queryResult->return_code != 'SUCCESS') { 54 | die($queryResult->return_msg); 55 | } 56 | $trade_state = $queryResult->trade_state; 57 | $data['code'] = $trade_state=='SUCCESS' ? 0 : 1; 58 | $data['data'] = $trade_state; 59 | $data['msg'] = $this->getTradeSTate($trade_state); 60 | $data['time'] = date('Y-m-d H:i:s'); 61 | return $data;exit(); 62 | } 63 | 64 | public function getTradeSTate($str) 65 | { 66 | switch ($str){ 67 | case 'SUCCESS'; 68 | return '支付成功'; 69 | case 'REFUND'; 70 | return '转入退款'; 71 | case 'NOTPAY'; 72 | return '未支付'; 73 | case 'CLOSED'; 74 | return '已关闭'; 75 | case 'REVOKED'; 76 | return '已撤销(刷卡支付)'; 77 | case 'USERPAYING'; 78 | return '用户支付中'; 79 | case 'PAYERROR'; 80 | return '支付失败'; 81 | } 82 | } 83 | /** 84 | * curl get 85 | * 86 | * @param string $url 87 | * @param array $options 88 | * @return mixed 89 | */ 90 | public static function curlGet($url = '', $options = array()) 91 | { 92 | $ch = curl_init($url); 93 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 94 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 95 | if (!empty($options)) { 96 | curl_setopt_array($ch, $options); 97 | } 98 | //https请求 不验证证书和host 99 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 100 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 101 | $data = curl_exec($ch); 102 | curl_close($ch); 103 | return $data; 104 | } 105 | public static function curlPost($url = '', $postData = '', $options = array()) 106 | { 107 | if (is_array($postData)) { 108 | $postData = http_build_query($postData); 109 | } 110 | $ch = curl_init(); 111 | curl_setopt($ch, CURLOPT_URL, $url); 112 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 113 | curl_setopt($ch, CURLOPT_POST, 1); 114 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 115 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 116 | if (!empty($options)) { 117 | curl_setopt_array($ch, $options); 118 | } 119 | //https请求 不验证证书和host 120 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 121 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 122 | $data = curl_exec($ch); 123 | curl_close($ch); 124 | return $data; 125 | } 126 | public static function createNonceStr($length = 16) 127 | { 128 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 129 | $str = ''; 130 | for ($i = 0; $i < $length; $i++) { 131 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 132 | } 133 | return $str; 134 | } 135 | public static function arrayToXml($arr) 136 | { 137 | $xml = ""; 138 | foreach ($arr as $key => $val) { 139 | if (is_numeric($val)) { 140 | $xml .= "<" . $key . ">" . $val . ""; 141 | } else 142 | $xml .= "<" . $key . ">"; 143 | } 144 | $xml .= ""; 145 | return $xml; 146 | } 147 | /** 148 | * 获取签名 149 | */ 150 | public static function getSign($params, $key) 151 | { 152 | ksort($params, SORT_STRING); 153 | $unSignParaString = self::formatQueryParaMap($params, false); 154 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 155 | return $signStr; 156 | } 157 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 158 | { 159 | $buff = ""; 160 | ksort($paraMap); 161 | foreach ($paraMap as $k => $v) { 162 | if (null != $v && "null" != $v) { 163 | if ($urlEncode) { 164 | $v = urlencode($v); 165 | } 166 | $buff .= $k . "=" . $v . "&"; 167 | } 168 | } 169 | $reqPar = ''; 170 | if (strlen($buff) > 0) { 171 | $reqPar = substr($buff, 0, strlen($buff) - 1); 172 | } 173 | return $reqPar; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /redpack.php: -------------------------------------------------------------------------------- 1 | 账户设置->API安全->下载证书 5 | * 2.默认的使用场景是抽奖(即scene_id参数为PRODUCT_2),额度是1-200元,所以测试时的最低金额是1元。如需修改在产品中心->产品大全->现金红包->产品设置中修改 6 | * 3.错误码参照 :https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 7 | */ 8 | header('Content-type:text/html; Charset=utf-8'); 9 | $mchid = 'xxxxx'; //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送 10 | $appid = 'xxxxx'; //微信支付申请对应的公众号的APPID 11 | $appKey = 'xxxxx'; //微信支付申请对应的公众号的APP Key 12 | $apiKey = 'xxxxx'; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥 13 | //填写证书所在位置,证书在https://pay.weixin.qq.com 账户中心->账户设置->API安全->下载证书,下载后将apiclient_cert.pem和apiclient_key.pem上传到服务器。 14 | $apiclient_cert = getcwd().'/cert/apiclient_cert.pem'; 15 | $apiclient_key = getcwd().'/cert/apiclient_key.pem'; 16 | 17 | //①、获取当前访问页面的用户openid(如果给指定用户发送红包,则填写指定用户的openid) 18 | $wxPay = new WxpayService($mchid,$appid,$appKey,$apiKey,$apiclient_cert,$apiclient_key); 19 | $openId = $wxPay->GetOpenid(); //获取openid 20 | if(!$openId) exit('获取openid失败'); 21 | //②、发送红包 22 | $outTradeNo = uniqid(); //你自己的商品订单号 23 | $payAmount = 1; //红包金额,单位:元 24 | $sendName = '元旦'; //红包发送者名称 25 | $wishing = '祝您元旦快乐!'; //红包祝福语 26 | $act_name='元旦快乐'; //活动名称 27 | $result = $wxPay->createJsBizPackage($openId,$payAmount,$outTradeNo,$sendName,$wishing,$act_name); 28 | echo 'success'; 29 | class WxpayService 30 | { 31 | protected $mchid; 32 | protected $appid; 33 | protected $appKey; 34 | protected $apiKey; 35 | protected $apiclient_cert; 36 | protected $apiclient_key; 37 | public $data = null; 38 | 39 | public function __construct($mchid, $appid, $appKey,$key,$apiclient_cert,$apiclient_key) 40 | { 41 | $this->mchid = $mchid; 42 | $this->appid = $appid; 43 | $this->appKey = $appKey; 44 | $this->apiKey = $key; 45 | $this->apiclient_cert = $apiclient_cert; 46 | $this->apiclient_key = $apiclient_key; 47 | } 48 | 49 | /** 50 | * 通过跳转获取用户的openid,跳转流程如下: 51 | * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize 52 | * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code 53 | * @return 用户的openid 54 | */ 55 | public function GetOpenid() 56 | { 57 | //通过code获得openid 58 | if (!isset($_GET['code'])){ 59 | //触发微信返回code码 60 | $scheme = $_SERVER['HTTPS']=='on' ? 'https://' : 'http://'; 61 | $uri = $_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']; 62 | if($_SERVER['REQUEST_URI']){ 63 | $uri = $_SERVER['REQUEST_URI']; 64 | } 65 | $baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$uri); 66 | $url = $this->__CreateOauthUrlForCode($baseUrl); 67 | Header("Location: $url"); 68 | exit(); 69 | } else { 70 | //获取code码,以获取openid 71 | $code = $_GET['code']; 72 | $openid = $this->getOpenidFromMp($code); 73 | return $openid; 74 | } 75 | } 76 | 77 | /** 78 | * 通过code从工作平台获取openid机器access_token 79 | * @param string $code 微信跳转回来带上的code 80 | * @return openid 81 | */ 82 | public function GetOpenidFromMp($code) 83 | { 84 | $url = $this->__CreateOauthUrlForOpenid($code); 85 | $res = self::curlGet($url); 86 | //取出openid 87 | $data = json_decode($res,true); 88 | $this->data = $data; 89 | $openid = $data['openid']; 90 | return $openid; 91 | } 92 | 93 | /** 94 | * 构造获取open和access_toke的url地址 95 | * @param string $code,微信跳转带回的code 96 | * @return 请求的url 97 | */ 98 | private function __CreateOauthUrlForOpenid($code) 99 | { 100 | $urlObj["appid"] = $this->appid; 101 | $urlObj["secret"] = $this->appKey; 102 | $urlObj["code"] = $code; 103 | $urlObj["grant_type"] = "authorization_code"; 104 | $bizString = $this->ToUrlParams($urlObj); 105 | return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString; 106 | } 107 | 108 | /** 109 | * 构造获取code的url连接 110 | * @param string $redirectUrl 微信服务器回跳的url,需要url编码 111 | * @return 返回构造好的url 112 | */ 113 | private function __CreateOauthUrlForCode($redirectUrl) 114 | { 115 | $urlObj["appid"] = $this->appid; 116 | $urlObj["redirect_uri"] = "$redirectUrl"; 117 | $urlObj["response_type"] = "code"; 118 | $urlObj["scope"] = "snsapi_base"; 119 | $urlObj["state"] = "STATE"."#wechat_redirect"; 120 | $bizString = $this->ToUrlParams($urlObj); 121 | return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString; 122 | } 123 | 124 | /** 125 | * 拼接签名字符串 126 | * @param array $urlObj 127 | * @return 返回已经拼接好的字符串 128 | */ 129 | private function ToUrlParams($urlObj) 130 | { 131 | $buff = ""; 132 | foreach ($urlObj as $k => $v) 133 | { 134 | if($k != "sign") $buff .= $k . "=" . $v . "&"; 135 | } 136 | $buff = trim($buff, "&"); 137 | return $buff; 138 | } 139 | 140 | /** 141 | * 统一下单 142 | * @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid 143 | * @param float $totalFee 收款总费用 单位元 144 | * @param string $outTradeNo 唯一的订单号 145 | * @param string $orderName 订单名称 146 | * @param string $notifyUrl 支付结果通知url 不要有问号 147 | * @param string $timestamp 支付时间 148 | * @return string 149 | */ 150 | public function createJsBizPackage($openid, $totalFee, $outTradeNo, $sendName,$wishing,$actName) 151 | { 152 | $config = array( 153 | 'mch_id' => $this->mchid, 154 | 'appid' => $this->appid, 155 | 'key' => $this->apiKey, 156 | ); 157 | $unified = array( 158 | 'wxappid' => $config['appid'], 159 | 'send_name' => $sendName, 160 | 'mch_id' => $config['mch_id'], 161 | 'nonce_str' => self::createNonceStr(), 162 | 're_openid' => $openid, 163 | 'mch_billno' => $outTradeNo, 164 | 'client_ip' => '127.0.0.1', 165 | 'total_amount' => floatval($totalFee) * 100, //单位 转为分 166 | 'total_num'=>1, //红包发放总人数 167 | 'wishing'=>$wishing, //红包祝福语 168 | 'act_name'=>$actName, //活动名称 169 | 'remark'=>'remark', //备注信息,如为中文注意转为UTF8编码 170 | 'scene_id'=>'PRODUCT_2', //发放红包使用场景,红包金额大于200时必传。https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3 171 | ); 172 | $unified['sign'] = self::getSign($unified, $config['key']); 173 | $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack', self::arrayToXml($unified)); 174 | file_put_contents('1.txt',print_r($responseXml,true)); 175 | // print_r($responseXml,true);die; 176 | $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 177 | if ($unifiedOrder === false) { 178 | die('parse xml error'); 179 | } 180 | if ($unifiedOrder->return_code != 'SUCCESS') { 181 | die($unifiedOrder->return_msg); 182 | } 183 | if ($unifiedOrder->result_code != 'SUCCESS') { 184 | die($unifiedOrder->err_code); 185 | } 186 | return true; 187 | } 188 | 189 | public static function curlGet($url = '', $options = array()) 190 | { 191 | $ch = curl_init($url); 192 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 193 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 194 | if (!empty($options)) { 195 | curl_setopt_array($ch, $options); 196 | } 197 | //https请求 不验证证书和host 198 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 199 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 200 | $data = curl_exec($ch); 201 | curl_close($ch); 202 | return $data; 203 | } 204 | 205 | public function curlPost($url = '', $postData = '', $options = array()) 206 | { 207 | if (is_array($postData)) { 208 | $postData = http_build_query($postData); 209 | } 210 | $ch = curl_init(); 211 | curl_setopt($ch, CURLOPT_URL, $url); 212 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 213 | curl_setopt($ch, CURLOPT_POST, 1); 214 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 215 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 216 | if (!empty($options)) { 217 | curl_setopt_array($ch, $options); 218 | } 219 | //https请求 不验证证书和host 220 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 221 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 222 | 223 | //第一种方法,cert 与 key 分别属于两个.pem文件 224 | //默认格式为PEM,可以注释 225 | curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); 226 | curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/cert/apiclient_cert.pem'); 227 | //默认格式为PEM,可以注释 228 | curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); 229 | curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/cert/apiclient_key.pem'); 230 | //第二种方式,两个文件合成一个.pem文件 231 | // curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem'); 232 | 233 | $data = curl_exec($ch); 234 | var_dump($data);die; 235 | curl_close($ch); 236 | return $data; 237 | } 238 | 239 | public static function createNonceStr($length = 16) 240 | { 241 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 242 | $str = ''; 243 | for ($i = 0; $i < $length; $i++) { 244 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 245 | } 246 | return $str; 247 | } 248 | public static function arrayToXml($arr) 249 | { 250 | $xml = ""; 251 | foreach ($arr as $key => $val) { 252 | if (is_numeric($val)) { 253 | $xml .= "<" . $key . ">" . $val . ""; 254 | } else 255 | $xml .= "<" . $key . ">"; 256 | } 257 | $xml .= ""; 258 | file_put_contents('1.txt',$xml); 259 | return $xml; 260 | } 261 | 262 | public static function getSign($params, $key) 263 | { 264 | ksort($params, SORT_STRING); 265 | $unSignParaString = self::formatQueryParaMap($params, false); 266 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 267 | return $signStr; 268 | } 269 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 270 | { 271 | $buff = ""; 272 | ksort($paraMap); 273 | foreach ($paraMap as $k => $v) { 274 | if (null != $v && "null" != $v) { 275 | if ($urlEncode) { 276 | $v = urlencode($v); 277 | } 278 | $buff .= $k . "=" . $v . "&"; 279 | } 280 | } 281 | $reqPar = ''; 282 | if (strlen($buff) > 0) { 283 | $reqPar = substr($buff, 0, strlen($buff) - 1); 284 | } 285 | return $reqPar; 286 | } 287 | } 288 | ?> 289 | -------------------------------------------------------------------------------- /refund.php: -------------------------------------------------------------------------------- 1 | 账户设置->API安全->下载证书,证书路径在第119行和122行修改 5 | * 2.错误码参照 :https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 6 | */ 7 | header('Content-type:text/html; Charset=utf-8'); 8 | $mchid = 'xxxxx'; //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送 9 | $appid = 'xxxxx'; //微信支付申请对应的公众号的APPID 10 | $apiKey = 'xxxxx'; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥 11 | $orderNo = ''; //商户订单号(商户订单号与微信订单号二选一,至少填一个) 12 | $wxOrderNo = ''; //微信订单号(商户订单号与微信订单号二选一,至少填一个) 13 | $totalFee = 0.01; //订单金额,单位:元 14 | $refundFee = 0.01; //退款金额,单位:元 15 | $refundNo = 'refund_'.uniqid(); //退款订单号(可随机生成) 16 | $wxPay = new WxpayService($mchid,$appid,$apiKey); 17 | $result = $wxPay->doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo,$orderNo); 18 | if($result===true){ 19 | echo 'refund success';exit(); 20 | } 21 | echo 'refund fail'; 22 | 23 | class WxpayService 24 | { 25 | protected $mchid; 26 | protected $appid; 27 | protected $apiKey; 28 | public $data = null; 29 | 30 | public function __construct($mchid, $appid, $key) 31 | { 32 | $this->mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号 33 | $this->appid = $appid; //微信支付申请对应的公众号的APPID 34 | $this->apiKey = $key; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥 35 | } 36 | 37 | /** 38 | * 退款 39 | * @param float $totalFee 订单金额 单位元 40 | * @param float $refundFee 退款金额 单位元 41 | * @param string $refundNo 退款单号 42 | * @param string $wxOrderNo 微信订单号 43 | * @param string $orderNo 商户订单号 44 | * @return string 45 | */ 46 | public function doRefund($totalFee, $refundFee, $refundNo, $wxOrderNo='',$orderNo='') 47 | { 48 | $config = array( 49 | 'mch_id' => $this->mchid, 50 | 'appid' => $this->appid, 51 | 'key' => $this->apiKey, 52 | ); 53 | $unified = array( 54 | 'appid' => $config['appid'], 55 | 'mch_id' => $config['mch_id'], 56 | 'nonce_str' => self::createNonceStr(), 57 | 'total_fee' => floatval($totalFee) * 100, //订单金额 单位 转为分 58 | 'refund_fee' => floatval($refundFee) * 100, //退款金额 单位 转为分 59 | 'sign_type' => 'MD5', //签名类型 支持HMAC-SHA256和MD5,默认为MD5 60 | 'transaction_id'=>$wxOrderNo, //微信订单号 61 | 'out_trade_no'=>$orderNo, //商户订单号 62 | 'out_refund_no'=>$refundNo, //商户退款单号 63 | 'refund_desc'=>'商品已售完', //退款原因(选填) 64 | ); 65 | $unified['sign'] = self::getSign($unified, $config['key']); 66 | $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/secapi/pay/refund', self::arrayToXml($unified)); 67 | $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 68 | if ($unifiedOrder === false) { 69 | die('parse xml error'); 70 | } 71 | if ($unifiedOrder->return_code != 'SUCCESS') { 72 | die($unifiedOrder->return_msg); 73 | } 74 | if ($unifiedOrder->result_code != 'SUCCESS') { 75 | die($unifiedOrder->err_code); 76 | } 77 | return true; 78 | } 79 | 80 | public static function curlGet($url = '', $options = array()) 81 | { 82 | $ch = curl_init($url); 83 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 84 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 85 | if (!empty($options)) { 86 | curl_setopt_array($ch, $options); 87 | } 88 | //https请求 不验证证书和host 89 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 90 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 91 | $data = curl_exec($ch); 92 | curl_close($ch); 93 | return $data; 94 | } 95 | 96 | public function curlPost($url = '', $postData = '', $options = array()) 97 | { 98 | if (is_array($postData)) { 99 | $postData = http_build_query($postData); 100 | } 101 | $ch = curl_init(); 102 | curl_setopt($ch, CURLOPT_URL, $url); 103 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 104 | curl_setopt($ch, CURLOPT_POST, 1); 105 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 106 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 107 | if (!empty($options)) { 108 | curl_setopt_array($ch, $options); 109 | } 110 | //https请求 不验证证书和host 111 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 112 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 113 | 114 | //第一种方法,cert 与 key 分别属于两个.pem文件 115 | //默认格式为PEM,可以注释 116 | curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); 117 | curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/cert/apiclient_cert.pem'); 118 | //默认格式为PEM,可以注释 119 | curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); 120 | curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/cert/apiclient_key.pem'); 121 | //第二种方式,两个文件合成一个.pem文件 122 | // curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem'); 123 | $data = curl_exec($ch); 124 | curl_close($ch); 125 | return $data; 126 | } 127 | 128 | public static function createNonceStr($length = 16) 129 | { 130 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 131 | $str = ''; 132 | for ($i = 0; $i < $length; $i++) { 133 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 134 | } 135 | return $str; 136 | } 137 | public static function arrayToXml($arr) 138 | { 139 | $xml = ""; 140 | foreach ($arr as $key => $val) { 141 | if (is_numeric($val)) { 142 | $xml .= "<" . $key . ">" . $val . ""; 143 | } else 144 | $xml .= "<" . $key . ">"; 145 | } 146 | $xml .= ""; 147 | return $xml; 148 | } 149 | 150 | public static function getSign($params, $key) 151 | { 152 | ksort($params, SORT_STRING); 153 | $unSignParaString = self::formatQueryParaMap($params, false); 154 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 155 | return $signStr; 156 | } 157 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 158 | { 159 | $buff = ""; 160 | ksort($paraMap); 161 | foreach ($paraMap as $k => $v) { 162 | if (null != $v && "null" != $v) { 163 | if ($urlEncode) { 164 | $v = urlencode($v); 165 | } 166 | $buff .= $k . "=" . $v . "&"; 167 | } 168 | } 169 | $reqPar = ''; 170 | if (strlen($buff) > 0) { 171 | $reqPar = substr($buff, 0, strlen($buff) - 1); 172 | } 173 | return $reqPar; 174 | } 175 | } 176 | ?> 177 | -------------------------------------------------------------------------------- /refund_query.php: -------------------------------------------------------------------------------- 1 | 商户退款订单号 > 微信订单号 > 商户订单号 8 | $orderNo = ''; //商户订单号 9 | $wxOrderNo = ''; //微信订单号 10 | $refundNo=''; //商户退款订单号 11 | $refundId = ''; //微信退款单号(微信生成的退款单号,在申请退款接口有返回) 12 | 13 | $wxPay = new WxpayService($mchid,$appid,$apiKey); 14 | $result = $wxPay->doRefundQuery($refundNo, $wxOrderNo,$orderNo,$refundId); 15 | if($result===true){ 16 | echo 'refund success';exit(); 17 | } 18 | echo 'refund fail'; 19 | 20 | class WxpayService 21 | { 22 | protected $mchid; 23 | protected $appid; 24 | protected $apiKey; 25 | public $data = null; 26 | 27 | public function __construct($mchid, $appid, $key) 28 | { 29 | $this->mchid = $mchid; //https://pay.weixin.qq.com 产品中心-开发配置-商户号 30 | $this->appid = $appid; //微信支付申请对应的公众号的APPID 31 | $this->apiKey = $key; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥 32 | } 33 | 34 | /** 35 | * 退款查询 36 | * @param string $refundNo 商户退款单号 37 | * @param string $wxOrderNo 微信订单号 38 | * @param string $orderNo 商户订单号 39 | * @param string $refundId 微信退款单号 40 | * @return string 41 | */ 42 | public function doRefundQuery($refundNo='', $wxOrderNo='',$orderNo='',$refundId='') 43 | { 44 | $config = array( 45 | 'mch_id' => $this->mchid, 46 | 'appid' => $this->appid, 47 | 'key' => $this->apiKey, 48 | ); 49 | $unified = array( 50 | 'appid' => $config['appid'], 51 | 'mch_id' => $config['mch_id'], 52 | 'nonce_str' => self::createNonceStr(), 53 | 'sign_type' => 'MD5', //签名类型 支持HMAC-SHA256和MD5,默认为MD5 54 | 'transaction_id'=>$wxOrderNo, //微信订单号 55 | 'out_trade_no'=>$orderNo, //商户订单号 56 | 'out_refund_no'=>$refundNo, //商户退款单号 57 | 'refund_id'=>$refundId, //微信退款单号 58 | ); 59 | $unified['sign'] = self::getSign($unified, $config['key']); 60 | $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/pay/refundquery', self::arrayToXml($unified)); 61 | file_put_contents('2.txt',$responseXml); 62 | $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 63 | if ($unifiedOrder === false) { 64 | die('parse xml error'); 65 | } 66 | if ($unifiedOrder->return_code != 'SUCCESS') { 67 | die($unifiedOrder->return_msg); 68 | } 69 | if ($unifiedOrder->result_code != 'SUCCESS') { 70 | die($unifiedOrder->err_code); 71 | } 72 | return true; 73 | } 74 | 75 | public static function curlGet($url = '', $options = array()) 76 | { 77 | $ch = curl_init($url); 78 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 79 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 80 | if (!empty($options)) { 81 | curl_setopt_array($ch, $options); 82 | } 83 | //https请求 不验证证书和host 84 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 85 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 86 | $data = curl_exec($ch); 87 | curl_close($ch); 88 | return $data; 89 | } 90 | 91 | public function curlPost($url = '', $postData = '', $options = array()) 92 | { 93 | if (is_array($postData)) { 94 | $postData = http_build_query($postData); 95 | } 96 | $ch = curl_init(); 97 | curl_setopt($ch, CURLOPT_URL, $url); 98 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 99 | curl_setopt($ch, CURLOPT_POST, 1); 100 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 101 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 102 | if (!empty($options)) { 103 | curl_setopt_array($ch, $options); 104 | } 105 | //https请求 不验证证书和host 106 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 107 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 108 | $data = curl_exec($ch); 109 | curl_close($ch); 110 | return $data; 111 | } 112 | 113 | public static function createNonceStr($length = 16) 114 | { 115 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 116 | $str = ''; 117 | for ($i = 0; $i < $length; $i++) { 118 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 119 | } 120 | return $str; 121 | } 122 | public static function arrayToXml($arr) 123 | { 124 | $xml = ""; 125 | foreach ($arr as $key => $val) { 126 | if (is_numeric($val)) { 127 | $xml .= "<" . $key . ">" . $val . ""; 128 | } else 129 | $xml .= "<" . $key . ">"; 130 | } 131 | $xml .= ""; 132 | return $xml; 133 | } 134 | 135 | public static function getSign($params, $key) 136 | { 137 | ksort($params, SORT_STRING); 138 | $unSignParaString = self::formatQueryParaMap($params, false); 139 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 140 | return $signStr; 141 | } 142 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 143 | { 144 | $buff = ""; 145 | ksort($paraMap); 146 | foreach ($paraMap as $k => $v) { 147 | if (null != $v && "null" != $v) { 148 | if ($urlEncode) { 149 | $v = urlencode($v); 150 | } 151 | $buff .= $k . "=" . $v . "&"; 152 | } 153 | } 154 | $reqPar = ''; 155 | if (strlen($buff) > 0) { 156 | $reqPar = substr($buff, 0, strlen($buff) - 1); 157 | } 158 | return $reqPar; 159 | } 160 | } 161 | ?> -------------------------------------------------------------------------------- /reverse.php: -------------------------------------------------------------------------------- 1 | 账户设置->API安全->下载证书 7 | * 2.错误码参照 :https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3 8 | */ 9 | header('Content-type:text/html; Charset=utf-8'); 10 | /* 配置开始 */ 11 | $mchid = ''; //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送 12 | $appid = ''; //微信支付申请对应的公众号的APPID 13 | $appKey = ''; //微信支付申请对应的公众号的APP Key 14 | $apiKey = ''; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥 15 | //填写证书所在位置,证书在https://pay.weixin.qq.com 账户中心->账户设置->API安全->下载证书,下载后将apiclient_cert.pem和apiclient_key.pem上传到服务器。 16 | $apiclient_cert = getcwd().'/cert/apiclient_cert.pem'; 17 | $apiclient_key = getcwd().'/cert/apiclient_key.pem'; 18 | $transaction_id = ''; //微信的订单号,优先使用。微信订单号与商户订单号不能同时为空 19 | $out_trade_no = ''; //商户订单号。微信订单号与商户订单号不能同时为空 20 | /* 配置结束 */ 21 | $wxPay = new WxpayService($mchid,$appid,$appKey,$apiKey); 22 | $wxPay->setApiclientCert($apiclient_cert); 23 | $wxPay->setApiclientKey($apiclient_key); 24 | $wxPay->setTransactionId($transaction_id); 25 | $wxPay->setOutTradeNo($out_trade_no); 26 | $result = $wxPay->doReverse(); 27 | echo 'success'; 28 | class WxpayService 29 | { 30 | protected $mchid; 31 | protected $appid; 32 | protected $appKey; 33 | protected $apiKey; 34 | protected $apiclient_cert; 35 | protected $apiclient_key; 36 | protected $transactionId; 37 | protected $outTradeNo; 38 | public $data = null; 39 | 40 | public function __construct($mchid, $appid, $appKey,$key) 41 | { 42 | $this->mchid = $mchid; 43 | $this->appid = $appid; 44 | $this->appKey = $appKey; 45 | $this->apiKey = $key; 46 | } 47 | 48 | public function setApiclientCert($apiclient_cert) 49 | { 50 | $this->apiclient_cert = $apiclient_cert; 51 | } 52 | 53 | public function setApiclientKey($apiclient_key) 54 | { 55 | $this->apiclient_key = $apiclient_key; 56 | } 57 | 58 | public function setTransactionId($transaction_id) 59 | { 60 | $this->transactionId = $transaction_id; 61 | } 62 | 63 | public function setOutTradeNo($out_trade_no) 64 | { 65 | $this->outTradeNo = $out_trade_no; 66 | } 67 | 68 | /** 69 | * 撤销订单 70 | */ 71 | public function doReverse() 72 | { 73 | $unified = array( 74 | 'mch_id' => $this->mchid, 75 | 'appid' => $this->appid, 76 | 'transaction_id' => $this->transactionId, 77 | 'out_trade_no' => $this->outTradeNo, 78 | 'nonce_str' => self::createNonceStr(), 79 | ); 80 | $unified['sign'] = self::getSign($unified, $this->apiKey); 81 | $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/secapi/pay/reverse', self::arrayToXml($unified)); 82 | $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 83 | if ($unifiedOrder === false) { 84 | die('parse xml error'); 85 | } 86 | if ($unifiedOrder->return_code != 'SUCCESS') { 87 | die($unifiedOrder->return_msg); 88 | } 89 | if ($unifiedOrder->result_code != 'SUCCESS') { 90 | die($unifiedOrder->err_code.':'.$unifiedOrder->err_code_des); 91 | } 92 | 93 | return true; 94 | } 95 | 96 | public static function curlGet($url = '', $options = array()) 97 | { 98 | $ch = curl_init($url); 99 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 100 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 101 | if (!empty($options)) { 102 | curl_setopt_array($ch, $options); 103 | } 104 | //https请求 不验证证书和host 105 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 106 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 107 | $data = curl_exec($ch); 108 | curl_close($ch); 109 | return $data; 110 | } 111 | 112 | public function curlPost($url = '', $postData = '', $options = array()) 113 | { 114 | if (is_array($postData)) { 115 | $postData = http_build_query($postData); 116 | } 117 | $ch = curl_init(); 118 | curl_setopt($ch, CURLOPT_URL, $url); 119 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 120 | curl_setopt($ch, CURLOPT_POST, 1); 121 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 122 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 123 | if (!empty($options)) { 124 | curl_setopt_array($ch, $options); 125 | } 126 | //https请求 不验证证书和host 127 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 128 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 129 | 130 | //第一种方法,cert 与 key 分别属于两个.pem文件 131 | //默认格式为PEM,可以注释 132 | curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); 133 | curl_setopt($ch,CURLOPT_SSLCERT,$this->apiclient_cert); 134 | //默认格式为PEM,可以注释 135 | curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); 136 | curl_setopt($ch,CURLOPT_SSLKEY,$this->apiclient_key); 137 | //第二种方式,两个文件合成一个.pem文件 138 | // curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem'); 139 | 140 | $data = curl_exec($ch); 141 | //var_dump($data);die; 142 | curl_close($ch); 143 | return $data; 144 | } 145 | 146 | public static function createNonceStr($length = 16) 147 | { 148 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 149 | $str = ''; 150 | for ($i = 0; $i < $length; $i++) { 151 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 152 | } 153 | return $str; 154 | } 155 | public static function arrayToXml($arr) 156 | { 157 | $xml = ""; 158 | foreach ($arr as $key => $val) { 159 | if (is_numeric($val)) { 160 | $xml .= "<" . $key . ">" . $val . ""; 161 | } else 162 | $xml .= "<" . $key . ">"; 163 | } 164 | $xml .= ""; 165 | file_put_contents('1.txt',$xml); 166 | return $xml; 167 | } 168 | 169 | public static function getSign($params, $key) 170 | { 171 | ksort($params, SORT_STRING); 172 | $unSignParaString = self::formatQueryParaMap($params, false); 173 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 174 | return $signStr; 175 | } 176 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 177 | { 178 | $buff = ""; 179 | ksort($paraMap); 180 | foreach ($paraMap as $k => $v) { 181 | if (null != $v && "null" != $v) { 182 | if ($urlEncode) { 183 | $v = urlencode($v); 184 | } 185 | $buff .= $k . "=" . $v . "&"; 186 | } 187 | } 188 | $reqPar = ''; 189 | if (strlen($buff) > 0) { 190 | $reqPar = substr($buff, 0, strlen($buff) - 1); 191 | } 192 | return $reqPar; 193 | } 194 | } 195 | ?> -------------------------------------------------------------------------------- /transfers.php: -------------------------------------------------------------------------------- 1 | 账户设置->API安全->下载证书,证书路径在第207行和210行修改 5 | * 2.错误码参照 :https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 6 | */ 7 | header('Content-type:text/html; Charset=utf-8'); 8 | $mchid = 'xxxxx'; //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送 9 | $appid = 'xxxxx'; //微信支付申请对应的公众号的APPID 10 | $appKey = 'xxxxx'; //微信支付申请对应的公众号的APP Key 11 | $apiKey = 'xxxxx'; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥 12 | 13 | //①、获取当前访问页面的用户openid(如果给指定用户转账,则直接填写指定用户的openid) 14 | $wxPay = new WxpayService($mchid,$appid,$appKey,$apiKey); 15 | $openId = $wxPay->GetOpenid(); //获取openid 16 | if(!$openId) exit('获取openid失败'); 17 | //②、付款 18 | $outTradeNo = uniqid(); //订单号 19 | $payAmount = 1; //转账金额,单位:元。转账最小金额为1元 20 | $trueName = '张三'; //收款人真实姓名 21 | $result = $wxPay->createJsBizPackage($openId,$payAmount,$outTradeNo,$trueName); 22 | echo 'success'; 23 | class WxpayService 24 | { 25 | protected $mchid; 26 | protected $appid; 27 | protected $appKey; 28 | protected $apiKey; 29 | public $data = null; 30 | 31 | public function __construct($mchid, $appid, $appKey,$key) 32 | { 33 | $this->mchid = $mchid; 34 | $this->appid = $appid; 35 | $this->appKey = $appKey; 36 | $this->apiKey = $key; 37 | } 38 | 39 | /** 40 | * 通过跳转获取用户的openid,跳转流程如下: 41 | * 1、设置自己需要调回的url及其其他参数,跳转到微信服务器https://open.weixin.qq.com/connect/oauth2/authorize 42 | * 2、微信服务处理完成之后会跳转回用户redirect_uri地址,此时会带上一些参数,如:code 43 | * @return 用户的openid 44 | */ 45 | public function GetOpenid() 46 | { 47 | //通过code获得openid 48 | if (!isset($_GET['code'])){ 49 | //触发微信返回code码 50 | $scheme = $_SERVER['HTTPS']=='on' ? 'https://' : 'http://'; 51 | $uri = $_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']; 52 | if($_SERVER['REQUEST_URI']){ 53 | $uri = $_SERVER['REQUEST_URI']; 54 | } 55 | $baseUrl = urlencode($scheme.$_SERVER['HTTP_HOST'].$uri); 56 | $url = $this->__CreateOauthUrlForCode($baseUrl); 57 | Header("Location: $url"); 58 | exit(); 59 | } else { 60 | //获取code码,以获取openid 61 | $code = $_GET['code']; 62 | $openid = $this->getOpenidFromMp($code); 63 | return $openid; 64 | } 65 | } 66 | 67 | /** 68 | * 通过code从工作平台获取openid机器access_token 69 | * @param string $code 微信跳转回来带上的code 70 | * @return openid 71 | */ 72 | public function GetOpenidFromMp($code) 73 | { 74 | $url = $this->__CreateOauthUrlForOpenid($code); 75 | $res = self::curlGet($url); 76 | //取出openid 77 | $data = json_decode($res,true); 78 | $this->data = $data; 79 | $openid = $data['openid']; 80 | return $openid; 81 | } 82 | 83 | /** 84 | * 构造获取open和access_toke的url地址 85 | * @param string $code,微信跳转带回的code 86 | * @return 请求的url 87 | */ 88 | private function __CreateOauthUrlForOpenid($code) 89 | { 90 | $urlObj["appid"] = $this->appid; 91 | $urlObj["secret"] = $this->appKey; 92 | $urlObj["code"] = $code; 93 | $urlObj["grant_type"] = "authorization_code"; 94 | $bizString = $this->ToUrlParams($urlObj); 95 | return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString; 96 | } 97 | 98 | /** 99 | * 构造获取code的url连接 100 | * @param string $redirectUrl 微信服务器回跳的url,需要url编码 101 | * @return 返回构造好的url 102 | */ 103 | private function __CreateOauthUrlForCode($redirectUrl) 104 | { 105 | $urlObj["appid"] = $this->appid; 106 | $urlObj["redirect_uri"] = "$redirectUrl"; 107 | $urlObj["response_type"] = "code"; 108 | $urlObj["scope"] = "snsapi_base"; 109 | $urlObj["state"] = "STATE"."#wechat_redirect"; 110 | $bizString = $this->ToUrlParams($urlObj); 111 | return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString; 112 | } 113 | 114 | /** 115 | * 拼接签名字符串 116 | * @param array $urlObj 117 | * @return 返回已经拼接好的字符串 118 | */ 119 | private function ToUrlParams($urlObj) 120 | { 121 | $buff = ""; 122 | foreach ($urlObj as $k => $v) 123 | { 124 | if($k != "sign") $buff .= $k . "=" . $v . "&"; 125 | } 126 | $buff = trim($buff, "&"); 127 | return $buff; 128 | } 129 | 130 | /** 131 | * 企业付款 132 | * @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid 133 | * @param float $totalFee 收款总费用 单位元 134 | * @param string $outTradeNo 唯一的订单号 135 | * @param string $orderName 订单名称 136 | * @param string $notifyUrl 支付结果通知url 不要有问号 137 | * @param string $timestamp 支付时间 138 | * @return string 139 | */ 140 | public function createJsBizPackage($openid, $totalFee, $outTradeNo,$trueName) 141 | { 142 | $config = array( 143 | 'mch_id' => $this->mchid, 144 | 'appid' => $this->appid, 145 | 'key' => $this->apiKey, 146 | ); 147 | $unified = array( 148 | 'mch_appid' => $config['appid'], 149 | 'mchid' => $config['mch_id'], 150 | 'nonce_str' => self::createNonceStr(), 151 | 'openid' => $openid, 152 | 'check_name'=>'FORCE_CHECK', //校验用户姓名选项。NO_CHECK:不校验真实姓名,FORCE_CHECK:强校验真实姓名 153 | 're_user_name'=>$trueName, //收款用户真实姓名(不支持给非实名用户打款) 154 | 'partner_trade_no' => $outTradeNo, 155 | 'spbill_create_ip' => '127.0.0.1', 156 | 'amount' => floatval($totalFee) * 100, //单位 转为分 157 | 'desc'=>'付款', //企业付款操作说明信息 158 | ); 159 | $unified['sign'] = self::getSign($unified, $config['key']); 160 | $responseXml = $this->curlPost('https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers', self::arrayToXml($unified)); 161 | $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA); 162 | if ($unifiedOrder === false) { 163 | die('parse xml error'); 164 | } 165 | if ($unifiedOrder->return_code != 'SUCCESS') { 166 | die($unifiedOrder->return_msg); 167 | } 168 | if ($unifiedOrder->result_code != 'SUCCESS') { 169 | die($unifiedOrder->err_code); 170 | } 171 | return true; 172 | } 173 | 174 | public static function curlGet($url = '', $options = array()) 175 | { 176 | $ch = curl_init($url); 177 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 178 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); 179 | if (!empty($options)) { 180 | curl_setopt_array($ch, $options); 181 | } 182 | //https请求 不验证证书和host 183 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 184 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 185 | $data = curl_exec($ch); 186 | curl_close($ch); 187 | return $data; 188 | } 189 | 190 | public function curlPost($url = '', $postData = '', $options = array()) 191 | { 192 | if (is_array($postData)) { 193 | $postData = http_build_query($postData); 194 | } 195 | $ch = curl_init(); 196 | curl_setopt($ch, CURLOPT_URL, $url); 197 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 198 | curl_setopt($ch, CURLOPT_POST, 1); 199 | curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); 200 | curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数 201 | if (!empty($options)) { 202 | curl_setopt_array($ch, $options); 203 | } 204 | //https请求 不验证证书和host 205 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 206 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 207 | 208 | //第一种方法,cert 与 key 分别属于两个.pem文件 209 | //默认格式为PEM,可以注释 210 | curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); 211 | curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/cert/apiclient_cert.pem'); 212 | //默认格式为PEM,可以注释 213 | curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); 214 | curl_setopt($ch,CURLOPT_SSLKEY,getcwd().'/cert/apiclient_key.pem'); 215 | //第二种方式,两个文件合成一个.pem文件 216 | // curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem'); 217 | $data = curl_exec($ch); 218 | if($data === false) 219 | { 220 | echo 'Curl error: ' . curl_error($ch);exit(); 221 | } 222 | curl_close($ch); 223 | return $data; 224 | } 225 | 226 | public static function createNonceStr($length = 16) 227 | { 228 | $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 229 | $str = ''; 230 | for ($i = 0; $i < $length; $i++) { 231 | $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 232 | } 233 | return $str; 234 | } 235 | public static function arrayToXml($arr) 236 | { 237 | $xml = ""; 238 | foreach ($arr as $key => $val) { 239 | if (is_numeric($val)) { 240 | $xml .= "<" . $key . ">" . $val . ""; 241 | } else 242 | $xml .= "<" . $key . ">"; 243 | } 244 | $xml .= ""; 245 | return $xml; 246 | } 247 | 248 | public static function getSign($params, $key) 249 | { 250 | ksort($params, SORT_STRING); 251 | $unSignParaString = self::formatQueryParaMap($params, false); 252 | $signStr = strtoupper(md5($unSignParaString . "&key=" . $key)); 253 | return $signStr; 254 | } 255 | protected static function formatQueryParaMap($paraMap, $urlEncode = false) 256 | { 257 | $buff = ""; 258 | ksort($paraMap); 259 | foreach ($paraMap as $k => $v) { 260 | if (null != $v && "null" != $v) { 261 | if ($urlEncode) { 262 | $v = urlencode($v); 263 | } 264 | $buff .= $k . "=" . $v . "&"; 265 | } 266 | } 267 | $reqPar = ''; 268 | if (strlen($buff) > 0) { 269 | $reqPar = substr($buff, 0, strlen($buff) - 1); 270 | } 271 | return $reqPar; 272 | } 273 | } 274 | ?> 275 | --------------------------------------------------------------------------------