当前位置:首页 > PHP教程 > php高级应用 > 列表

PHP实现 APP端微信支付功能

发布:smiling 来源: PHP粉丝网  添加日期:2021-10-05 12:22:09 浏览: 评论:0 

前面已经写了手机APP支付宝支付,今天再把手机APP微信支付补上,前期的准备工作在这里就不多说了,可以参考微信支付开发文档,一定要仔细阅读开发文档,可以让你少踩点坑;准备工作完成后就是配置参数,调用统一下单接口,支付后异步回调三部曲啦;

1.我封装好的一个支付类文件,多余的东西都去除掉了,并且把配置参数放到了这个支付类中,只需要修改Weixinpayandroid方法内的几个参数就可以直接复制使用:

  1. class Wxpayandroid 
  2. { 
  3.  //参数配置 
  4.  public $config = array( 
  5.     'appid' => "", /*微信开放平台上的应用id*/ 
  6.     'mch_id' => "", /*微信申请成功之后邮件中的商户id*/ 
  7.     'api_key' => "", /*在微信商户平台上自己设定的api密钥 32位*/ 
  8.    ); 
  9.  //服务器异步通知页面路径(必填) 
  10.  public $notify_url = ''; 
  11.  //商户订单号(必填,商户网站订单系统中唯一订单号) 
  12.  public $out_trade_no = ''; 
  13.  //商品描述(必填,不填则为商品名称) 
  14.  public $body = ''; 
  15.  //付款金额(必填) 
  16.  public $total_fee = 0; 
  17.  //自定义超时(选填,支持dhmc) 
  18.  public $time_expire = ''; 
  19.  private $WxPayHelper; 
  20.  public function Weixinpayandroid($total_fee,$tade_no) 
  21.  { 
  22.   $this->total_fee = intval($total_fee * 100);//订单的金额 1元 
  23.   $this->out_trade_no = $tade_no;// date('YmdHis') . substr(time(), - 5) . substr(microtime(), 2, 5) . sprintf('%02d', rand(0, 99));//订单号 
  24.   $this->body = 'wxpay';//支付描述信息 
  25.   $this->time_expire = date('YmdHis', time() + 86400);//订单支付的过期时间(eg:一天过期) 
  26.   $this->notify_url = "http://www.ceshi.com/notifyandroid";//异步通知URL(更改支付状态) 
  27.   //数据以JSON的形式返回给APP 
  28.   $app_response = $this->doPay();  
  29.   if (isset($app_response['return_code']) && $app_response['return_code'] == 'FAIL') { 
  30.    $errorCode = 100; 
  31.    $errorMsg = $app_response['return_msg']; 
  32.    $this->echoResult($errorCode, $errorMsg); 
  33.   } else { 
  34.    $errorCode = 0; 
  35.    $errorMsg = 'success'; 
  36.    $responseData = array( 
  37.     'notify_url' => $this->notify_url, 
  38.     'app_response' => $app_response, 
  39.    ); 
  40.    $this->echoResult($errorCode, $errorMsg, $responseData); 
  41.   } 
  42.  } 
  43.  //接口输出 
  44.  function echoResult($errorCode = 0, $errorMsg = 'success', $responseData = array()) 
  45.  { 
  46.   $arr = array( 
  47.    'errorCode' => $errorCode, 
  48.    'errorMsg' => $errorMsg, 
  49.    'responseData' => $responseData, 
  50.   ); 
  51.    exit(json_encode($arr));  //exit可以正常发送给APP json数据 
  52.   // return json_encode($arr); //在TP5中return这个json数据,APP接收到的是null,无法正常吊起微信支付 
  53.  } 
  54.  function getVerifySign($data, $key) 
  55.  { 
  56.   $String = $this->formatParameters($data, false); 
  57.   //签名步骤二:在string后加入KEY 
  58.   $String = $String . "&key=" . $key; 
  59.   //签名步骤三:MD5加密 
  60.   $String = md5($String); 
  61.   //签名步骤四:所有字符转为大写 
  62.   $result = strtoupper($String); 
  63.   return $result; 
  64.  } 
  65.  function formatParameters($paraMap, $urlencode) 
  66.  { 
  67.   $buff = ""; 
  68.   ksort($paraMap); 
  69.   foreach ($paraMap as $k => $v) { 
  70.    if($k=="sign"){ 
  71.     continue; 
  72.    } 
  73.    if ($urlencode) { 
  74.     $v = urlencode($v); 
  75.    } 
  76.    $buff .= $k . "=" . $v . "&"; 
  77.   } 
  78.   $reqPar; 
  79.   if (strlen($buff) > 0) { 
  80.    $reqPar = substr($buff, 0, strlen($buff) - 1); 
  81.   } 
  82.   return $reqPar; 
  83.  } 
  84.  /** 
  85.   * 得到签名 
  86.   * @param object $obj 
  87.   * @param string $api_key 
  88.   * @return string 
  89.   */ 
  90.  function getSign($obj, $api_key) 
  91.  { 
  92.   foreach ($obj as $k => $v) 
  93.   { 
  94.    $Parameters[strtolower($k)] = $v; 
  95.   } 
  96.   //签名步骤一:按字典序排序参数 
  97.   ksort($Parameters); 
  98.   $String = $this->formatBizQueryParaMap($Parameters, false); 
  99.   //签名步骤二:在string后加入KEY 
  100.   $String = $String."&key=".$api_key; 
  101.   //签名步骤三:MD5加密 
  102.   $result = strtoupper(md5($String)); 
  103.   return $result; 
  104.  } 
  105.  /** 
  106.   * 获取指定长度的随机字符串 
  107.   * @param int $length 
  108.   * @return Ambigous <NULL, string> 
  109.   */ 
  110.  function getRandChar($length){ 
  111.   $str = null; 
  112.   $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; 
  113.   $max = strlen($strPol)-1; 
  114.   for($i=0;$i<$length;$i++){ 
  115.    $str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数 
  116.   } 
  117.   return $str; 
  118.  } 
  119.  /** 
  120.   * 数组转xml 
  121.   * @param array $arr 
  122.   * @return string 
  123.   */ 
  124.  function arrayToXml($arr) 
  125.  { 
  126.   $xml = "<xml>"; 
  127.   foreach ($arr as $key=>$val) 
  128.   { 
  129.     if (is_numeric($val)) 
  130.     { 
  131.     $xml.="<".$key.">".$val."</".$key.">"; 
  132.     } 
  133.     else 
  134.     $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";  
  135.   } 
  136.   $xml.="</xml>"; 
  137.   return $xml; 
  138.  } 
  139.  /** 
  140.   * 以post方式提交xml到对应的接口url 
  141.   * 
  142.   * @param string $xml 需要post的xml数据 
  143.   * @param string $url url 
  144.   * @param bool $useCert 是否需要证书,默认不需要 
  145.   * @param int $second url执行超时时间,默认30s 
  146.   * @throws WxPayException 
  147.   */ 
  148.  function postXmlCurl($xml, $url, $second=30, $useCert=false, $sslcert_path='', $sslkey_path='') 
  149.  { 
  150.   $ch = curl_init(); 
  151.   //设置超时 
  152.   curl_setopt($ch, CURLOPT_TIMEOUT, $second); 
  153.   curl_setopt($ch,CURLOPT_URL, $url); 
  154.   //设置header 
  155.   curl_setopt($ch, CURLOPT_HEADER, FALSE); 
  156.   //要求结果为字符串且输出到屏幕上 
  157.   curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); 
  158.   curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE); 
  159.   curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE); 
  160.   if($useCert == true){ 
  161.    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE); 
  162.    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验 
  163.    //设置证书 
  164.    //使用证书:cert 与 key 分别属于两个.pem文件 
  165.    curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); 
  166.    curl_setopt($ch,CURLOPT_SSLCERT, $sslcert_path); 
  167.    curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); 
  168.    curl_setopt($ch,CURLOPT_SSLKEY, $sslkey_path); 
  169.   } 
  170.   //post提交方式 
  171.   curl_setopt($ch, CURLOPT_POST, TRUE); 
  172.   curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); 
  173.   //运行curl 
  174.   $data = curl_exec($ch); 
  175.   //返回结果 
  176.   if($data){ 
  177.    curl_close($ch); 
  178.    return $data; 
  179.   } else { 
  180.    $error = curl_errno($ch); 
  181.    curl_close($ch); 
  182.    return false; 
  183.   } 
  184.  } 
  185.  /** 
  186.   * 获取当前服务器的IP 
  187.   * @return Ambigous <string, unknown> 
  188.   */ 
  189.  function get_client_ip() 
  190.  { 
  191.   if (isset($_SERVER['REMOTE_ADDR'])) { 
  192.    $cip = $_SERVER['REMOTE_ADDR']; 
  193.   } elseif (getenv("REMOTE_ADDR")) { 
  194.    $cip = getenv("REMOTE_ADDR"); 
  195.   } elseif (getenv("HTTP_CLIENT_IP")) { 
  196.    $cip = getenv("HTTP_CLIENT_IP"); 
  197.   } else { 
  198.    $cip = "127.0.0.1"; 
  199.   } 
  200.   return $cip; 
  201.  } 
  202.  /** 
  203.   * 将数组转成uri字符串 
  204.   * @param array $paraMap 
  205.   * @param bool $urlencode 
  206.   * @return string 
  207.   */ 
  208.  function formatBizQueryParaMap($paraMap, $urlencode) 
  209.  { 
  210.   $buff = ""; 
  211.   ksort($paraMap); 
  212.   foreach ($paraMap as $k => $v) 
  213.   { 
  214.    if($urlencode) 
  215.    { 
  216.     $v = urlencode($v); 
  217.    } 
  218.    $buff .= strtolower($k) . "=" . $v . "&"; 
  219.   } 
  220.   $reqPar; 
  221.   if (strlen($buff) > 0) 
  222.   { 
  223.    $reqPar = substr($buff, 0, strlen($buff)-1); 
  224.   } 
  225.   return $reqPar; 
  226.  } 
  227.  /** 
  228.   * XML转数组 
  229.   * @param unknown $xml 
  230.   * @return mixed 
  231.   */ 
  232.  function xmlToArray($xml) 
  233.  { 
  234.   //将XML转为array 
  235.   $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); 
  236.   return $array_data; 
  237.  } 
  238.  public function chkParam() 
  239.  { 
  240.   //用户网站订单号 
  241.   if (emptyempty($this->out_trade_no)) { 
  242.    die('out_trade_no error'); 
  243.   }  
  244.   //商品描述 
  245.   if (emptyempty($this->body)) { 
  246.    die('body error'); 
  247.   } 
  248.   if (emptyempty($this->time_expire)){ 
  249.    die('time_expire error'); 
  250.   } 
  251.   //检测支付金额 
  252.   if (emptyempty($this->total_fee) || !is_numeric($this->total_fee)) { 
  253.    die('total_fee error'); 
  254.   } 
  255.   //异步通知URL 
  256.   if (emptyempty($this->notify_url)) { 
  257.    die('notify_url error'); 
  258.   } 
  259.   if (!preg_match("#^http:\/\/#i", $this->notify_url)) { 
  260.    $this->notify_url = "http://" . $_SERVER['HTTP_HOST'] . $this->notify_url; 
  261.   } 
  262.   return true; 
  263.  } 
  264.  /** 
  265.   * 生成支付(返回给APP) 
  266.   * @return boolean|mixed 
  267.   */ 
  268.  public function doPay() { 
  269.   //检测构造参数 
  270.   $this->chkParam(); 
  271.   return $this->createAppPara(); 
  272.  } 
  273.  /** 
  274.   * APP统一下单 
  275.   */ 
  276.  private function createAppPara() 
  277.  { 
  278.   $url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; 
  279.   $data["appid"]  = $this->config['appid'];//微信开放平台审核通过的应用APPID 
  280.   $data["body"]   = $this->body;//商品或支付单简要描述 
  281.   $data["mch_id"]  = $this->config['mch_id'];//商户号 
  282.   $data["nonce_str"] = $this->getRandChar(32);//随机字符串 
  283.   $data["notify_url"] = $this->notify_url;//通知地址 
  284.   $data["out_trade_no"] = $this->out_trade_no;//商户订单号 
  285.   $data["spbill_create_ip"] = $this->get_client_ip();//终端IP 
  286.   $data["total_fee"]  = $this->total_fee;//总金额 
  287.   $data["time_expire"]  = $this->time_expire;//交易结束时间 
  288.   $data["trade_type"]  = "APP";//交易类型 
  289.   $data["sign"]    = $this->getSign($data, $this->config['api_key']);//签名 
  290.   $xml  = $this->arrayToXml($data); 
  291.   $response = $this->postXmlCurl($xml, $url); 
  292.   //将微信返回的结果xml转成数组 
  293.   $responseArr = $this->xmlToArray($response); 
  294.   if(isset($responseArr["return_code"]) && $responseArr["return_code"]=='SUCCESS'){ 
  295.    return $this->getOrder($responseArr['prepay_id']); 
  296.   } 
  297.   return $responseArr; 
  298.  } 
  299.  /** 
  300.   * 执行第二次签名,才能返回给客户端使用 
  301.   * @param int $prepayId:预支付交易会话标识 
  302.   * @return array 
  303.   */ 
  304.  public function getOrder($prepayId) 
  305.  { 
  306.   $data["appid"]  = $this->config['appid']; 
  307.   $data["noncestr"] = $this->getRandChar(32); 
  308.   $data["package"] = "Sign=WXPay"; 
  309.   $data["partnerid"] = $this->config['mch_id']; 
  310.   $data["prepayid"] = $prepayId; 
  311.   $data["timestamp"] = time(); 
  312.   $data["sign"]  = $this->getSign($data, $this->config['api_key']); 
  313.   $data["packagestr"] = "Sign=WXPay"; 
  314.   return $data; 
  315.  } 
  316.  /** 
  317.   * 异步通知信息验证 
  318.   * @return boolean|mixed 
  319.   */ 
  320.  public function verifyNotify() 
  321.  { 
  322.   $xml = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';  
  323.   if(!$xml){ 
  324.    return false; 
  325.   } 
  326.   $wx_back = $this->xmlToArray($xml); 
  327.   if(emptyempty($wx_back)){ 
  328.    return false; 
  329.   } 
  330.   $checkSign = $this->getVerifySign($wx_back, $this->config['api_key']);  
  331.   if($checkSign=$wx_back['sign']){ 
  332.    return $wx_back; 
  333.   }else{ 
  334.    return false; 
  335.   }  
  336.  } 
  337. } 

2.创建控制器定义统一下单接口和支付后的异步回调接口:

  1. //异步通知接口 
  2.  public function notifyandroid() 
  3.  { 
  4.   $wxpayandroid = new \Wxpayandroid;  //实例化微信支付类 
  5.   $verify_result = $wxpayandroid->verifyNotify(); 
  6.   if ($verify_result['return_code']=='SUCCESS' && $verify_result['result_code']=='SUCCESS') { 
  7.     //商户订单号 
  8.     $out_trade_no = $verify_result['out_trade_no']; 
  9.     //交易号 
  10.     $trade_no  = $verify_result['transaction_id']; 
  11.     //交易状态 
  12.     $trade_status = $verify_result['result_code']; 
  13.     //支付金额 
  14.     $total_fee = $verify_result['total_fee']/100; 
  15.     //支付过期时间 
  16.     $pay_date  = $verify_result['time_end']; 
  17.     $order = new Order(); 
  18.     $ret = $order->getOrderN2($out_trade_no); //获取订单信息 
  19.     $total_amount=$ret['money']; 
  20.     if ($total_amount==$total_fee) { 
  21.      // 验证成功 修改数据库的订单状态等 $result['out_trade_no']为订单号 
  22.      //此处写自己的逻辑代码 
  23.     } 
  24.    exit('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'); 
  25.   }else{ 
  26.    exit('<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>'); 
  27.   } 
  28.  } 
  29.    
  30.  //调用统一下单接口生成预支付订单并把数据返回给APP 
  31.  public function wxpayandroid(Request $request) 
  32.  { 
  33.   $param = $request->param(); //接收值 
  34.    
  35.   $tade_no = $param['orderCode']; 
  36.   $order = new Order(); //实例化订单 
  37.   $ret = $order->getOrderN2($tade_no); //查询订单信息 
  38.   $total_fee = $ret['money']; //订单总金额 
  39.     
  40.   $wxpayandroid = new \Wxpayandroid;  //实例化微信支付类 
  41.   $res = $wxpayandroid->Weixinpayandroid($total_fee,$tade_no); //调用weixinpay方法 
  42.     
  43.  } 

封装一个支付类文件,并把配置参数放到支付类内,再定义控制器创建两个方法,这样两步就可以把手机APP微信支付搞定啦。

Tags: PHP微信支付 APP微信支付

分享到: