微信支付开发

时间:2015-06-20 18:22:53   收藏:0   阅读:1826

微信支付真心最坑爹的支付,总结一下

一:前台页面

<include file="Public/mobile_head"/>
<html>
<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/> 
    <title>微信支付样例-支付</title>
    <script type="text/javascript">
    //调用微信JS api 支付
    function jsApiCall()
    {
        WeixinJSBridge.invoke(
            getBrandWCPayRequest,
            {$jsApiParameters},
            function(res){
                WeixinJSBridge.log(res.err_msg);
                //alert(res.err_code+res.err_desc+res.err_msg);
                var html="您已成功购买{$month_time}个月月卡,立刻获赠游戏币{$coin_award}。游戏UID:{$uid}<br>月卡到期时间: {$month_card_overtime}<br><button class=‘blue_btn mb_10 ‘>确定</button>";
                $.Dialog.sure(html);//成功调用 提示一秒后自动关闭
                $(".dialog_sure").click(function(){
                    WeixinJSBridge.call(closeWindow);
                });
                
            }
        );
    }

    function callpay()
    {
        if (typeof WeixinJSBridge == "undefined"){
            if( document.addEventListener ){
                document.addEventListener(WeixinJSBridgeReady, jsApiCall, false);
            }else if (document.attachEvent){
                document.attachEvent(WeixinJSBridgeReady, jsApiCall); 
                document.attachEvent(onWeixinJSBridgeReady, jsApiCall);
            }
        }else{
            jsApiCall();
        }
    }


    
    </script>
</head>
<body>
    <br/>
    <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">{$mustpay/100}元</span></b></font><br/><br/>
    <div align="center">
        <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>
    </div>
</body>
</html>

两个问题

(1)$jsApiParameters参数要对,包含noticestr,stamp,prepayid等,不能丢失,否则付款按钮不能点击

(2)WeixinJSBridge.call(closeWindow);这是微信的关闭当前页面,不然支付完成还会返回到这里

二.后台php

    function index() {
        if(IS_POST){
      /**前台参数post处理部分 略***/
        }
        //用的是weiphp 主要是要获取openid
        $param [‘token‘] = get_token ();
        $param [‘openid‘] = get_openid ();
        $config = getAddonConfig ( ‘Jssdk‘ );
        $appid = $config[‘APPID‘];
        $secret = $config[‘APPSECRET‘];
        //$id = 1;                                            //如有插件中数据id,分享url中应加入id参数
        //$url = addons_url ( ‘WxPay://WxPay/payorder‘, $param );  //分享的url需要和WeixinAddonModel中的组装回复url保持相同
        //$this->assign ( ‘share_url‘, $url );
        
        $jssdk = new JSSDK($appid, $secret);
        $jssdk->debug = false;    //启用本地调试模式,将官方的两个json文件放到入口文件index.php同级目录即可!
        $signPackage = $jssdk->GetSignPackage();
        
        $this->assign ( ‘signPackage‘, $signPackage );
        
        //微信支付部分
        //此处可以动态获取数据库中的MCHID和KEY
       /***************************/ 
    //这部分自行配置 /***************************/ $jssdk->MCHID =$config[‘MCHID‘]; // 动态MCHID;微信支付分配的商户号 $jssdk->KEY = $config[‘KEY‘]; // 动态KEY; $jssdk->SSLCERT_PATH = $config[‘SSLCERT_PATH‘]; // 动态KEY; $jssdk->SSLKEY_PATH = $config[‘SSLKEY_PATH‘]; // 动态KEY; $jssdk->APPID = $config[‘APPID‘]; // 动态KEY; $jssdk->APPSECRET = $config[‘APPSECRET‘]; // 动态KEY; //=========步骤2:使用统一支付接口,获取prepay_id============ //使用统一支付接口 $jssdk->parameters[‘openid‘] = $param [‘openid‘]; //trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。 $jssdk->parameters[‘body‘]="订单支付"; //商品或支付单简要描述 $jssdk->parameters[‘out_trade_no‘] = "outtradeno".time(); //商户系统内部的订单号,32个字符内、可包含字母,不可重复 $jssdk->parameters[‘total_fee‘] = 1; //此处单位为分 出现小数点接口报错必须是整数 $jssdk->parameters[‘notify_url‘] = ‘xxxxxxxxxxxxxxxxxxxxxxxxxxxxx 带http的地址 就下面那个alarmnotify方法‘; //接收微信支付异步通知回调地址 $jssdk->parameters[‘trade_type‘] = "JSAPI"; //取值如下:JSAPI,NATIVE,APP $jssdk->parameters[‘spbill_create_ip‘] = get_client_ip(); //APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。 //以下非必填参数根据需要添加 //$jssdk->parameters[‘device_info‘] = "013467007045764"; //微信支付分配的终端设备号,商户自定义 $jssdk->parameters[‘detail‘] = $desc; //商品名称明细列表 //$jssdk->parameters[‘attach‘] = "说明"; //附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 //$jssdk->parameters[‘fee_type‘] = "CNY"; //符合ISO 4217标准的三位字母代码,默认人民币:CNY $jssdk->parameters[‘time_start‘] = time(); //订单生成时间,格式为yyyyMMddHHmmss //$jssdk->parameters[‘time_expire‘] = "20091227091010"; //订单失效时间,格式为yyyyMMddHHmmss //$jssdk->parameters[‘goods_tag‘] = "WXG"; //商品标记,代金券或立减优惠功能的参数 $jssdk->parameters[‘product_id‘] = $product_id; //trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。 $prepay_id = $jssdk->getPrepayId(); //微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时 $jssdk->prepay_id = $prepay_id ; //微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时 //=========步骤3:使用jsapi调起支付============ $jsApiParameters = $jssdk->getParameters(); //=========步骤N:当有prepayid再生成订单用户自己生成订单============ if($prepay_id){
    //这是自己的业务逻辑,理论上有prepayid之后,自己数据库里面就应该有一张订单,未支付的订单
$data[‘uid‘]=$uid; $data[‘mustpay‘]=$mustpay; $data[‘prepay_id‘]=$prepay_id; $data[‘goods‘]=$sel;//选择的商品,目前是select的值 $data[‘desc‘]=$desc; $data[‘product_id‘] = $product_id; $data[‘openid‘]= $param [‘openid‘]; $data[‘trade_type‘]="JSAPI"; $data[‘time‘]=time(); $data[‘body‘]= $jssdk->parameters[‘body‘]; $data[‘out_trade_no‘]=$jssdk->parameters[‘out_trade_no‘]; M ( "mall_order" )->add($data); } //JSSDK 用户支付完成后的一些系统操作 /* $param1 [‘dcnum‘] = $jssdk->parameters[‘out_trade_no‘]; $param1 [‘openid‘] = $jssdk->parameters[‘openid‘]; $ajaxurl = addons_url ( ‘Jssdk://Jssdk/orderpaid‘, $param1 ); //用户支付完成后,在微信支付返回alarmnotify之前(不保证时序),可以通过ajax调用,进行一些预处理操作 $jsApiParameters = substr($jsApiParameters,1,-1).",success: function (res) { // 支付成功后的js回调函数 }"; */ $param1 [‘openid‘] = $jssdk->parameters[‘openid‘]; $ajaxurl= addons_url ( ‘Member://Member/show_info‘ ,$param); $this->assign("ajaxurl",$ajaxurl); //向页面传整理好的调起支付参数 $this->assign("jsApiParameters",$jsApiParameters); //向页面传整理好的调起支付参数 $this->display (); }

三,通知处理

 //支付完成接收支付服务器返回通知
    public function alarmnotify(){
        $post_data = $GLOBALS [‘HTTP_RAW_POST_DATA‘];
        \Think\Log::record ( "alarmnotify". $post_data  );
                //log的内容大体如下
        /*
          public ‘appid‘ => string ‘w345345345a83a9b9b9‘ (length=18)
          public ‘bank_type‘ => string ‘CFT‘ (length=3)
          public ‘cash_fee‘ => string ‘1‘ (length=1)
          public ‘fee_type‘ => string ‘CNY‘ (length=3)
          public ‘is_subscribe‘ => string ‘Y‘ (length=1)
          public ‘mch_id‘ => string ‘1233453902‘ (length=10)
          public ‘nonce_str‘ => string ‘ma4l5345345rzZN‘ (length=16)
          public ‘openid‘ => string ‘oV345345345calllGlBdwRU1BjY‘ (length=28)
          public ‘out_trade_no‘ => string ‘outtradeno14234324‘ (length=20)
          public ‘result_code‘ => string ‘SUCCESS‘ (length=7)
          public ‘return_code‘ => string ‘SUCCESS‘ (length=7)
          public ‘sign‘ => string ‘735C329E5234234796E723728969‘ (length=32)
          public ‘time_end‘ => string ‘2034534504523‘ (length=14)
          public ‘total_fee‘ => string ‘1‘ (length=1)
          public ‘trade_type‘ => string ‘JSAPI‘ (length=5)
          public ‘transaction_id‘ => string ‘13453453453422835‘ (length=28)
         */
       
        $result = xmlToArray($post_data);
        $resp = true;
        $respdata["return_code"] = "SUCCESS";
        $respdata["return_msg"] = "";
    
        $map["appid"] = $result["appid"];
        $map["mch_id"] = $result["mch_id"];
        $map["openid"] = $result["openid"];
        $map["out_trade_no"] = $result["out_trade_no"];
        
 
        if($result[‘result_code‘]==‘SUCCESS‘  &&      $result[‘return_code‘]==‘SUCCESS‘){
            //支付成功了
            //进行业务处理
            
            echo "success";
        }
        


    }    

四,微信支付类

<?php

/**


类是uctoo写的,貌似现在的类更好,我反正能用就行,


 * 官方文档:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
 * 微信支付:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=9_1#
 * 官方示例:http://demo.open.weixin.qq.com/jssdk/sample.zip
 * 
 * 微信JSSDK类,主要修改了保存会话信息机制,示例中使用的是文件,这里使用了ThinkPHP的缓存机制,参考官方提供的示例文档 
 * 新增了调试模式,调用示例如下:
 *         $jssdk = new JSSDK(C(‘WX_APPID‘), C(‘WX_SECRET‘));
 *      $jssdk->debug = true;    //启用本地调试模式,将官方的两个json文件放到入口文件index.php同级目录即可!
 *       $signPackage = $jssdk->GetSignPackage();
 * @命名空间版本
 * @author uctoo (www.uctoo.com) & 阿甘 (QQ:33808 624)
 * @date 2015-1-10 14:10
 */
namespace Com;

class JsSdk {
  private $appId;
  private $appSecret;
  public $debug = false;
  public $parameters;//获取prepay_id时的请求参数
  //受理商ID,身份标识
  public $MCHID = ‘‘;
  //商户支付密钥Key。审核通过后,在微信发送的邮件中查看
  public $KEY = ‘‘;

  //=======【JSAPI路径设置】===================================
  //获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面
  public $JS_API_CALL_URL = ‘‘;

  //=======【证书路径设置】=====================================
  //证书路径,注意应该填写绝对路径
  public $SSLCERT_PATH = ‘/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_cert.pem‘;
  public $SSLKEY_PATH = ‘/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_key.pem‘;

  //=======【异步通知url设置】===================================
  //异步通知url,商户根据实际开发过程设定
  //C(‘url‘)."admin.php/order/notify_url.html";
  public $NOTIFY_URL = ‘‘;

  //=======【curl超时设置】===================================
  //本例程通过curl使用HTTP POST方法,此处可修改其超时时间,默认为30秒
  public  $CURL_TIMEOUT = 30;

  public  $prepay_id;


  public function __construct($appId, $appSecret) {
    $this->appId = $appId;
    $this->appSecret = $appSecret;
  }

  public function getSignPackage() {
    $jsapiTicket = $this->getJsApiTicket();
    $url = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
    $timestamp = time();
    $nonceStr = $this->createNonceStr();

    // 这里参数的顺序要按照 key 值 ASCII 码升序排序
    $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";

    $signature = sha1($string);

    $signPackage = array(
      "appId"     => $this->appId,
      "nonceStr"  => $nonceStr,
      "timestamp" => $timestamp,
      "url"       => $url,
      "signature" => $signature,
      "rawString" => $string
    );
    return $signPackage; 
  }

  private function createNonceStr($length = 16) {
    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    $str = "";
    for ($i = 0; $i < $length; $i++) {
      $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
    }
    return $str;
  }

  private function getJsApiTicket() {
      //debug模式
      if ($this->debug) {
        // jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
        $data = json_decode(file_get_contents("jsapi_ticket.json"));
      } else {
          //从cache中读取,基于ThinkPHP的缓存机制
          $data = (object)(S(‘jsapi_ticket_json‘));
      }

    if ($data->expire_time < time()) {       
      $accessToken = $this->getAccessToken();
      $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=1&access_token=$accessToken";
      $res = json_decode($this->httpGet($url));
      $ticket = $res->ticket;
      
      if ($ticket) {
        $data->expire_time = time() + 7200;
        $data->jsapi_ticket = $ticket;
               
        //debug模式
        if ($this->debug) {
            $fp = fopen("jsapi_ticket.json", "w");
            fwrite($fp, json_encode($data));
            fclose($fp);
        } else {
            //将对象以数组的形式进行缓存
            S(‘jsapi_ticket_json‘, (array)$data);
        }

      }
    } else {
      $ticket = $data->jsapi_ticket;
    }

    return $ticket;
  }

  private function getAccessToken() {

      //debug模式
      if ($this->debug) {
        // access_token 应该全局存储与更新,以下代码以写入到文件中做示例
          $data = json_decode(file_get_contents("access_token.json"));
          dump($data);
          die();
      } else {
          //从缓存中读取数组并转成对象
        $data = (Object)(S(‘access_token.json‘));
      }
    
    if ($data->expire_time < time()) { 
      $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
      $res = json_decode($this->httpGet($url));
      $access_token = $res->access_token;

      if ($access_token) {
        $data->expire_time = time() + 7000;
        $data->access_token = $access_token;

        //debug模式
        if ($this->debug) {
            $fp = fopen("access_token.json", "w");
            fwrite($fp, json_encode($data));
            fclose($fp);
        } else {
            //缓存数组
            S(‘access_token.json‘, (array)$data);            
        }
        
      }
    } else {
      $access_token = $data->access_token;
    }
    return $access_token;
  }

  private function httpGet($url) {
      
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_TIMEOUT, 500);
    curl_setopt($curl, CURLOPT_URL, $url);

    $res = curl_exec($curl);
    
    //错误检测
    $error = curl_error($curl);
    
    curl_close($curl);

    //发生错误,抛出异常
    if($error) throw new \Exception(‘请求发生错误(表检查是否在授权域名下访问):‘ . $error);
    
    return $res;
  }

  //微信支付相关方法
  /**
   *     作用:格式化参数,签名过程需要使用
   */
  function formatBizQueryParaMap($paraMap, $urlencode)
  {
    $buff = "";
    ksort($paraMap);
    foreach ($paraMap as $k => $v)
    {
      if($urlencode)
      {
        $v = urlencode($v);
      }
      //$buff .= strtolower($k) . "=" . $v . "&";
      $buff .= $k . "=" . $v . "&";
    }
    $reqPar = "";
    if (strlen($buff) > 0)
    {
      $reqPar = substr($buff, 0, strlen($buff)-1);
    }
    return $reqPar;
  }
  /**
   *     作用:设置jsapi的参数
   */
  public function getParameters()
  {
    $jsApiObj["appId"] = $this->appId;           //请求生成支付签名时需要,js调起支付参数中不需要
    $timeStamp = time();
    $jsApiObj["timeStamp"] = "$timeStamp";      //用大写的timeStamp参数请求生成支付签名
    $jsParamObj["appId"] =$jsApiObj["appId"]; //加入一行appid
    $jsParamObj["timeStamp"] = $jsApiObj["timeStamp"];      //用小写的timestamp参数生成js支付参数,还要注意数据类型,坑!
    $jsParamObj["nonceStr"] = $jsApiObj["nonceStr"] = $this->createNoncestr();
    $jsParamObj["package"] = $jsApiObj["package"] = "prepay_id=$this->prepay_id";
    $jsParamObj["signType"] = $jsApiObj["signType"] = "MD5";
    $jsParamObj["paySign"] = $jsApiObj["paySign"] = $this->getSign($jsApiObj);

    $jsParam = json_encode($jsParamObj);

    return $jsParam;
  }

  /**
   * 获取prepay_id
   */
  function getPrepayId()
  {
    $result = $this->xmlToArray($this->postXml());
    $prepay_id = $result["prepay_id"];
    return $prepay_id;
  }
  /**
   *     作用:将xml转为array
   */
  public function xmlToArray($xml)
  {
    //将XML转为array
    $array_data = json_decode(json_encode(simplexml_load_string($xml, ‘SimpleXMLElement‘, LIBXML_NOCDATA)), true);
    return $array_data;
  }
  /**
   *     作用:post请求xml
   */
  function postXml()
  {
    $xml = $this->createXml();

    return  $this->postXmlCurl($xml,"https://api.mch.weixin.qq.com/pay/unifiedorder",$this->CURL_TIMEOUT);

  }
  /**
   *     作用:以post方式提交xml到对应的接口url
   */
  public function postXmlCurl($xml,$url,$second=30)
  {
    //初始化curl
    $ch = curl_init();
    //设置超时
    curl_setopt($ch,CURLOP_TIMEOUT, $this->CURL_TIMEOUT);
    //这里设置代理,如果有的话
    //curl_setopt($ch,CURLOPT_PROXY, ‘8.8.8.8‘);
    //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
    curl_setopt($ch,CURLOPT_URL, $url);
    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
    //设置header
    curl_setopt($ch, CURLOPT_HEADER, FALSE);
    //要求结果为字符串且输出到屏幕上
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    //post提交方式
    curl_setopt($ch, CURLOPT_POST, TRUE);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
    //运行curl
    $data = curl_exec($ch);
    curl_close($ch);
    //返回结果
    if($data)
    {
      curl_close($ch);
      return $data;
    }
    else
    {
      $error = curl_errno($ch);
      echo "curl出错,错误码:$error"."<br>";
      echo "<a href=‘http://curl.haxx.se/libcurl/c/libcurl-errors.html‘>错误原因查询</a></br>";
      curl_close($ch);
      return false;
    }
  }
  /**
   *     作用:设置标配的请求参数,生成签名,生成接口参数xml
   */
  function createXml()
  {
    $this->parameters["appid"] = $this->appId;//公众账号ID
    $this->parameters["mch_id"] = $this->MCHID;//商户号
    $this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
    $this->parameters["sign"] = $this->getSign($this->parameters);//签名
    return  $this->arrayToXml($this->parameters);
  }
   /**
   *     作用:array转xml
   */
  function arrayToXml($arr)
  {
    $xml = "<xml>";
    foreach ($arr as $key=>$val)
    {
      if (is_numeric($val))
      {
        $xml.="<".$key.">".$val."</".$key.">";

      }
      else
        $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
    }
    $xml.="</xml>";
    return $xml;
  }
  /**
   *     作用:生成签名
   */
  public function getSign($Obj)
  {
    foreach ($Obj as $k => $v)
    {
      $Parameters[$k] = $v;
    }
    //签名步骤一:按字典序排序参数
    ksort($Parameters);
    $String = $this->formatBizQueryParaMap($Parameters, false);
    //echo ‘【string1】‘.$String.‘</br>‘;
    //签名步骤二:在string后加入KEY
    $String = $String."&key=".$this->KEY;
    //echo "【string2】".$String."</br>";
    //签名步骤三:MD5加密
    $String = md5($String);
    //echo "【string3】 ".$String."</br>";
    //签名步骤四:所有字符转为大写
    $result_ = strtoupper($String);
    //echo "【result】 ".$result_."</br>";
    return $result_;
  }
    
}

 

有问题再单独开日志吧...感觉不是很顺畅..比支付宝差远了

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!