微信支付之公众号支付

  1. <?php
  2. header('Content-type:text/html; Charset=utf-8');
  3. $mchid = 'xxxxx'; //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
  4. $appid = 'xxxxx'; //微信支付申请对应的公众号的APPID
  5. $appKey = 'xxxxx'; //微信支付申请对应的公众号的APP Key
  6. $apiKey = 'xxxxx'; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
  7. //①、获取用户openid
  8. $wxPay = new WxpayService($mchid,$appid,$appKey,$apiKey);
  9. $openId = $wxPay->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. <html>
  21. <head>
  22. <meta charset="utf-8" />
  23. <meta name="viewport" content="width=device-width, initial-scale=1"/>
  24. <title>微信支付样例-支付</title>
  25. <script type="text/javascript">
  26. //调用微信JS api 支付
  27. function jsApiCall()
  28. {
  29. WeixinJSBridge.invoke(
  30. 'getBrandWCPayRequest',
  31. <?php echo $jsApiParameters; ?>,
  32. function(res){
  33. WeixinJSBridge.log(res.err_msg);
  34. if(res.err_msg=='get_brand_wcpay_request:ok'){
  35. alert('支付成功!');
  36. }else{
  37. alert('支付失败:'+res.err_code+res.err_desc+res.err_msg);
  38. }
  39. }
  40. );
  41. }
  42. function callpay()
  43. {
  44. if (typeof WeixinJSBridge == "undefined"){
  45. if( document.addEventListener ){
  46. document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
  47. }else if (document.attachEvent){
  48. document.attachEvent('WeixinJSBridgeReady', jsApiCall);
  49. document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
  50. }
  51. }else{
  52. jsApiCall();
  53. }
  54. }
  55. </script>
  56. </head>
  57. <body>
  58. <br/>
  59. <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px"><?php echo $payAmount?></span></b></font><br/><br/>
  60. <div align="center">
  61. <button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" onclick="callpay()" >立即支付</button>
  62. </div>
  63. </body>
  64. </html>
  65. <?php
  66. class WxpayService
  67. {
  68. protected $mchid;
  69. protected $appid;
  70. protected $appKey;
  71. protected $apiKey;
  72. public $data = null;
  73. public function __construct($mchid, $appid, $appKey,$key)
  74. {
  75. $this->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' => intval($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 = "<xml>";
  267. foreach ($arr as $key => $val) {
  268. if (is_numeric($val)) {
  269. $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
  270. } else
  271. $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
  272. }
  273. $xml .= "</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. }


评论 0

发表评论

Top