PHP 微信支付

欢迎使用微信支付!
微信支付API共四份(证书pkcs12格式、证书pem格式、证书密钥pem格式、CA证书),为接口中强制要求时需携带的证书文件。
证书属于敏感信息,请妥善保管不要泄露和被他人复制。
不同开发语言下的证书格式不同,以下为说明指引:
    证书pkcs12格式(apiclient_cert.p12)
        包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份
        部分安全性要求较高的API需要使用该证书来确认您的调用身份
        windows上可以直接双击导入系统,导入过程中会提示输入证书密码,证书密码默认为您的商户ID(如:10010000)
    证书pem格式(apiclient_cert.pem)
        从apiclient_cert.p12中导出证书部分的文件,为pem格式,请妥善保管不要泄漏和被他人复制
        部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
        您也可以使用openssl命令来自己导出:openssl pkcs12 -clcerts -nokeys -in apiclient_cert.p12 -out apiclient_cert.pem
    证书密钥pem格式(apiclient_key.pem)
        从apiclient_cert.p12中导出密钥部分的文件,为pem格式
        部分开发语言和环境,不能直接使用p12文件,而需要使用pem,所以为了方便您使用,已为您直接提供
        您也可以使用openssl命令来自己导出:openssl pkcs12 -nocerts -in apiclient_cert.p12 -out apiclient_key.pem
    CA证书(rootca.pem)
        微信支付api服务器上也部署了证明微信支付身份的服务器证书,您在使用api进行调用时也需要验证所调用服务器及域名的真实性
        该文件为签署微信支付证书的权威机构的根证书,可以用来验证微信支付服务器证书的真实性
        某些环境和工具已经内置了若干权威机构的根证书,无需引用该证书也可以正常进行验证,这里提供给您在未内置所必须根证书的环境中载入使用

商户接入微信支付,调用API必须遵循以下规则:

传输方式为保证交易安全性,采用HTTPS传输
提交方式采用POST方法提交
数据格式提交和返回数据都为XML格式,根节点名为xml
字符编码统一采用UTF-8字符编码
签名算法MD5,后续会兼容SHA1、SHA256、HMAC等。
签名要求请求和接收数据均需要校验签名,详细方法请参考安全规范-签名算法
证书要求调用申请退款、撤销订单接口需要商户证书
判断逻辑先判断协议字段返回,再判断业务返回,最后判断交易状态

 

请求参数

字段名变量名必填类型示例值描述
公众账号IDappidString(32)wxd678efh567hg6787微信支付分配的公众账号ID(企业号corpid即为此appId)
商户号mch_idString(32)1230000109微信支付分配的商户号
随机字符串nonce_strString(32)5K8264ILTKCH16CQ2502SI8ZNMTM67VS随机字符串,长度要求在32位以内。推荐随机数生成算法
签名signString(32)C380BEC2BFD727A4B6845133519F3AD6通过签名算法计算得出的签名值,详见签名生成算法
商品描述bodyString(128)腾讯充值中心-QQ会员充值

商品简单描述,该字段请按照规范传递,具体请见参数规定

商户订单号out_trade_noString(32)20150806125346商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一。详见商户订单号
标价金额total_feeInt88订单总金额,单位为分,详见支付金额
终端IPspbill_create_ipString(16)123.12.12.123APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
通知地址notify_urlString(256)http://www.weixin.qq.com/wxpay/pay.php异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
交易类型trade_typeString(16)JSAPI

JSAPI 公众号支付

NATIVE 扫码支付

APP APP支付

说明详见参数规定

用户标识openidString(128)oUpF8uMuAJO_M2pxb1Q9zNjWeS6otrade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。openid如何获取,可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换

以下代码只是对微信支付进行了简单分析,代码不可直接拿来使用,只做参考 ,这个是公众号支付,trade_type=JSAPI时(即公众号支付),此参数openid必传

     /*发起微信公众号支付订单*/
     private function SubmitWxpaysend($subject,$OpenId,$OrderId,$PayMoney,$NotifyPage)          
     {
            //时间设置
            ini_set('date.timezone','Asia/Shanghai');
            //引入微信支付类文件
            require_once  "WxPay.Api.php";
            require_once  "WxPay.JsApiPay.php";
            //统一下单
            $input = new \WxPayUnifiedOrder();
            $input->SetBody("商品描述");
            $input->SetAttach("附加数据");
            $input->SetOut_trade_no($OrderId); //商户订单号
            $input->SetTotal_fee($PayMoney);  //订单总金额,单位为分
            $input->SetTime_start(date("YmdHis")); //订单生成时间,格式为yyyyMMddHHmmss
            $input->SetTime_expire(date("YmdHis", time() + 600)); //订单失效时间
            $input->SetGoods_tag('余额充值'); //订单优惠标记
            $input->SetNotify_url("weixin.com/web/admin/api/pay/".$NotifyPage); //设置回调地址
            $input->SetTrade_type("JSAPI"); //交易类型 JSAPI 公众号支付
            $input->SetOpenid($openId); //此参数为微信用户在商户对应appid下的唯一标识
            $input->SetAppid(WxPayConfig::APPID);//公众账号ID
            $input->SetMch_id(WxPayConfig::MCHID);//商户号
            $input->SetSpbill_create_ip($_SERVER['REMOTE_ADDR']);//终端ip	  	    
            $input->SetNonce_str(self::getNonceStr());//随机字符串
            $input->SetSign();//签名
            //以上的方法是将值保存到$values这个属性的数组里面
            //数组转换为XML
            $xml = $input->ToXml();//(这个方法是去拿$values的值,并且转换为XML返回)

            //向统一下单接口发起请求
            $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//统一下单
            $response = self::postXmlCurl($xml, $url, false,$timeOut=6);
            //将xml转为array
            $result = WxPayResults::Init($response);
            $tools = new \JsApiPay();
            //获取jsapi支付的参数
            $jsApiParameters =$tools->GetJsApiParameters($order);
            return $jsApiParameters;

  }
 

     //随机字符串函数
     public static function getNonceStr($length = 32) 
     {
	     $chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
	     $str ="";
           for ( $i = 0; $i < $length; $i++ ) {  
		    $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);  
		  } 
		 return $str;
	}

            //签名生成函数
            public function SetSign()
            {
		      //签名步骤一:按字典序排序参数
		       ksort($this->values);	
               //格式化参数格式化成url参数
		        $buff = "";
		      foreach ($this->values as $k => $v)
		         {
			        if($k != "sign" && $v != "" && !is_array($v)){
				      $buff .= $k . "=" . $v . "&";
			         }
		        }
		        $buff = trim($buff,"&");	
		        $string = $buff;
		        //签名步骤二:在string后加入KEY
		        $string = $string . "&key=".WxPayConfig::KEY;
		        //签名步骤三:MD5加密
		        $string = md5($string);
		        //签名步骤四:所有字符转为大写
		        $result = strtoupper($string);
		        $this->values['sign'] = $result;
		        return $sign;
	         }

          
    //数组转XML
	public function ToXml()
	{
		if(!is_array($this->values)||count($this->values) <= 0){
    		throw new WxPayException("数组数据异常!");
    	    } 	
    	  $xml = "<xml>";
    	  foreach ($this->values as $key=>$val){
    		 if (is_numeric($val)){
    			 $xml.="<".$key.">".$val."</".$key.">";
    		 }else{
    			 $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
    		 }
         }
          $xml.="</xml>";
          return $xml; 
   }  
 
	/**
	 * 以post方式提交xml到对应的接口url
	 * 
	 * @param string $xml  需要post的xml数据
	 * @param string $url  url
	 * @param bool $useCert 是否需要证书,默认不需要
	 * @param int $second   url执行超时时间,默认30s
	 * @throws WxPayException
	 */
     private static function postXmlCurl($xml, $url, $useCert = false, $second = 30)
	   {		
		$ch = curl_init();
		//设置超时
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);
		
		//如果有配置代理这里就设置代理
		if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0" 
			&& WxPayConfig::CURL_PROXY_PORT != 0){
			curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
			curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
		}
		curl_setopt($ch,CURLOPT_URL, $url);
		curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
		curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
		//设置header
		curl_setopt($ch, CURLOPT_HEADER, FALSE);
		//要求结果为字符串且输出到屏幕上
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	
		if($useCert == true){
			//设置证书
			//使用证书:cert 与 key 分别属于两个.pem文件
			$certpath="cert/apiclient_cert.pem"; //证书路径
			$certkeypath="cert/apiclient_key.pem"; //证书路径
			curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
			curl_setopt($ch,CURLOPT_SSLCERT, $certpath);
			curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
			curl_setopt($ch,CURLOPT_SSLKEY, $certkeypath);
		}
		//post提交方式
		curl_setopt($ch, CURLOPT_POST, TRUE);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
		//运行curl
		$data = curl_exec($ch);
		api_functions::writeLog("postXmlCurl:".$data);
		//返回结果
		if($data){
			curl_close($ch);
			return $data;
		} else { 
			$error = curl_errno($ch);
			curl_close($ch);
			throw new WxPayException("curl出错,错误码:$error");
		}
	}

     /**
     * 将xml转为array
     * @param string $xml
     * @throws WxPayException
     */
	public static function Init($xml)
	{	
		$obj = new self();
		$obj->FromXml($xml);
		//fix bug 2015-06-29
		if($obj->values['return_code'] != 'SUCCESS'){
			 return $obj->GetValues();
		}
		 $obj->CheckSign();
         return $obj->GetValues();
	  }
   }

	/**
	 * 
	 * 获取jsapi支付的参数
	 * @param array $UnifiedOrderResult 统一支付接口返回的数据
	 * @throws WxPayException
	 * 
	 * @return json数据,可直接填入js函数作为参数
	 */
	public function GetJsApiParameters($UnifiedOrderResult)
	{
		if(!array_key_exists("appid", $UnifiedOrderResult)
		|| !array_key_exists("prepay_id", $UnifiedOrderResult)
		|| $UnifiedOrderResult['prepay_id'] == "")
		{
			throw new WxPayException("参数错误");
		}
		$jsapi = new WxPayJsApiPay();
		$jsapi->SetAppid($UnifiedOrderResult["appid"]);
		$timeStamp = time();
		$jsapi->SetTimeStamp("$timeStamp");
		$jsapi->SetNonceStr(WxPayApi::getNonceStr());
		$jsapi->SetPackage("prepay_id=" . $UnifiedOrderResult['prepay_id']);
		$jsapi->SetSignType("MD5");
		$jsapi->SetPaySign($jsapi->MakeSign());
		$parameters = json_encode($jsapi->GetValues());
		return $parameters;
	}    

下面对上面代码做过总结,这里是app支付

    /*发起APP微信支付订单*/
    private function SubmitWxpaysend($body,$subject,$UserId,$OrderId,$PayMoney,$PayType,$NotifyPage)
    {
        try
        {
            $appid=$this->wx_appid;
            $mch_id=$this->wx_mch_id;
            $wxKEY=$this->wx_wxKEY;
    
            header("Content-type: text/html; charset=utf-8");
    
            $order = $OrderId;
            $noceStr = md5(rand(100,1000).time());//获取随机字符串
            $time = time();
            $paramarr = array(
                "appid"       =>    $appid,//公众账号ID
                "body"        =>    $subject,//商品描述
                "mch_id"      =>    $mch_id, //商户号
                "nonce_str"   =>    $noceStr,//随机字符串
                "notify_url"  =>    "/web/admin/api/web/".$NotifyPage,//回调地址
                "out_trade_no"=>    $order, //商户订单号
                "spbill_create_ip"=>$_SERVER["REMOTE_ADDR"],//APP和网页支付提交用户端ip
                "total_fee"   =>    ($PayMoney)*100,//订单总金额,单位为分
                "trade_type"  =>    "APP"//APP APP支付 
            );
            //字符串拼接,生成签名
            ksort($paramarr);	
            $sign = "";
            foreach($paramarr as $k => $v){
                $sign .= $k."=".$v."&";
             }

            $sign .= "key=".$wxKEY;
            $sign = strtoupper(md5($sign));
            $paramarr['sign'] = $sign;
            //数组转XML
            $paramXml = "<xml>";
            foreach($paramarr as $k => $v){
                $paramXml .= "<" . $k . ">" . $v . "</" . $k . ">";
            }
            $paramXml .= "</xml>";
    
    
            $ch = curl_init ();
            @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
            @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);  // 从证书中检查SSL加密算法是否存在
            @curl_setopt($ch, CURLOPT_URL, "https://api.mch.weixin.qq.com/pay/unifiedorder");
            @curl_setopt($ch, CURLOPT_HEADER, FALSE);
            @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            @curl_setopt($ch, CURLOPT_POST, 1);
            @curl_setopt($ch, CURLOPT_POSTFIELDS, $paramXml);
            @$resultXmlStr = curl_exec($ch);
           
            curl_close($ch);

            //XML转数组
            $msg = array();
            $postStr =$resultXmlStr;
            $msg = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);    
            $time2 = time();
            $prepayid = $result['prepay_id'];
            $sign = "";
            $noceStr = md5(rand(100,1000).time());//获取随机字符串
            $paramarr2 = array(
                "appid"     =>  $appid,
                "noncestr"  =>  $noceStr,
                "package"   =>  "Sign=WXPay",
                "partnerid" =>  $mch_id,
                "prepayid"  =>  $prepayid,
                "timestamp" =>  $time2
            );
             //字符串拼接,生成签名
                ksort($paramarr2);	
                $sign = "";
                foreach($paramarr2 as $k => $v){
                $sign .= $k."=".$v."&";
                }
                $sign .= "key=".$wxKEY;
                $sign = strtoupper(md5($sign));
                echo json_encode($paramarr2);
    
        }
        catch (Exception $ex)
        {
            echo $ex->getMessage();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值