基于H5的微信支付开发详解

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

成都创新互联-专业网站定制、快速模板网站建设、高性价比库车网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式库车网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖库车地区。费用合理售后完善,十余年实体公司更值得信赖。

一、前言

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

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

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

二、思路详解

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

#p#

三、代码讲解

本次开发环境用的是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文件: 
  2. defined('BASEPATH') OR exit('No direct script access allowed'); 
  3.  
  4. class Wxpay extends MY_Controller { 
  5.     public function __construct() { 
  6.         parent::__construct(); 
  7.         $this->load->model('wxpay_model'); 
  8.         //$this->load->model('wxpay'); 
  9.          
  10.     } 
  11.   
  12.     public function index() { 
  13.         //微信支付 
  14.         $this->smarty['wxPayUrl'] = $this->wxpay_model->retWxPayUrl(); 
  15.         $this->displayView('wxpay/index.tpl'); 
  16.     } 

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

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

 
 
  1. defined('BASEPATH') OR exit('No direct script access allowed'); 
  2.  
  3. class Wxpay_model extends CI_Model { 
  4.     public function __construct() { 
  5.         parent::__construct(); 
  6.     } 
  7.      
  8.     /** 
  9.      * 返回可以获得微信code的URL (用以获取openid) 
  10.      * @return [type] [description] 
  11.      */ 
  12.     public function retWxPayUrl() { 
  13.         $jsApi = new JsApi_handle(); 
  14.         return $jsApi->createOauthUrlForCode(); 
  15.     } 
  16.   
  17.     /** 
  18.      * 微信jsapi点击支付 
  19.      * @param  [type] $data [description] 
  20.      * @return [type]       [description] 
  21.      */ 
  22.     public function wxPayJsApi($data) { 
  23.         $jsApi = new JsApi_handle(); 
  24.         //统一下单接口所需数据 
  25.         $payData = $this->returnData($data); 
  26.         //获取code码,用以获取openid 
  27.         $code = $_GET['code']; 
  28.         $jsApi->setCode($code); 
  29.         //通过code获取openid 
  30.         $openid = $jsApi->getOpenId(); 
  31.          
  32.         $unifiedOrderResult = null; 
  33.         if ($openid != null) { 
  34.             //取得统一下单接口返回的数据 
  35.             $unifiedOrderResult = $this->getResult($payData, 'JSAPI', $openid); 
  36.             //获取订单接口状态 
  37.             $returnMessage = $this->returnMessage($unifiedOrder, 'prepay_id'); 
  38.             if ($returnMessage['resultCode']) { 
  39.                 $jsApi->setPrepayId($retuenMessage['resultField']); 
  40.                 //取得wxjsapi接口所需要的数据 
  41.                 $returnMessage['resultData'] = $jsApi->getParams(); 
  42.             }  
  43.  
  44.             return $returnMessage; 
  45.         } 
  46.     } 
  47.  
  48.     /** 
  49.      * 统一下单接口所需要的数据 
  50.      * @param  [type] $data [description] 
  51.      * @return [type]       [description] 
  52.      */ 
  53.     public function returnData($data) { 
  54.         $payData['sn'] = $data['sn']; 
  55.         $payData['body'] = $data['goods_name']; 
  56.         $payData['out_trade_no'] = $data['order_no']; 
  57.         $payData['total_fee'] = $data['fee']; 
  58.         $payData['attach'] = $data['attach']; 
  59.  
  60.         return $payData; 
  61.     } 
  62.  
  63.     /** 
  64.      * 返回统一下单接口结果 (参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1) 
  65.      * @param  [type] $payData    [description] 
  66.      * @param  [type] $trade_type [description] 
  67.      * @param  [type] $openid     [description] 
  68.      * @return [type]             [description] 
  69.      */ 
  70.     public function getResult($payData, $trade_type, $openid = null) { 
  71.         $unifiedOrder = new UnifiedOrder_handle(); 
  72.  
  73.         if ($opneid != null) { 
  74.             $unifiedOrder->setParam('openid', $openid); 
  75.         } 
  76.         $unifiedOrder->setParam('body', $payData['body']);  //商品描述 
  77.         $unifiedOrder->setParam('out_trade_no', $payData['out_trade_no']); //商户订单号 
  78.         $unifiedOrder->setParam('total_fee', $payData['total_fee']);    //总金额 
  79.         $unifiedOrder->setParam('attach', $payData['attach']);  //附加数据 
  80.         $unifiedOrder->setParam('notify_url', base_url('/Wxpay/pay_callback'));//通知地址 
  81.         $unifiedOrder->setParam('trade_type', $trade_type); //交易类型 
  82.  
  83.         //非必填参数,商户可根据实际情况选填 
  84.         //$unifiedOrder->setParam("sub_mch_id","XXXX");//子商户号 
  85.         //$unifiedOrder->setParam("device_info","XXXX");//设备号 
  86.         //$unifiedOrder->setParam("time_start","XXXX");//交易起始时间 
  87.         //$unifiedOrder->setParam("time_expire","XXXX");//交易结束时间 
  88.         //$unifiedOrder->setParam("goods_tag","XXXX");//商品标记 
  89.         //$unifiedOrder->setParam("product_id","XXXX");//商品ID 
  90.          
  91.         return $unifiedOrder->getResult(); 
  92.     } 
  93.  
  94.     /** 
  95.      * 返回微信订单状态 
  96.      */ 
  97.     public function returnMessage($unifiedOrderResult,$field){ 
  98.         $arrMessage=array("resultCode"=>0,"resultType"=>"获取错误","resultMsg"=>"该字段为空"); 
  99.         if($unifiedOrderResult==null){ 
  100.             $arrMessage["resultType"]="未获取权限"; 
  101.             $arrMessage["resultMsg"]="请重新打开页面"; 
  102.         }elseif ($unifiedOrderResult["return_code"] == "FAIL") 
  103.         { 
  104.             $arrMessage["resultType"]="网络错误"; 
  105.             $arrMessage["resultMsg"]=$unifiedOrderResult['return_msg']; 
  106.         } 
  107.         elseif($unifiedOrderResult["result_code"] == "FAIL") 
  108.         { 
  109.             $arrMessage["resultType"]="订单错误"; 
  110.             $arrMessage["resultMsg"]=$unifiedOrderResult['err_code_des']; 
  111.         } 
  112.         elseif($unifiedOrderResult[$field] != NULL) 
  113.         { 
  114.             $arrMessage["resultCode"]=1; 
  115.             $arrMessage["resultType"]="生成订单"; 
  116.             $arrMessage["resultMsg"]="OK"; 
  117.             $arrMessage["resultField"] = $unifiedOrderResult[$field]; 
  118.         } 
  119.         return $arrMessage; 
  120.     } 
  121.  
  122.     /** 
  123.      * 微信回调接口返回  验证签名并回应微信 
  124.      * @param  [type] $xml [description] 
  125.      * @return [type]      [description] 
  126.      */ 
  127.     public function wxPayNotify($xml) { 
  128.         $notify = new Wxpay_server(); 
  129.         $notify->saveData($xml); 
  130.         //验证签名,并回复微信 
  131.         //对后台通知交互时,如果微信收到商户的应答不是成功或者超时,微信认为通知失败 
  132.         //微信会通过一定的策略(如30分钟共8次),定期重新发起通知 
  133.         if ($notify->checkSign() == false) { 
  134.             $notify->setReturnParameter("return_code","FAIL");//返回状态码 
  135.             $notify->setReturnParameter("return_msg","签名失败");//返回信息 
  136.         } else { 
  137.             $notify->checkSign=TRUE; 
  138.             $notify->setReturnParameter("return_code","SUCCESS");//设置返回码 
  139.         } 
  140.  
  141.         return $notify; 
  142.     } 
  143.  
  144. /** 
  145. * JSAPI支付——H5网页端调起支付接口 
  146. */ 
  147. class JsApi_handle extends JsApi_common { 
  148.     public $code;//code码,用以获取openid 
  149.     public $openid;//用户的openid 
  150.     public $parameters;//jsapi参数,格式为json 
  151.     public $prepay_id;//使用统一支付接口得到的预支付id 
  152.     public $curl_timeout;//curl超时时间 
  153.  
  154.     function __construct() 
  155.     { 
  156.         //设置curl超时时间 
  157.         $this->curl_timeout = WxPayConf::CURL_TIMEOUT; 
  158.     } 
  159.  
  160.     /** 
  161.      * 生成获取code的URL 
  162.      * @return [type] [description] 
  163.      */ 
  164.     public function createOauthUrlForCode() { 
  165.         //重定向URL 
  166.         $redirectUrl = "http://www.itcen.cn/wxpay/confirm/".$orderId."?showwxpaytitle=1"; 
  167.         $urlParams['appid'] = WxPayConf::APPID; 
  168.         $urlParams['redirect_uri'] = $redirectUrl; 
  169.         $urlParams['response_type'] = 'code'; 
  170.         $urlParams['scope'] = 'snsapi_base'; 
  171.         $urlParams['state'] = "STATE"."#wechat_redirect"; 
  172.         //拼接字符串 
  173.         $queryString = $this->ToUrlParams($urlParams, false); 
  174.         return "https://open.weixin.qq.com/connect/oauth2/authorize?".$queryString; 
  175.     } 
  176.  
  177.     /** 
  178.      * 设置code 
  179.      * @param [type] $code [description] 
  180.      */ 
  181.     public function setCode($code) { 
  182.         $this->code = $code; 
  183.     } 
  184.  
  185.     /** 
  186.      *  作用:设置prepay_id 
  187.      */ 
  188.     public function setPrepayId($prepayId) 
  189.     { 
  190.         $this->prepay_id = $prepayId; 
  191.     } 
  192.  
  193.     /** 
  194.      *  作用:获取jsapi的参数 
  195.      */ 
  196.     public function getParams() 
  197.     { 
  198.         $jsApiObj["appId"] = WxPayConf::APPID; 
  199.         $timeStamp = time(); 
  200.         $jsApiObj["timeStamp"] = "$timeStamp"; 
  201.         $jsApiObj["nonceStr"] = $this->createNoncestr(); 
  202.         $jsApiObj["package"] = "prepay_id=$this->prepay_id"; 
  203.         $jsApiObj["signType"] = "MD5"; 
  204.         $jsApiObj["paySign"] = $this->getSign($jsApiObj); 
  205.         $this->parameters = json_encode($jsApiObj); 
  206.  
  207.         return $this->parameters; 
  208.     } 
  209.  
  210.     /** 
  211.      * 通过curl 向微信提交code 用以获取openid 
  212.      * @return [type] [description] 
  213.      */ 
  214.     public function getOpenId() { 
  215.         //创建openid 的链接 
  216.         $url = $this->createOauthUrlForOpenid(); 
  217.         //初始化 
  218.         $ch = curl_init(); 
  219.         curl_setopt($ch, CURL_TIMEOUT, $this->curl_timeout); 
  220.         curl_setopt($ch, CURL_URL, $url); 
  221.         curl_setopt($ch, CURL_SSL_VERIFYPEER, FALSE); 
  222.         curl_setopt($ch, CURL_SSL_VERIFYHOST, FALSE); 
  223.         curl_setopt($ch, CURL_HEADER, FALSE); 
  224.         curl_setopt($ch, CURL_RETURNTRANSFER, TRUE); 
  225.         //执行curl 
  226.         $res = curl_exec($ch); 
  227.         curl_close($ch); 
  228.         //取出openid 
  229.         $data = json_decode($res); 
  230.         if (isset($data['openid'])) { 
  231.             $this->openid = $data['openid']; 
  232.         } else { 
  233.             return null; 
  234.         } 
  235.  
  236.         return $this->openid; 
  237.  
  238.     } 
  239.  
  240.     /** 
  241.      * 生成可以获取openid 的URL 
  242.      * @return [type] [description] 
  243.      */ 
  244.     public function createOauthUrlForOpenid() { 
  245.         $urlParams['appid'] = WxPayConf::APPID; 
  246.         $urlParams['secret'] = WxPayConf::APPSECRET; 
  247.         $urlParams['code'] = $this->code; 
  248.         $urlParams['grant_type'] = "authorization_code"; 
  249.         $queryString = $this->ToUrlParams($urlParams, false); 
  250.         return "https://api.weixin.qq.com/sns/oauth2/access_token?".$queryString; 
  251.     } 
  252.  
  253. /** 
  254.  * 统一下单接口类 
  255.  */ 
  256. class UnifiedOrder_handle extends Wxpay_client_handle { 
  257.     public function __construct() { 
  258.         //设置接口链接 
  259.         $this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; 
  260.         //设置curl超时时间 
  261.         $this->curl_timeout = WxPayConf::CURL_TIMEOUT; 
  262.     } 
  263.  
  264.  
  265. /** 
  266.  * 响应型接口基类 
  267.  */ 
  268. class Wxpay_server_handle extends JsApi_common{ 
  269.     public $data; //接收到的数据,类型为关联数组 
  270.     public $returnParams;   //返回参数,类型为关联数组 
  271.  
  272.     /** 
  273.      * 将微信请求的xml转换成关联数组 
  274.      * @param  [type] $xml [description] 
  275.      * @return [type]      [description] 
  276.      */ 
  277.     public function saveData($xml) { 
  278.         $this->data = $this->xmlToArray($xml);  
  279.     } 
  280.  
  281.  
  282.     /** 
  283.      * 验证签名 
  284.      * @return [type] [description] 
  285.      */ 
  286.     public function checkSign() { 
  287.         $tmpData = $this->data; 
  288.         unset($temData['sign']); 
  289.         $sign = $this->getSign($tmpData); 
  290.         if ($this->data['sign'] == $sign) { 
  291.             return true; 
  292.         } 
  293.         return false; 
  294.     } 
  295.  
  296.  
  297.     /** 
  298.      * 设置返回微信的xml数据 
  299.      */ 
  300.     function setReturnParameter($parameter, $parameterValue) 
  301.     { 
  302.         $this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue); 
  303.     } 
  304.  
  305.     /** 
  306.      * 将xml数据返回微信 
  307.      */ 
  308.     function returnXml() 
  309.     { 
  310.         $returnXml = $this->createXml(); 
  311.         return $returnXml; 
  312.     } 
  313.  
  314.  
  315. /** 
  316.  * 请求型接口的基类 
  317.  */ 
  318. class Wxpay_client_handle extends JsApi_common{ 
  319.     public $params; //请求参数,类型为关联数组 
  320.     public $response; //微信返回的响应 
  321.     public $result; //返回参数,类型类关联数组 
  322.     public $url; //接口链接 
  323.     public $curl_timeout; //curl超时时间 
  324.  
  325.     /** 
  326.      * 设置请求参数 
  327.      * @param [type] $param      [description] 
  328.      * @param [type] $paramValue [description] 
  329.      */ 
  330.     public function setParam($param, $paramValue) { 
  331.         $this->params[$this->tirmString($param)] = $this->trimString($paramValue); 
  332.     } 
  333.  
  334.     /** 
  335.      * 获取结果,默认不使用证书 
  336.      * @return [type] [description] 
  337.      */ 
  338.     public function getResult() { 
  339.         $this->postxml();  
  340.         $this->result = $this->xmlToArray($this->response); 
  341.  
  342.         return $this->result; 
  343.     } 
  344.  
  345.     /** 
  346.      * post请求xml 
  347.      * @return [type] [description] 
  348.      */ 
  349.     public function postxml() { 
  350.         $xml = $this->createXml(); 
  351.         $this->response = $this->postXmlCurl($xml, $this->curl, $this->curl_timeout); 
  352.  
  353.         return $this->response; 
  354.     } 
  355.  
  356.     public function createXml() { 
  357.         $this->params['appid'] = WxPayConf::APPID; //公众号ID 
  358.         $this->params['mch_id'] = WxPayConf::MCHID; //商户号 
  359.         $this->params['nonce_str'] = $this->createNoncestr();   //随机字符串 
  360.         $this->params['sign'] = $this->getSign($this->params);  //签名 
  361.          
  362.         return $this->arrayToXml($this->params);  
  363.     } 
  364.  
  365.      
  366.  
  367.  
  368. /** 
  369.  * 所有接口的基类 
  370.  */ 
  371. class JsApi_common { 
  372.     function __construct() { 
  373.  
  374.     } 
  375.  
  376.     public function trimString($value) { 
  377.         $ret = null; 
  378.         if (null != $value) { 
  379.             $ret = trim($value); 
  380.             if (strlen($ret) == 0) { 
  381.                 $ret = null; 
  382.             } 
  383.         }  
  384.         return $ret; 
  385.     } 
  386.  
  387.     /** 
  388.      * 产生随机字符串,不长于32位 
  389.      * @param  integer $length [description] 
  390.      * @return [type]          [description] 
  391.      */ 
  392.     public function createNoncestr($length = 32) { 
  393.         $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; 
  394.         $str = ''; 
  395.         for ($i = 0; $i < $length; $i++) { 
  396.             $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); 
  397.         } 
  398.  
  399.         return $str; 
  400.     } 
  401.  
  402.     /** 
  403.      * 格式化参数 拼接字符串,签名过程需要使用 
  404.      * @param [type] $urlParams     [description] 
  405.      * @param [type] $needUrlencode [description] 
  406.      */ 
  407.     public function ToUrlParams($urlParams, $needUrlencode) { 
  408.         $buff = ""; 
  409.         ksort($urlParams); 
  410.  
  411.         foreach ($urlParams as $k => $v) { 
  412.             if($needUrlencode) $v = urlencode($v); 
  413.             $buff .= $k .'='. $v .'&'; 
  414.         } 
  415.  
  416.         $reqString = ''; 
  417.         if (strlen($buff) > 0) { 
  418.             $reqString = substr($buff, 0, strlen($buff) - 1); 
  419.         } 
  420.  
  421.         return $reqString; 
  422.     } 
  423.  
  424.     /** 
  425.      * 生成签名 
  426.      * @param  [type] $params [description] 
  427.      * @return [type]         [description] 
  428.      */ 
  429.     public function getSign($obj) { 
  430.         foreach ($obj as $k => $v) { 
  431.             $params[$k] = $v; 
  432.         } 
  433.         //签名步骤一:按字典序排序参数 
  434.         ksort($params); 
  435.         $str = $this->ToUrlParams($params, false);   
  436.         //签名步骤二:在$str后加入key 
  437.         $str = $str."$key=".WxPayConf::KEY; 
  438.         //签名步骤三:md5加密 
  439.         $str = md5($str); 
  440.         //签名步骤四:所有字符转为大写 
  441.         $result = strtoupper($str); 
  442.  
  443.         return $result; 
  444.     } 
  445.  
  446.     /** 
  447.      * array转xml 
  448.      * @param  [type] $arr [description] 
  449.      * @return [type]      [description] 
  450.      */ 
  451.     public function arrayToXml($arr) { 
  452.         $xml = ""; 
  453.         foreach ($arr as $k => $v) { 
  454.             if (is_numeric($val)) { 
  455.                 $xml .= "<".$key.">".$key.""; 
  456.             } else { 
  457.                 $xml .= "<".$key.">"; 
  458.             } 
  459.         } 
  460.         $xml .= ""; 
  461.    

    文章名称:基于H5的微信支付开发详解
    文章来源:http://www.shufengxianlan.com/qtweb/news3/528353.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联