zoukankan      html  css  js  c++  java
  • 微信支付 APP 支付方式的服务器端处理程序

    对于微信的APP的支付,客户服务说只能通过微信开放平台申请。后来在公众帐号确实发现了证据: 
    这里写图片描述

    微信支付在申请的时候就比较严(麻烦),对服务类的一些支付,本来商品就是虚拟的,所以需要将商品描述的比较详细,服务类的嘛,支付流程是如何的,我们提供什么服务的,操作界面如何等。商品描述140个字,考验你的文本组织能力了。

    支付帐号申请下来后,收到财付通的一封邮件 
    效果如下: 
    https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=3_1 
    这里写图片描述

    基本上,app支付的流程就是 
    1、统一下单(由自己的服务器处理) 
    2、发起支付(客户端) 
    3、支付成功回调(服务器端) 
    https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1 
    这里写图片描述

    这里只说第一点,统一下单程序。统一下单的服务器端处理,就是要生成预支付订单的ID 
    调试了一下,有一些坑,整理代码如下:

    <?php
    header("Content-type: text/html; charset=utf-8");
    include "../../config.php";
    
    $orderBody = "test商品";
    $tade_no = "abc_" . time();
    $total_fee = 1;
    $WxPayHelper = new WxPayHelper();
    $response = $WxPayHelper->getPrePayOrder($orderBody, $tade_no, $total_fee);
    
    p_val("---response----");
    p_val($response);
    p_val("---拿到prepayId再次签名----");
    $x = $WxPayHelper->getOrder($response['prepay_id']);
    p_val($x);
    
    /**
     * convert xml string to php array - useful to get a serializable value
     *
     * @param string $xmlstr
     * @return array
     * @author Adrien aka Gaarf
     */
    
    class WxPayHelper{
        /*
        配置参数
        */
        var $config = array(
            'appid' => "wx7e26b00000000000",    /*微信开放平台上的应用id*/
            'mch_id' => "1233000000",   /*微信申请成功之后邮件中的商户id*/
            'api_key' => "s6aITei3J3d4UYcCn3k0Mq0000000000",    /*在微信商户平台上自己设定的api密钥 32位*/
            'notify_url' => 'http://mycompany.com/pub_v2/pay/notify.v2.php' /*自定义的回调程序地址id*/
        );
    
        public function  __construct() {
    
        }
    
        //获取预支付订单
        public function getPrePayOrder($body, $out_trade_no, $total_fee){
            $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
            $notify_url = $this->config["notify_url"];
    
            $onoce_str = $this->getRandChar(32);
    
            $data["appid"] = $this->config["appid"];
            $data["body"] = $body;
            $data["mch_id"] = $this->config['mch_id'];
            $data["nonce_str"] = $onoce_str;
            $data["notify_url"] = $notify_url;
            $data["out_trade_no"] = $out_trade_no;
            $data["spbill_create_ip"] = $this->get_client_ip();
            $data["total_fee"] = $total_fee;
            $data["trade_type"] = "APP";
            $s = $this->getSign($data, false);
            $data["sign"] = $s;
    
            $xml = $this->arrayToXml($data);
            $response = $this->postXmlCurl($xml, $url);
    
            //将微信返回的结果xml转成数组
            return $this->xmlstr_to_array($response);
        }
    
        //执行第二次签名,才能返回给客户端使用
        public function getOrder($prepayId){
            $data["appid"] = $this->config["appid"];
            $data["noncestr"] = $this->getRandChar(32);;
            $data["package"] = "Sign=WXPay";
            $data["partnerid"] = $this->config['mch_id'];
            $data["prepayid"] = $prepayId;
            $data["timestamp"] = time();
            $s = $this->getSign($data, false);
            $data["sign"] = $s;
    
            return $data;
        }
    
        /*
            生成签名
        */
        function getSign($Obj)
        {
            foreach ($Obj as $k => $v)
            {
                $Parameters[strtolower($k)] = $v;
            }
            //签名步骤一:按字典序排序参数
            ksort($Parameters);
            $String = $this->formatBizQueryParaMap($Parameters, false);
            //echo "【string】 =".$String."</br>";
            //签名步骤二:在string后加入KEY
            $String = $String."&key=".$this->config['api_key'];
            echo "<textarea style=' 50%; height: 150px;'>$String</textarea> <br />";
            //签名步骤三:MD5加密
            $result_ = strtoupper(md5($String));
            return $result_;
        }
    
        //获取指定长度的随机字符串
        function getRandChar($length){
           $str = null;
           $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
           $max = strlen($strPol)-1;
    
           for($i=0;$i<$length;$i++){
            $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
           }
    
           return $str;
        }
    
        //数组转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; 
        }
    
        //post https请求,CURLOPT_POSTFIELDS xml格式
        function postXmlCurl($xml,$url,$second=30)
        {       
            //初始化curl        
            $ch = curl_init();
            //超时时间
            curl_setopt($ch,CURLOPT_TIMEOUT,$second);
            //这里设置代理,如果有的话
            //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);
            //返回结果
            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;
            }
        }
    
        /*
            获取当前服务器的IP
        */
        function get_client_ip()
        {
            if ($_SERVER['REMOTE_ADDR']) {
            $cip = $_SERVER['REMOTE_ADDR'];
            } elseif (getenv("REMOTE_ADDR")) {
            $cip = getenv("REMOTE_ADDR");
            } elseif (getenv("HTTP_CLIENT_IP")) {
            $cip = getenv("HTTP_CLIENT_IP");
            } else {
            $cip = "unknown";
            }
            return $cip;
        }
    
        //将数组转成uri字符串
        function formatBizQueryParaMap($paraMap, $urlencode)
        {
            $buff = "";
            ksort($paraMap);
            foreach ($paraMap as $k => $v)
            {
                if($urlencode)
                {
                   $v = urlencode($v);
                }
                $buff .= strtolower($k) . "=" . $v . "&";
            }
            $reqPar;
            if (strlen($buff) > 0) 
            {
                $reqPar = substr($buff, 0, strlen($buff)-1);
            }
            return $reqPar;
        }
    
        /**
        xml转成数组
        */
        function xmlstr_to_array($xmlstr) {
          $doc = new DOMDocument();
          $doc->loadXML($xmlstr);
          return $this->domnode_to_array($doc->documentElement);
        }
        function domnode_to_array($node) {
          $output = array();
          switch ($node->nodeType) {
           case XML_CDATA_SECTION_NODE:
           case XML_TEXT_NODE:
            $output = trim($node->textContent);
           break;
           case XML_ELEMENT_NODE:
            for ($i=0, $m=$node->childNodes->length; $i<$m; $i++) {
             $child = $node->childNodes->item($i);
             $v = $this->domnode_to_array($child);
             if(isset($child->tagName)) {
               $t = $child->tagName;
               if(!isset($output[$t])) {
                $output[$t] = array();
               }
               $output[$t][] = $v;
             }
             elseif($v) {
              $output = (string) $v;
             }
            }
            if(is_array($output)) {
             if($node->attributes->length) {
              $a = array();
              foreach($node->attributes as $attrName => $attrNode) {
               $a[$attrName] = (string) $attrNode->value;
              }
              $output['@attributes'] = $a;
             }
             foreach ($output as $t => $v) {
              if(is_array($v) && count($v)==1 && $t!='@attributes') {
               $output[$t] = $v[0];
              }
             }
            }
           break;
          }
          return $output;
        }
    }
    ?>
    

    注意点: 
    ①post必须支持https,且参数格式必须是xml 
    ②sign签名的参数包括所有$data,除了自己 
    这里写图片描述 
    ③$data[“spbill_create_ip”]不能随便设定一个ip地址,不要以为调试方便随便设定,结果返回签名错误坑你没商量。一定要是程序执行时所在的服务器ip地址,所以使用get_client_ip()获取就好。 
    ④api_key是需要自己进入商户平台设定的,邮件不会发给你哦 
    这里写图片描述
    使用随机程序产生32个字符就好了 
    ⑤相当重要的是:返回给各户端发起支付时,还要进行二次签名 $WxPayHelper->getOrder

  • 相关阅读:
    P1121 环状最大两段子段和
    无题
    cdoj 1485 柱爷搞子串 sam treap
    自然数幂和
    Gym 100341C AVL Trees NTT
    线性筛分解质因子
    codeforces 366 Ant Man dp
    UVALive 6914 Maze Mayhem 轮廓线dp
    hdu 5790 Prefix 字典树 主席树
    莫比乌斯反演个人小结
  • 原文地址:https://www.cnblogs.com/phpxuetang/p/5032462.html
Copyright © 2011-2022 走看看