一对一免费咨询: 13913005726 025-66045668

这次总结一下用户在微信内打开网页时,可以调用微信支 付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能。当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现 代码可供参考,有的朋友直接看文档就可以自己实现此支付接口的开发了。

一、前言

为何我还写一篇微信支付接口的博文呢?第一,我们 必须知道,所谓的工作经验很多都是靠总结出来的,你只有总结了更多知识,积累了更多经验,你才能在该行业中脱颖而出,我个人觉得如今的招聘,很多都需要工 作经验(1年、3年、5年....),其实,工作时间的长久不能衡量一个人技术水平的高低,有的人一年的工作经验能拿3年工作经验的程序猿的工资,有的3 年工作经验的却有可能比别人只有一年工作经验的还低,所以说,总结才能让自己的知识体系,经验深黄山网站建设公司度更牛逼更稳固(虽然写一篇博文挺花费时间的);第二,写 博文分享给大家还是挺有成就感的,首先是能让新手从我分享的博文中能学到东西,并且能快速将博文所讲解的技术运用到实际中来,所以我写的博文基本上能让新 人快速读懂并且容易理解,另外,技术大神的话,看到博文有讲解的不对之处,还可以指出,并且可以交流,何乐而不为呢,我们需要的就是分享和交流。

扯远了,直接进入该主题的详解。

现在的微信支付方式有N种,看下图,有刷卡支付、 公众号支付、扫码支付和APP支付,另外还有支付工具的开发,本博文选择的是公众号支付借口而开发进行讲解,其他几种支付接口开发基本上思路都是一样的, 只要你能看懂我这博文所讲解的基本思路,你基本上也能独自开发其他几个支付接口。

二、思路详解

我们可以拿微信支付接口文档里的业务流程时序图看 看,如下图,基本思路是这样子:首先在后台生成一个链接,展示给用户让用户点击(例如页面上有微信支付的按钮),用户点击按钮后,网站后台会根据订单的相 关信息生成一个支付订单,此时会调用统一下单接口,对微信支付系统发起请求,而微信支付系统受到请求后,会根据请求过来的数据,生成一个 预支付交易会话标识(prepay_id,就是通过这个来识别该订单的),我们的网站收到微信支付系统的响应后,会得到prepay_id,然后通过自己 构造微信支付所需要的参数,接着将支付所需参数返回给客户端,用户此时可能会有一个订单信息页,会有一个按钮,点击支付,此时会调用JSAPI接口对微信 支付系统发起 请求支付,微信支付系统检查了请求的相关合法性之后,就会提示输入密码,用户此时输入密码确认,微信支付系统会对其进行验证,通过的话会返回支付结果,然 后微信跳转会H5页面,这其中有一步是异步通知网站支付结果,我们网站需要对此进行处理(比如说异步支付结果通过后,需要更新数据表或者订单信息,例如标 志用户已支付该订单了,同时也需要更新订单日志,防止用户重复提交订单)。

三、代码讲解

本次开发环境用的是php5.6 + MySQL + Redis + Linux 六安网站建设公司 + Apache,所选用的框架的CI框架(这些环境不一定需要和我的一致,框架也可以自己选择,反正自己稍微修改下代码就能移植过去了)。

微信支付接口的开发代码我已经提前写好了,在这里我对其进行分析讲解,方便大家能轻松理解,当然,假如你有一定的基础,直接看代码就能理清所有流程了,并且我的代码基本上都写上了注释(对于新手来说,这一点比微信文档所提供的代码好一点)。

1、构造一个链接展示给用户

这里我们提前需要知道一个点,那就是请求统一下单接口需要微信用户的openid(详情可看这https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1),而获取openid需要先获取code(详情可看这微信登录接口),所以我们需要构造一个获取code的URL:

  1. Wxpay.php文件:

    ?php

  2. defined('BASEPATH')ORexit('Nodirectscriptaccessallowed');

  3. classWxpayextendsMY_Controller{

    publicfunction__construct(){

  4. parent::__construct();

    $this-load-model('wxpay_model');

  5. //$this-load-model('wxpay');

  6. }

  7. publicfunctionindex(){

    //微信支付

  8. $this-smarty['wxPayUrl']=$this-wxpay_model-retWxPayUrl();

    $this-displayView('wxpay/index.tpl');

  9. }

    }

在这先看看model里所写的几个类:model里有几个类:微信支付类、统一下单接口类、响应型接口基类、请求型接口基类、所有接口基类、配置类。为何要分那么多类而不在一个类里实现所有的方法的,因为,这样看起来代码逻辑清晰,哪个类该干嘛就干嘛。

这里我直接附上model的代码了,里面基本上每一个类每一个方法甚至每一行代码都会有解释的了,这里我就不对其展开一句句分析了:

  1. ?php

    defined('BASEPATH')ORexit('Nodirectscriptaccessallowed');

  2. classWxpay_modelextendsCI_Model{

  3. publicfunction__construct(){

    parent::__construct();

  4. }

  5. /**

    *返回可以获得微信code的URL(用以获取openid)

  6. *@return[type][description]

    */

  7. publicfunctionretWxPayUrl(){

    $jsApi=newJsApi_handle();

  8. return$jsApi-createOauthUrlForCode();

    }

  9. /**

  10. *微信jsapi点击支付

    *@param[type]$data[description]

  11. *@return[type][description]

    */

  12. publicfunctionwxPayJsApi($data){

    $jsApi=newJsApi_handle();

  13. //统一下单接口所需数据

    $payData=$this-returnData($data);

  14. //获取code码,用以获取openid

    $code=$_GET['code'];

  15. $jsApi-setCode($code);

    //通过code获取openid

  16. $openid=$jsApi-getOpenId();

  17. $unifiedOrderResult=null;

    if($openid!=null){

  18. //取得统一下单接口返回的数据

    $unifiedOrderResult=$this-getResult($payData,'JSAPI',$openid);

  19. //获取订单接口状态

    $returnMessage=$this-returnMessage($unifiedOrder,'prepay_id');

  20. if($returnMessage['resultCode']){

    $jsApi-setPrepayId($retuenMessage['resultField']);

  21. //取得wxjsapi接口所需要的数据

    $returnMessage['resultData']=$jsApi-getParams();

  22. }

  23. return$returnMessage;

    }

  24. }

  25. /**

    *统一下单接口所需要的数据

  26. *@param[type]$data[description]

    *@return[type][description]

  27. */

    publicfunctionreturnData($data){

  28. $payData['sn']=$data['sn'];

    $payData['body']=$data['goods_name'];

  29. $payData['out_trade_no']=$data['order_no'];

    $payData['total_fee']=$data['fee'];

  30. $payData['attach']=$data['attach'];

  31. return$payData;

    }

  32. /**

  33. *返回统一下单接口结果(参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)

    *@param[type]$payData[description]

  34. *@param[type]$trade_type[description]

    *@param[type]$openid[description]

  35. *@return[type][description]

    */

  36. publicfunctiongetResult($payData,$trade_type,$openid=null){

    $unifiedOrder=newUnifiedOrder_handle();

  37. if($opneid!=null){

  38. $unifiedOrder-setParam('openid',$openid);

    }

  39. $unifiedOrder-setParam('body',$payData['body']);//商品描述

    $unifiedOrder-setParam('out_trade_no',$payData['out_trade_no']);//商户订单号

  40. $unifiedOrder-setParam('total_fee',$payData['total_fee']);//总金额

    $unifiedOrder-setParam('attach',$payData['attach']);//附加数据

  41. $unifiedOrder-setParam('notify_url',base_url('/Wxpay/pay_callback'));//通知地址

    $unifiedOrder-setParam('trade_type',$trade_type);//交易类型

  42. //非必填参数,商户可根据实际情况选填

  43. //$unifiedOrder-setParam("sub_mch_id","XXXX");//子商户号

    //$unifiedOrder-setParam("device_info","XXXX");//设备号

  44. //$unifiedOrder-setParam("time_start","XXXX");//交易起始时间

    //$unifiedOrder-setParam("time_expire","XXXX");//交易结束时间

  45. //$unifiedOrder-setParam("goods_tag","XXXX");//商品标记

    /网站建设需求方案/$unifiedOrder-setParam("product_id","XXXX");//商品ID

  46. return$unifiedOrder-getResult();

  47. }

  48. /**

    *返回微信订单状态

  49. */

    publicfunctionreturnMessage($unifiedOrderResult,$field){

  50. $arrMessage=array("resultCode"=0,"resultType"="获取错误","resultMsg"="该字段为空");

    if($unifiedOrderResult==null){

  51. $arrMessage["resultType"]="未获取权限";

    $arrMessage["resultMsg"]="请重新打开页面";

  52. }elseif($unifiedOrderResult["return_code"]=="FAIL")

    {

  53. $arrMessage["resultType"]="网络错误";

    $arrMessage["resultMsg"]=$unifiedOrderResult['return_msg'];

  54. }

    elseif($unifiedOrderResult["result_code"]=="FAIL")

  55. {

    $arrMessage["resultType"]="订单错误";

  56. $arrMessage["resultMsg"]=$unifiedOrderResult['err_code_des'];

    }

  57. elseif($unifiedOrderResult[$field]!=NULL)

    {

  58. $arrMessage["resultCode"]=1;

    $arrMessage["resultType"]="生成订单";

  59. $arrMessage["resultMsg"]="OK";

    $arrMessage["resultField"]=$unifiedOrderResult[$field];

  60. }

    return$arrMessage;

  61. }

  62. /**

    *微信回调接口返回验证签名并回应微信

  63. *@param[type]$xml[description]

    *@return[type][description]

  64. */

    publicfunctionwxPayNotify($xml){

  65. $notify=newWxpay_server();

    $notify-saveData($xml);

  66. //验证签名,并回复微信

    //对后台通知交互时,如果微信收到商户的应答不是成功或者超时,微信认为通知失败

  67. //微信会通过一定的策略(如30分钟共8次),定期重新发起通知

    if($notify-checkSign()==false){

  68. $notify-setReturnParameter("return_code","FAIL");//返回状态码

    $notify-setReturnParameter("return_msg","签名失败");//返回信息

  69. }else{

    $notify-checkSign=TRUE;

  70. $notify-setReturnParameter("return_code","SUCCESS");//设置返回码

    }

  71. return$notify;

  72. }

    }

  73. /**

  74. *JSAPI支付H5网页端调起支付接口

    */

  75. classJsApi_handleextendsJsApi_common{

    public$code;//code码,用以获取openid

  76. public$openid;//用户的openid

    public$parameters;//jsapi参数,格式为json

  77. public$prepay_id;//使用统一支付接口得到的预支付id

    public$curl_timeout;//curl超时时间

  78. function__construct()

  79. {

    //设置curl超时时间

  80. $this-curl_timeout=WxPayConf::CURL_TIMEOUT;

    }

  81. /**

  82. *生成获取code的URL

    *@return[type][description]

  83. */

    publicfunctioncreateOauthUrlForCode(){

  84. //重定向URL

    $redirectUrl="http://www.itcen.cn/wxpay/confirm/".$orderId."?showwxpaytitle=1";

  85. $urlParams['appid']=WxPayConf::APPID;

    $urlParams['redirect_uri']=$redirectUrl;

  86. $urlParams['response_type']='code';

    $urlParams['scope']='snsapi_base';

  87. $urlParams['state']="STATE"."#wechat_redirect";

    //拼接字符串

  88. $queryString=$this-ToUrlParams($urlParams,false);

    return"https://open.weixin.qq.com/connect/oauth2/authorize?".$queryString;

  89. }

  90. /**

    *设置code

  91. *@param[type]$code[description]

    */

  92. publicfunctionsetCode($code){

    $this-code=$code;

  93. }

  94. /**

    *作用:设置prepay_id

  95. */

    publicfunctionsetPrepayId($prepayId)

  96. {

    $this-prepay_id=$prepayId;

  97. }

  98. /**

    *作用:获取jsapi的参数

  99. */

    publicfunctiongetParams()

  100. {

    $jsApiObj["appId"]=WxPayConf::APPID;

  101. $timeStamp=time();

    $jsApiObj["timeStamp"]="$timeStamp";

  102. $jsApiObj["nonceStr"]=$this-createNoncestr();

    $jsApiObj["package"]="prepay_id=$this-prepay_id";

  103. $jsApiObj["signType"]="MD5";

    $jsApiObj["paySign"]=$this-getSign($jsApiObj);

  104. $this-parameters=json_encode($jsApiObj);

  105. return$this-parameters;

    }

  106. /**

  107. *通过curl向微信提交code用以获取openid

    *@return[type][description]

  108. */

    publicfunctiongetOpenId(){

  109. //创建openid的链接

    $url=$this-createOauthUrlForOpenid();

  110. //初始化

    $ch=curl_init();

  111. curl_setopt($ch,CURL_TIMEOUT,$this-curl_timeout);

    curl_setopt($ch,CURL_URL,$url);

  112. curl_setopt($ch,CURL_SSL_VERIFYPEER,FALSE);

    curl_setopt($ch,CURL_SSL_VERIFYHOST,FALSE);

  113. curl_setopt($ch,CURL_HEADER,FALSE);

    curl_setopt($ch,CURL_RETURNTRANSFER,TRUE);

  114. //执行curl

    $res=curl_exec($ch);

  115. curl_close($ch);

    //取出openid

  116. $data=json_decode($res);

    if(isset($data['openid'])){

  117. $this-openid=$data['openid'];

    }else{

  118. returnnull;

    }

  119. return$this-openid;

  120. }

  121. /**

  122. *生成可以获取openid的URL

    *@return[type][description]

  123. */

    publicfunctioncreateOauthUrlForOpenid(){

  124. $urlParams['appid']=WxPayConf::APPID;

    $urlParams['secret']=WxPayConf::APPSECRET;

  125. $urlParams['code']=$this-code;

    $urlParams['grant_type']="authorization_code";

  126. $queryString=$this-ToUrlParams($urlParams,false);

    return"https://api.weixin.qq.com/sns/oauth2/access_token?".$queryString;

  127. }

    }

  128. /**

  129. *统一下单接口类

    */

  130. classUnifiedOrder_handleextendsWxpay_client_handle{

    publicfunction__construct(){

  131. //设置接口链接

    $this-url="https://api.mch.weixin.qq.com/pay/unifiedorder";

  132. //设置curl超时时间

    $this-curl_timeout=WxPayConf::CURL_TIMEOUT;

  133. }

  134. }

  135. /**

    *响应型接口基类

  136. */

    classWxpay_server_handleextendsJsApi_common{

  137. public$data;//接收到的数据,类型为关联数组

    public$returnParams;//返回参数,类型为关联数组

  138. /**

  139. *将微信请求的xml转换成关联数组

    *@param[type]$xml[description]

  140. *@return[type][description]

    */

  141. publicfunctionsaveData($xml){

    $this-data=$this-xmlToArray($xml);

  142. }

  143. /**

  144. *验证签名

    *@return[type][description]

  145. */

    publicfunctioncheckSign(){

  146. $tmpData=$this-data;

    unset($temData['sign']);

  147. $sign=$this-getSign($tmpData);

    if($this-data['sign']==$sign){

  148. returntrue;

    }

  149. returnfalse;

    }

  150. /**

    *设置返回微信的xml数据

  151. */

    functionsetReturnParameter($parameter,$parameterValue)

  152. {

    $this-returnParameters[$this-trimString($parameter)]=$this-trimString($parameterValue);

  153. }

  154. /**

    *将xml数据返回微信

  155. */

    functionreturnXml()

  156. {

    $returnXml=$this-createXml();

  157. return$returnXml;

    }

  158. }

  159. /**

  160. *请求型接口的基类

    */

  161. classWxpay_client_handleextendsJsApi_common{

    public$params;//请求参数,类型为关联数组

  162. public$response;//微信返回的响应

    public$result;//返回参数,类型类关联数组

  163. public$url;//接口链接

    public$curl_timeout;//curl超时时间

  164. /**

  165. *设置请求参数

    *@param[type]$param[description]

  166. *@param[type]$paramValue[description]

    */

  167. publicfunctionsetParam($param,$paramValue){

    $this-params[$this-tirmString($param)]=$this-trimString($paramValue);

  168. }

  169. /**

    *获取结果,默认不使用证书

  170. *@return[type][description]

    */

  171. publicfunctiongetResult(){

    $this-postxml();

  172. $this-result=$this-xmlToArray($this-response);

  173. return$this-result;

    }

  174. /**

  175. *post请求xml

    *@return[type][description]

  176. */

    publicfunctionpostxml(){

  177. $xml=$this-createXml();

    $this-response=$this-postXmlCurl($xml,$this-curl,$this-curl_timeout);

  178. return$this-response;

  179. }

  180. publicfunctioncreateXml(){

    $this-params['appid']=WxPayConf::APPID;//公众号ID

  181. $this-params['mch_id']=WxPayConf::MCHID;//商户号

    $this-params['nonce_str']=$this-createNoncestr();//随机字符串

  182. $this-params['sign']=$this-getSign($this-params);//签名

  183. return$this-arrayToXml($this-params);

    }

  184. }

  185. /**

  186. *所有接口的基类

    */

  187. classJsApi_common{

    function__construct(){

  188. }

  189. publicfunctiontrimString($value){

  190. $ret=null;

    if(null!=$value){

  191. $ret=trim($value);

    if(strlen($ret)==0){

  192. $ret=null;

    }

  193. }

    return$ret;

  194. }

  195. /**

    *产生随机字符串,不长于32位

  196. *@paraminteger$length[description]

    *@return[type][description]

  197. */

    publicfunctioncreateNoncestr($length=32){

  198. $chars="abcdefghijklmnopqrstuvwxyz0123456789";

    $str='';

  199. for($i=0;$i$length;$i++){

    $str.=substr($chars,mt_rand(0,strlen($chars)-1),1);

  200. }

  201. return$str;

    }

  202. /**

  203. *格式化参数拼接字符串,签名过程需要使用

    *@param[type]$urlParams[description]

  204. *@param[type]$needUrlencode[description]

    */

  205. publicfunctionToUrlParams($urlParams,$needUrlencode){

    $buff="";

  206. ksort($urlParams);

  207. foreach($urlParamsas$k=$v){

    if($needUrlencode)$v=urlencode($v);

  208. $buff.=$k.'='.$v.'&';

    }

  209. $reqString='';

  210. if(strlen($buff)0){

    $reqString=substr($buff,0,strlen($buff)-1);

  211. }

  212. return$reqString;

    }

  213. /**

  214. *生成签名

    *@param[type]$params[description]

  215. *@return[type][description]

    */

  216. publicfunctiongetSign($obj){

    foreach($objas$k=$v){

  217. $params[$k]=$v;

    }

  218. //签名步骤一:按字典序排序参数

    ksort($params);

  219. $str=$this-ToUrlParams($params,false);

    //签名步骤二:在$str后加入key

  220. $str=$str."$key=".WxPayConf::KEY;

    //签名步骤三:md5加密

  221. $str=md5($str);

    //签名步骤四:所有字符转为大写

  222. $result=strtoupper($str);

  223. return$result;

    }

  224. /**

  225. *array转xml

    *@param[type]$arr[description]

  226. *@return[type][description]

    */

  227. publicfunctionarrayToXml($arr){

    $xml="xml";

  228. foreach($arras$k=$v){

    if(is_numeric($val)){

  229. $xml.="".$key."".$key."/".$key."";

    }else{

  230. $xml.="".$key."![CDATA[".$val."]]/".$key."";

    }

  231. }

    $xml.="/xml";

  232. return$xml;

    }

  233. /**

  234. *将xml转为array

    *@param[type]$xml[description]

  235. *@return[type][description]

    */

  236. publicfunctionxmlToArray($xml){

    $arr=json_decode(json_encode(simplexml_load_string($xml,'SinpleXMLElement',LIBXML_NOCDATA)),true);

  237. return$arr;

  238. }

  239. /**

    *以post方式提交xml到对应的接口

  240. *@param[type]$xml[description]

    *@param[type]$url[description]

  241. *@paraminteger$second[description]

    *@return[type][description]

  242. */

    publicfunctionpostXmlCurl($xml,$url,$second=30){

  243. //初始化curl

    $ch=curl_init();

  244. //设置超时

    curl_setopt($ch,CURL_TIMEOUT,$second);

  245. curl_setopt($ch,CURL_URL,$url);

    //这里设置代理,如果有的话

  246. //curl_setopt($ch,CURLOPT_PROXY,'8.8.8.8');

    //curl_setopt($ch,CURLOPT_PROXYPORT,8080);

  247. curl_setopt($ch,CURL_SSL_VERIFYHOST,FALSE);

    curl_setopt($ch,CURL_SSL_VERIFYPEER,FALSE);

  248. //设置header

    curl_setopt($ch,CURL_HEADER,FALSE);

  249. //要求结果为字符串且输出到屏幕上

    curl_setopt($ch,CURL_RETURNTRANSFER,TRUE);

  250. //以post方式提交

    curl_setopt($ch,CURL_POST,TRUE);

  251. curl_setopt($ch,CURL_POSTFIELDS,$xml);

    //执行curl

  252. $res=curl_exec($ch);

  253. if($res){

    curl_close($ch);

  254. return$res;

    }else{

  255. $error=curl_errno($ch);

    echo"curl出错,错误码:$error"."br";

  256. echo"ahref='http://curl.haxx.se/libcurl/c/libcurl-errors.html'错误原因查询/a/br";

    curl_close($ch);

  257. returnfalse;

    }

  258. }

    }

  259. /**

  260. *配置类

    */

  261. classWxPayConf{

    //微信公众号身份的唯一标识。

  262. constAPPID='wx654a22c6423213b7';

    //受理商ID,身份标识

  263. constMCHID='10043241';

    constMCHNAME='KellyCen的博客';

  264. //商户支付密钥Key。

  265. constKEY='0000000000000000000000000000000';

    //JSAPI接口中获取openid

  266. constAPPSECRET='000000000000000000000000000';

  267. //证书路径,注意应该填写绝对路径

    constSSLCERT_PATH='/home/WxPayCacert/apiclient_cert.pem';

  268. constSSLKEY_PATH='/home/WxPayCacert/apiclient_key.pem';

    constSSLCA_PATH='/home/WxPayCacert/rootca.pem';

  269. //本例程通过curl使用HTTPPOST方法,此处可修改其超时时间,默认为30秒

  270. constCURL_TIMEOUT=30;

    }

  271. Wxpay_model.php

获取到code的URL后,将其分配到页面去,让用户去点击,用户进行点击后,就会从微信服务器获取到code,然后回调到redirect_uri所指的地址去。

企业官网建设


 


 南京牧狼文化传媒有限公司简介:


      牧狼传媒,牧者之心,狼者之性,以牧之谦卑宽容之心待人,以狼之团结无畏之性做事!


  公司注册资金100万,主营众筹全案服务、网站营销全案服务、网站建设、微信小程序开发、电商网店设计、H5页面设计、腾讯社交广告投放以及电商营销推广全案等相关业务,致力于为客户提供更有价值的服务,创造让用户满意的效果!


  为百度官方及其大客户、苏宁易购、金山WPS秀堂、美的、创维家电、新东方在线、伊莱克斯、宝丽莱等国内国外知名品牌服务过,服务经验丰富!同时,公司也是南京电子商务协会会员单位、猪八戒网官方认证签约服务商、江苏八戒服务网联盟、南京浦口文化产业联合会会员单位,可以为您提供更好的服务!


  主营项目:众筹全案服务、网站营销全案服务、网站建设、微信小程序开发、电商网店设计、H5页面设计、腾讯社交广告投放、竞价托管、网站优化、电商代运营等


  合作客户:百度、苏宁易购、饿了么、美的、创维家电、新东方在线、宝丽莱、金山WPS秀堂、伊莱克斯


  资质荣誉:百度商业服务市场2017年度最佳图片服务商、南京电子商务协会会员单位、猪八戒网官方认证签约服务商、江苏八戒服务网联盟、南京浦口文化产业联合会会员单位、八戒通TOP服务商、"易拍即合杯"H5创意大赛"三等奖"。



致力于为客户创造更多价值
13913005726 025-66045668
需求提交
电话咨询
在线咨询