php微信支付

基于php的微信H5、APP支付

1.准备工作

企业需要登录微信商户平台申请商户号,申请商户号时需要绑定微信成为商户平台的超级管理员,审核通过后可以使用微信扫码登录微信商户平台,在商户平台-产品中心可以看到H5支付、APP支付、JSAPI支付等,点击对应的开通。然后需要在产品中心-APPID授权管理查看是否已经绑定对应的app_id(H5、APP支付对应的app_id是微信开放平台申请的应用id),没绑定需要绑定。参考:
http://kf.qq.com/faq/180910QZzmaE180910vQJfIB.html

2.支付大概流程

2.1用户选择好商品,选择微信支付,前端发送ajax请求到后端,后端接收到订单并检查订单是否存在,之后组装参数请求微信统一下单接口,H5跟JSAPI支付的区别在于H5针对的是非微信浏览器打开的WAP应用,注意不是电脑网站,电脑网站使用native扫码支付。H5跟APP支付参数略有不同。
微信H5支付的一个大坑:微信H5支付参数的app_id按照官方文档写的,是要公众平台去申请,也就是必须要申请公众号,本人就此咨询邮件过微信支付技术,他们也是这么说,但我们公司没有申请公众号,只在开发平台申请了APP应用,因为要做APP支付,本人APP支付跟H5支付用的同一个app_id,亲测可行
以下0代表H5支付,1代表APP支付,示例代码:

$time = time();
$input['appid'] = $wxapp_id; //开发平台申请的APP应用id
$input['mch_id'] = $mch_id; //商户号
$input['nonce_str'] = md5(uniqid(mt_rand(),true));//随机数
$input['body'] = "开发平台申请的APP的名字";
$input['out_trade_no'] = $order_num;
$input['total_fee'] = $order_price * 100;
$input['time_start'] = date('YmdHis',$time);
$input['notify_url'] = API_URL.'/cloud/wxpay?method=notify'; //异步回调地址
if($pay_type==0)  //H5支付
{
    $input['trade_type'] = 'MWEB'; //H5支付类型
    $input['spbill_create_ip'] = $obj->get_client_ip();
    $scene_info = ['h5_info'=>['type'=>'Wap','wap_url'=>API_URL,'wap_name'=>'JUAN']];
    $input['scene_info'] = json_encode($scene_info);
}else if($pay_type==1) //APP支付
{
    $input['trade_type'] = 'APP';
}

获取客户端ip的函数,主要用于服务器有代理:

function get_client_ip() {
	    if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
	        $ip = getenv('HTTP_CLIENT_IP');
	    } elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
	        $ip = getenv('HTTP_X_FORWARDED_FOR');
	    } elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
	        $ip = getenv('REMOTE_ADDR');
	    } elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
	        $ip = $_SERVER['REMOTE_ADDR'];
	    }
    	return preg_match ( '/[\d\.]{7,15}/', $ip, $matches ) ? $matches [0] : '';
	}

2.2生成签名,下面的config变量就是前面拿到的input,$key是商户平台设置的32位密钥,可以登陆商户平台设置,代码如下:

 /**
	 * 生成签名
	 * @param  $config  配置对象
	 * @param  $sign_type 加密类型
	 * @param  $key 商户秘钥
	 */
	public function MakeSign($config, $sign_type = 'MD5', $key)
	{
		//签名步骤一:按字典序排序参数
		ksort($config);
		$string = "";
		foreach ($config as $k => $v)
		{
			if($k != "sign" && $v != "" && !is_array($v)){
				$string .= $k . "=" . $v . "&";
			}
		}
		
		$string = trim($string, "&");
		//签名步骤二:在string后加入KEY
		$string = $string . "&key=".$key;
		//签名步骤三:MD5加密或者HMAC-SHA256
		if($sign_type == "MD5"){
			$string = md5($string);
		} else if($sign_type == "HMAC-SHA256") {
			$string = hash_hmac("sha256",$string ,$key);
		} else {
			throw new WxPayException("签名类型不支持!");
		}
		
		//签名步骤四:所有字符转为大写
		$result = strtoupper($string);
		return $result;
	}

2.3 把生成的签名放入原数组中:

$input['sign'] = $sign;

2.4 请求统一下单接口,H5支付需要返回值中的mweb_url,这个是预支付的链接,返回给前端,前端做一个A连接,把mweb_url赋值给A连接,用户点击A连接即可跳到微信支付页,或者直接用js跳转也可以。支付完成或取消支付默认回到发起支付的页面,如果需要跳转到指定页面,可以在mweb_url后面加上redirect_url参数,参考以下页面的回调地址说明:

https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4
APP支付主要返回预支付prepare_id等参数给APP开发,调起微信支付也是由他们做,后端主要负责下单接口,查询订单接口以及异步回调。一定要注意参数的名字一定要跟微信开发文档规定的一致,微信的几个API同样的参数会有命名不一致等问题,APP需要返回以下参数给APP开发者,
在这里插入图片描述

可以看到这里的prepayid在前面是这样的prepare_id,一定要注意,否则会出现APP端无法调起支付页面。
请求微信下单接口需要传递XML格式数据,不能用数组,以下是转xml:

/**
	 * 输出xml字符
	 * @throws WxPayException
	**/
	public function ToXml($data)
	{
		if(!is_array($data) || count($data) <= 0)
		{
    		return false;
    	}
    	
    	$xml = "<xml>";
    	foreach ($data as $key=>$val)
    	{
    		if (is_numeric($val)){
    			$xml.="<".$key.">".$val."</".$key.">";
    		}else{
    			$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
    		}
        }
        $xml.="</xml>";
        return $xml; 
	}

下面是请求统一下单接口示例代码:

$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信传参地址
$output = http_post($url,$xml);
if (is_array($output)) 
{
    $error = new EseeApiError(3004);
    $error->addExtensionInfo('info','curl execute fail'.$output['errMsg']);
    $error->outputError();  
}
$arr = json_decode(json_encode(simplexml_load_string($output, 'SimpleXMLElement', LIBXML_NOCDATA)),true);
if ($pay_type == 1 && $arr['return_code'] == 'SUCCESS' && $arr['result_code'] == 'SUCCESS') 
{
    //APP支付需要返回prepay_id以及生成签名到前端,H5主要返回mweb_url调起支付
    $return['prepayid'] = $arr['prepay_id'];
    $return['appid'] = $wxapp_id;
    $return['partnerid'] = $mch_id;
    $return['package'] = 'Sign=WXPay';
    $return['noncestr'] = md5(uniqid(mt_rand(),true));
    $return['timestamp'] = time();//这里传时间戳
    $sign = $obj->SetSign($return,$sign_type,$key);
    $return['sign'] = $sign;
    exit(json_encode($return));
    //$output = $obj->ToXml($return);

}
exit(json_encode($arr));

http_post函数代码如下:

//发送http请求
	public function http_post($url,$data,$second =30)
	{
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_TIMEOUT, $second);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
		$output = curl_exec($ch);
		if(empty($output))
		{
			$errno = curl_errno($ch);
			$errMsg = curl_error($ch);
			curl_close($ch);
			return ['errno'=>$errno,'errMsg'=>$errMsg];
		}
		curl_close($ch);
		return $output;
	}

到这里下单接口要做的已经完成。

2.5 查询订单接口
查询订单接口需要传递订单号,先检测订单是否存在,之后组装参数:

$data['mch_id'] = $resultObj->mch_id;
$data['out_trade_no'] = $resultObj->order_num;
$data['nonce_str'] = md5(uniqid(mt_rand(),true)); //随机数
$data['appid'] = $resultObj->wechat_open_apikey; //应用id

跟之前一样生成签名,然后放进$data中,然后数组转xml格式这里不写了。
请求订单查询接口,代码如下:

$url = "https://api.mch.weixin.qq.com/pay/orderquery";
$xml = toxml($data);
$output = http_post($url,$xml);
if (is_array($output)) 
{
    $error = new EseeApiError(3004);
    $error->addExtensionInfo('info','curl execute fail' .$output['errno'] . $output['errMsg']);
    $error->outputError();
}
exit(json_encode(simplexml_load_string($output, 'SimpleXMLElement', LIBXML_NOCDATA)));

返回给前端或者APP开发即可。

2.6异步回调
主要是接收微信异步传递的数据,判断支付金额,订单号,签名是否匹配,之后处理业务,处理成功后需要输出一段XML,不然微信服务器会一直回调(最多10次)
在这里插入图片描述

代码如下:
接收参数

if(!array_key_exists('out_trade_no',$_POST)
    || !array_key_exists('total_fee',$_POST)
    || !array_key_exists('sign',$_POST)
    || !array_key_exists('mch_id',$_POST)
    || !array_key_exists('appid',$_POST)
    || !array_key_exists('transaction_id',$_POST)
    || !array_key_exists('nonce_str',$_POST)
    || !array_key_exists('time_end',$_POST)
    || !array_key_exists('return_code',$_POST))
{
    exit;
}

判断支付状态:

$order_num = $_POST['out_trade_no'];
$wx_app_id = $_POST['appid'];
$mch_id = $_POST['mch_id'];
$total_fee = $_POST['total_fee'];
$sign = $_POST['sign'];
$trade_type = $_POST['trade_type'];
$return_code = $_POST['return_code'];
$result_code = $_POST['result_code'];
$time_end = strtotime($_POST['time_end']); //支付完成时间
$transaction_id = $_POST['transaction_id'];//微信返回的订单号
if ($return_code != 'SUCCESS' || $result_code !='SUCCESS')
{
    exit;
}

查询数据库的订单的金额,商户号,app_id等与返回的是否匹配,之后把传递来的数据生成签名与返回值里面的签名比较看是否一致,主要是确认回调来自微信服务器,之后处理订单状态,记录支付日志等,这里补贴代码了。
最后别忘记在代码最后输出xml,不然微信会一直请求异步回调接口:

$xml = "<xml> 
            <return_code><![CDATA[SUCCESS]]></return_code>
            <return_msg><![CDATA[OK]]></return_msg>
</xml>";
echo $xml;

微信支付常见报错如下:
https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值