四时宝库

程序员的知识宝库

微信小程序的支付和微信APP的支付流程

最近在做应用商城涉及到小程序微信支付和app微信支付,所以也大概地将这方面的东西看了一个遍,整个流程梳理总结一下。

环境是:tp5 + 小程序 + App

appid 小程序的appid

appsecret 小程序appsecret

mch_id 商户号

key 商户支付密钥Key

文件位置 \extends\service\PayService
//微信支付类 
<?php
/*
*	微信支付类,可直接根据各种框架复制里面的方法直接引用!*	
*	作者 (1612666131@qq.com)
*	Date:2021-03-12
*/
namespace service;
use think\Controller;
use think\Db;
use think\db\Query;

class PayService extends Controller {
	private $appid; //小程序appid
	private $appsecret; //小程序的secret
	private $MCHID; //商户号id
	private $KEY; //商户号key
	private $url='https://api.mch.weixin.qq.com/pay/unifiedorder'; //商户号key

	public function __construct($config){
		$this->appid=$config['appid'];
		$this->appsecret=$config['appsecret'];
		$this->MCHID=$config['MCHID'];
		$this->KEY=$config['KEY'];
       
    }


    public function dopay($out_trade_no,$money,$notify_url){
        //总金额(分)
        $total_fee = intval($money*100); 
         //商品类型信息
        $body='';        
        //附加数据
        $attach=date('YmdHis');
        $out_trade_no=$out_trade_no.rand(1111,9999);

    	//构造请求参数
        $key= $this->KEY;
        $appid='wxacd6ff08cd2e7b6f';
        $mch_id=$this->MCHID;
        $device_info='WEB';
        $nonce_str=$this->randomkeys(32);
       
        $fee_type='CNY';    
        $spbill_create_ip=getIP();
        $trade_type='APP';
        $limit_pay='no_credit';     
        $sign=$this->sign($appid,$mch_id,$attach,$body,$nonce_str,$key,$out_trade_no,$total_fee,$spbill_create_ip,$notify_url,$trade_type);      
        $signs = md5($sign);    
        $signss=strtoupper($signs);     
        $transData='<xml>
                   <appid><![CDATA['.$appid.']]></appid>
                   <attach><![CDATA['.$attach.']]></attach>
                   <body><![CDATA['.$body.']]></body>
                   <mch_id><![CDATA['.$mch_id.']]></mch_id>
                   <nonce_str><![CDATA['.$nonce_str.']]></nonce_str>
                   <notify_url><![CDATA['.$notify_url.']]></notify_url>
                   <out_trade_no><![CDATA['.$out_trade_no.']]></out_trade_no>
                   <spbill_create_ip><![CDATA['.$spbill_create_ip.']]></spbill_create_ip>
                   <total_fee><![CDATA['.$total_fee.']]></total_fee>
                   <trade_type><![CDATA['.$trade_type.']]></trade_type>
                   <sign>'.$signss.'</sign>
                   </xml>';
        $data=$this->curl_post("https://api.mch.weixin.qq.com/pay/unifiedorder",$transData);  
        $sinsagin=$this->again($data,$appid,$out_trade_no,$nonce_str,$key); 
        $weixin = $this->fors($data,$appid,$nonce_str,$out_trade_no,$sinsagin);  
        return $weixin;
    }




	//发送请求
	public function curl_get_contents($url){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        //忽略证书错误信息
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        @curl_setopt($ch, CURLOPT_GET, true);
         
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $result=curl_exec($ch);
        // $result = json_decode($resp,true);
        curl_close($ch);
        return $result;
	}

	public function payOrder($order){
		

	    $total_fee =$order['money']; //支付金额	  
	    if(empty($order['money']) || empty($order['openid'])){ //一定要有用户Openid和支付金额
	        die("缺少参数!");
	    }
	    $total_fee            = $total_fee * 100; //支付金额单位是分的,所以要乘100
	    $appid                = $this->appid;
 	    $MCHID                = $this->MCHID; //商户号
	    $KEY                  =  $this->KEY; //商户key
	    $data['appid']        = $appid;  //小程序appid
	    $data['mch_id']       = $MCHID;	//商户号id
	    $data['nonce_str']    = md5($MCHID.time()); //验证的支付
	    $data['openid']       = $order['openid']; //用户openid
	    $data['body']         = '油BOSS订单【'.$order['pay_no'].'】'; //微信支付对应的商家/公司主体名
	    $data['out_trade_no'] = $order['pay_no'].rand(1111,9999); //订单号id,用于回调改订单状态
	    $data['total_fee']    = $total_fee; //支付金额,单位为分!!
	    $data['spbill_create_ip'] = '8.8.8.8'; //验证ip地址,这个不用改随意
	    $data['notify_url']       = $order['notify_url']; //微信支付成功的回调路径,要写死这个路径,记得要是小程序允许访问的路径
	    $data['trade_type']       = "JSAPI"; //小程序支付,所以是JSAPI

	    // --------------------以下这一串都不用改--------------------------------
	    ksort($data); 
	    $sign_str = $this->ToUrlParams($data);
	    $sign_str = $sign_str."&key=".$KEY;
	   // echo $sign_str;
	    $data['sign'] = strtoupper(md5($sign_str));
	    $xml = $this->arrayToXml($data);
	    $r = $this->postXmlCurl($xml,$this->url,true);
	    $result = json_decode($this->xml_to_json($r));
	    if($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS' ){
	        $sdata['appId'] = $appid;
	        $sdata['timeStamp'] = (string)time();
	        $sdata['nonceStr'] = md5(time().rand().rand().$data['openid']);
	        $sdata['package'] = "prepay_id=".$result->prepay_id;
	        $sdata['signType'] = "MD5";

	        ksort($sdata);
	        $sign_str = $this->ToUrlParams($sdata);
	        $sign_str = $sign_str."&key=".$KEY;
	        $sdata['paySign'] = strtoupper(md5($sign_str));
	        $info['code']='1';
	        $info['data']=$sdata;
	        $data['msg']='';
	        return $info;
	    }else{
	    	$info['code']='0';
	        $info['data']=array();
	        $info['msg']=$result->err_code_des;
	        return $info;
	    }
	    // -----------------------都不用改-----------------------------------------------
	}
	/**
	 * 【支付成功后回调】
	 *
	 *  by: leoyi 
	 *  Date:2018-04-08
	*/
	public function suc_call(Request $request) {
	    $data=file_get_contents('php://input');
	    
	    $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);

		if($msg['result_code'] == "SUCCESS") {
			// 支付成功这里要做的操作!
			$sql = "update ....";//可以修改订单的状态之类的
			$result = DB::update($sql);
		} 
		echo '<xml>
	      <return_code><![CDATA[SUCCESS]]></return_code>
	      <return_msg><![CDATA[OK]]></return_msg>
	    </xml>';
	}
	/**
	 * 【退款的接口】
	 *
	 * by:leoyi 
	 * Date: 2018-04-08
	*/
	private function payRefund(Request $request){
		// 具体代码后期另外写 也可以联系我私发
	}
	/*
	*	注意:以下方法都是为了方便直接调取转换格式用的方法,
	*	个人需要可以另外抽取出来放
	*==========================================
	*	以下代码不需要修改!!
	*/
	/**
	 * 用户post方法请求xml信息用的
	 * @author write by leoyi 2018-04-8
	*/
	public function postXmlCurl($xml, $url, $useCert = false, $second = 10)
	{
	    $ch = curl_init();
	    //设置超时
	    curl_setopt($ch, CURLOPT_TIMEOUT, $second);
	    curl_setopt($ch,CURLOPT_URL, $url);
	    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
	    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验2
	    //设置header
	    curl_setopt($ch, CURLOPT_HEADER, FALSE);
	    //要求结果为字符串且输出到屏幕上
	    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	    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);
	      curl_close($ch);
	      return $error;
	    }
	}
	/*
	*   用于微信支付转换认证的信息用的
	*   by:leoyi
	*   date:2018-4-8
	*/
	public function ToUrlParams($data)
	{
	  $buff = "";
	  foreach ($data as $k => $v)
	  {
	    if($k != "sign" && $v != "" && !is_array($v)){
	      $buff .= $k . "=" . $v . "&";
	    }
	  }

	  $buff = trim($buff, "&");
	  return $buff;
	}
	/*
	*   微信支付-数组转xml
	*   by:leoyi
	*   date:2018-4-8
	*/
	public 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;
	}
	/*
	*   微信支付-数组转xml
	*   by:leoyi
	*   date:2018-4-8
	*/
	public function  xml_to_json($xmlstring) {
	    return json_encode($this->xml_to_array($xmlstring),JSON_UNESCAPED_UNICODE);
	}
	/*
	*   post方法
	*   by:leoyi
	*   date:2018-4-8
	*/
	public function post_url($post_data, $url)
	{
	  $ch = curl_init();
	  //设置超时
	  curl_setopt($ch, CURLOPT_TIMEOUT, 10);

	  curl_setopt($ch,CURLOPT_URL, $url);

	  curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
	  curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验2

	  curl_setopt($ch, CURLOPT_HEADER, FALSE);

	  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);


	  curl_setopt($ch, CURLOPT_POST, TRUE);
	  curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);

	  $data = curl_exec($ch);
	  curl_close($ch);
	  return $data;
	}
	/*
	* 把xml转换成array
	* by:leoyi
	* Date:2018-4-11
	*/
	public function xml_to_array($xml) {
	    //return ((array) simplexml_load_string($xmlstring));
	  return simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA);

	    //return json_decode(xml_to_json($xmlstring));
	}


	private function sign($appid,$mch_id,$attach,$body,$nonce_str,$key,$out_trade_no,$total_fee,$spbill_create_ip,$notify_url,$trade_type)
    {
        $sign='';
        $stringSignTemp='';
        $stringSignTemp="appid=".$appid."&attach=".$attach."&body=".$body."&mch_id=".$mch_id."&nonce_str=".$nonce_str."?ify_url=".$notify_url."&out_trade_no=".$out_trade_no."&spbill_create_ip=".$spbill_create_ip."&total_fee=".$total_fee."&trade_type=".$trade_type."&key=$key";
        $sign=$stringSignTemp;
        return $sign;
    }

     private function curl_post($url,$data)
    {
        if(is_array($data)) $data=http_build_query($data);
        $curl = curl_init();
        //$url=$url;
        curl_setopt($curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER,false); // 对认证证书来源的检查
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        $result = curl_exec($curl);
        curl_close($curl);
        if($result === false){
            return curl_error($curl);
        }
        else{
            return $result;
        }
    }

     private function again($data,$appid,$out_trade_no,$nonce_str,$key)
    {        
        $res = simplexml_load_string($data,NULL,LIBXML_NOCDATA);        
        $res = json_decode(json_encode($res),true); 

        if ($res['result_code'] == "FAIL") {
        	echoJson($res['err_code_des'],"1001");         
        }
        $partnerid=$out_trade_no;
        $prepayid=$res['prepay_id'];
        $package='Sign=WXPay';
        $noncestr=$nonce_str;
        $timestamp=time();
        $sign='';
        $stringSignTemp='';
        $stringSignTemp="appid=".$appid."&noncestr=".$noncestr."&package=".$package."&partnerid=".$partnerid."&prepayid=".$prepayid."×tamp=".$timestamp."&key=$key";
        $sign=MD5($stringSignTemp);
        $signs=strtoupper($sign);
        return $signs;
    }

      private function fors($data,$appid,$nonce_str,$out_trade_no,$sinsagin)
    {
        $time=(string)time();
        $res = simplexml_load_string($data,NULL,LIBXML_NOCDATA);         
        $res = json_decode(json_encode($res),true);
        
        return array(
                'appid'=>$appid,
                'noncestr'=>$nonce_str,
                'package'=>'Sign=WXPay',
                'partnerid'=>$out_trade_no,
                'prepayid'=>$res['prepay_id'],
                'timestamp'=>$time,
                'sign'=>$sinsagin
        );
    }

    /**
     * 生成32位随机字符串  WX
     * @param unknown $length
     * @return string
     */
    private function randomkeys($length)
    {
        $key='';
        $pattern='1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMNOPQRSTUVWXYZ';
        for ($i = 0; $i < $length; $i++) {
            $key.= $pattern{mt_rand(1,60)};    //生成php随机数
        }
        return $key;
    }


}

小程序微信支付调用

use service\PayService;
<?php
/*
*	pay_no string 支付单号	
*	money  string 支付金额(单位分)
* openid 用户openID
* notify_url 支付后回调地址
*	Date:2021-03-12
*/
//微信支付
function parampay($pay_no,$money,$openid,$notify_url){     
    $config['appid']      = '';// 小程序appid
    $config['appsecret']  = '';// 小程序appsecret
    $config['MCHID']      = ''; //微信商户
    $config['KEY']        = ''; //微信商户key
    $pay = new PayService($config);
    $order['money']     = $money;
    $order['openid']    = $openid;
    $order['pay_no']    = $pay_no;
    $order['notify_url']= $notify_url;   
    return $pay->payOrder($order);
}
?>

app微信支付

use service\PayService;
<?php
/*
*	pay_no string 支付单号	
*	money  string 支付金额(单位分)
* notify_url 支付后回调地址
*	Date:2021-03-12
*/
//微信支付
function parampay($pay_no,$money,$notify_url){     
    $config['appid']      = '';// 小程序appid
    $config['appsecret']  = '';// 小程序appsecret
    $config['MCHID']      = ''; //微信商户
    $config['KEY']        = ''; //微信商户key   
    $notify_url= WEB_URL.'/api/Onilnestudy/notifyUrl';                 
    $PayService=new PayService($config); 
    return $PayService->dopay($pay_no,$money,$notify_url);
}
?>

异步回调消息处理

 public function notifyUrl(){
        $xmlData = file_get_contents('php://input');
        libxml_disable_entity_loader(true);
        $result = json_decode(json_encode(simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        ksort($result);        
        $str = '';
        foreach ($result as $k => $v) {
            $str .= $k.':'.$v."\r\n";
        }
        $fp = fopen("./wechatnotify.txt","a");
        flock($fp, LOCK_EX) ;
        fwrite($fp,"--返回微信支付数据--执行日期:".strftime("%Y-%m-%d %H:%M:%S",time())."--------------------\r\n".$str."\r\n\r\n");
        flock($fp, LOCK_UN);
        fclose($fp);
        // $result['result_code']='SUCCESS';
        // $result['out_trade_no']='650380663249821367350';        
        if($result['result_code'] == 'SUCCESS'){
            //此处应该更新一下订单状态,商户自行增删操作
            $map = [];
            $map['pay_no'] = substr($result['out_trade_no'],0,-4);
            $info = DB::name("onilne_study_buy")->where($map)->find();
            if ($info['is_status'] > 0) {
                die('success');
            }            
            if ($info) {
                $data = [
                    "is_status" => 1,
                    "pay_time" => date('Y-m-d H:i:s'),
                ];
                $re = DB::name("onilne_study_buy")->where($map)->update($data);
                if ($re) {              
                    die("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");                
                }
            }           
        }else{
            die("fail");
        }
    }

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接