微信JSAPI支付指的是通过微信内部浏览器进行的一种支付方式。

一、准备工作

1、已经注册好的微信商户号(pay.weixin.qq.com/index.php)

2、认证成功的微信服务号(mp.weixin.qq.com/)


二、JSAPI支付流程

支付流程看下图:

微信内网页支付时序图

商户系统和微信支付系统主要交互:

1、商户server调用统一下单接口请求订单,api参见公共api【统一下单API】

2、商户server可通过【JSAPI调起支付API】调起微信支付,发起支付请求。

3、商户server接收支付通知,api参见公共api【支付结果通知API】

三、开发

1、生成code并跳转(Getcode.php)

  1. private $url; //跳转链接需要转成Unicode格式  
  2. private $appid; //appid  
  3. private $state = 'demo'//自行定义状态码  
  4. //跳转到$url地址并获取openid 
  5. public function getopenids($order_number){  
  6.     header('Location:https://open.weixin.qq.com/connect/oauth2/authorize?appid='.$this->appid.'&redirect_uri='.$this->url .'&response_type=code&scope=snsapi_base&state='.$this->state.'#wechat_redirect');  
  7.     }  
  8. }  

2、获取openid(WxPayServer.php)

  1. public function getJsapiOpenid()  
  2. {  
  3.     header("Access-Control-Allow-Origin: *");  
  4.     if(isset($_GET['code'])){  
  5.         //拿到code码获取openid  
  6.         $weixin = $this->file_get_content("https://api.weixin.qq.com/sns/oauth2/access_token?appid=".$appid."&secret=".$appsecret."&code=" . $_GET['code'] . "&state=sun&grant_type=authorization_code");  
  7.         $jsondecode = json_decode($weixin);  
  8.         $array = get_object_vars($jsondecode);    
  9.         $token = $array['access_token'];  
  10.         $data['openid'] = $array['openid'];  
  11.         $this->assign('data',$data);  
  12.         return $this->fetch();  
  13.     }else{  
  14.         //未拿到code码跳转到第一步类  
  15. $getCode = new Getcode(); 
  16.          $getCode->getopenids();  
  17.     }  
  18. }  

3、发起支付(WxPayServe)

  1. #支付成功后回调地址  
  2. protected $notifyUrl = '/index.php/index/Pay_Success_Information/saveInfor.html';  
  3. /**  
  4.  * 微信jsapi 支付跳转接收openid  
  5.  */  
  6. public function setPayInfo()  
  7. {   
  8.     header('Content-type:text/html; Charset=utf-8');  
  9.     //自己生成的订单号  
  10.     $order_number = $_GET['order_number'];  
  11.     $openid = $_GET['openid'];  
  12.         //自身业务数据信息  
  13.     $userOrder = $this->orderInformation->where('order_number'$order_number)->find();  
  14.     #传给微信支付的信息,原样返回给我  
  15.     $userInfor = [  
  16.         #用户订单号  
  17.         'order_number' => $userOrder['order_number'],//自行设置  
  18.     ];  
  19.     $userInfor = json_encode($userInfor);  
  20.     $today = date("Ymd");  
  21.     $outTradeNo = $today . uniqid(); #商户订单号,自己生成的  
  22.     //付款时间  
  23.     $payTime = time();  
  24.     $notify_url = 'http://'.$_SERVER['HTTP_HOST']. $this->notifyUrl; //回调地址       
  25.     $arr = $this->createJsBizPackage(  
  26.         $userOrder['pay_price'],//价格  
  27.         $outTradeNo,  
  28.         $userOrder['order_name'],//订单名称  
  29.         $notify_url,  
  30.         $payTime,  
  31.         $wxPayInformation['appid'],//appid  
  32.         $wxPayInformation['mchid'],//商户号  
  33.         $wxPayInformation['key'],//key  
  34.         $userInfor,  
  35.         $openid,  //jsapi必要参数  
  36.         $order_number  
  37.     );  
  38. }  
  39.   
  40.   
  41.   
  42. /**  
  43.  * 发起订单  
  44.  * @param float $totalFee 收款总费用 单位元  
  45.  * @param string $outTradeNo 唯一的订单号  
  46.  * @param string $orderName 订单名称  
  47.  * @param string $notifyUrl 支付结果通知url 不要有问号  
  48.  * @param string $timestamp 订单发起时间  
  49.  * @param string $appid 微信公众号appid  
  50.  * @param string $mchid 商户id  
  51.  * @param string $key 商户秘钥  
  52.  * @param string $userInfor 用户数据  
  53.  * @param string $openid 用户openid  
  54.  * @return array  
  55.  */  
  56. protected function createJsBizPackage($totalFee$outTradeNo$orderName$notifyUrl$timestamp,$appid,$mchid,$key,$userInfor,$openid='',$order_number='')  
  57. {  
  58.   
  59.     header('Content-type:text/html; Charset=utf-8');  
  60.     $config = array(  
  61.         'mch_id' => $mchid,  
  62.         'appid' => $appid,  
  63.         'key' => $key,  
  64.     );  
  65.     $timestamp = "$timestamp";  
  66.     //$orderName = iconv('GBK','UTF-8',$orderName);  
  67.     if(!emptyempty($openid)){  
  68.         //jsapi支付  
  69.         $unified = array(  
  70.             //jsapi支付  
  71.             'appid' => $config['appid'],  
  72.             'attach' => $userInfor,             //商家数据包,原样返回,如果填写中文,请注意转换为utf-8  
  73.             'body' => $orderName,  
  74.             'mch_id' => $config['mch_id'],  
  75.             'nonce_str' => self::createNonceStr(),  
  76.             'notify_url' => $notifyUrl,  
  77.             'out_trade_no' => $outTradeNo,  
  78.             'spbill_create_ip' => $this->get_client_ip(),  
  79.             'total_fee' => floatval($totalFee) * 100,       //单位 转为分  
  80.             //'trade_type' => 'NATIVE',  
  81.             'trade_type' => 'JSAPI',  
  82.             'openid' => $openid  
  83.         );  
  84.     }  
  85.     $unified['sign'] = self::getSign($unified$config['key']);  
  86.     # v3 api url  
  87.     // https://api.mch.weixin.qq.com/v3/pay/transactions/native  
  88.     # v2 api url  
  89.     // https://api.mch.weixin.qq.com/pay/unifiedorder  
  90.     $responseXml = self::curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', self::arrayToXml($unified));  
  91.     //禁止引用外部xml实体  
  92.     libxml_disable_entity_loader(true);  
  93.     $unifiedOrder = simplexml_load_string($responseXml'SimpleXMLElement', LIBXML_NOCDATA);  
  94.     $payType= (array)$unifiedOrder->trade_type;  
  95.     if($payType[0] == 'JSAPI'){  
  96.         //JSAPI 支付逻辑  
  97.         $appid = (array)$unifiedOrder->appid;  
  98.         $nonce_str = (array)$unifiedOrder->nonce_str;  
  99.         $package = (array)$unifiedOrder->prepay_id;  
  100.         $sing = (array)$unifiedOrder->sign;  
  101.         $times = strval(time());  
  102.         $jsApiArr = array(  
  103.             'appid' => $appid[0],  
  104.             'timeStamp' => $times,  
  105.             'nonceStr' => $nonce_str[0],  
  106.             'package' => 'prepay_id=' . $package[0],  
  107.             'signType' => 'MD5',  
  108.             'paySign' => $sing[0]  
  109.         );  
  110.         Session::set($order_number,json_encode($jsApiArr));  
  111.     }  
  112. }  
  113.   
  114.  /**获取jsApi 支付json信息 前端唤起jsapi支付使用  
  115.  * @param $order_number  
  116.  * @return array|mixed|string|null  
  117.  */  
  118. public function getJsApiPayInfo($order_number){  
  119.     if(Session::get($order_number)){  
  120.         $data =  Session::get($order_number);  
  121.     }  
  122.     return $data;  
  123. }  
  124.   
  125.   
  126. /**获取设备ip地址  
  127.  * @return array|false|mixed|string  
  128.  */  
  129. function get_client_ip() {  
  130.     static $realip;  
  131.     if (isset($_SERVER)) {  
  132.         if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) {  
  133.             $realip = $_SERVER["HTTP_X_FORWARDED_FOR"];  
  134.         } else if (isset($_SERVER["HTTP_CLIENT_IP"])) {  
  135.             $realip = $_SERVER["HTTP_CLIENT_IP"];  
  136.         } else {  
  137.             $realip = $_SERVER["REMOTE_ADDR"];  
  138.         }  
  139.     } else {  
  140.         if (getenv("HTTP_X_FORWARDED_FOR")) {  
  141.             $realip = getenv("HTTP_X_FORWARDED_FOR");  
  142.         } else if (getenv("HTTP_CLIENT_IP")) {  
  143.             $realip = getenv("HTTP_CLIENT_IP");  
  144.         } else {  
  145.             $realip = getenv("REMOTE_ADDR");  
  146.         }  
  147.     }  
  148.     return $realip;