The whole process of "WeChat payment" based on PHP+HTML (2021)

1. Merchants who register for WeChat Pay

 

2. Configure the path of the program in the WeChat payment background

 

3. Create order information

Access the HTML page on the WeChat browser to operate. According to the incoming user openid and amount, the returned order creation information. The code is divided into the following three components:

The first part, JS listens to the click event of the payment button, and sends it to the background to obtain the created order information, the code is as follows:

//点击支付的按钮
$("#pay").on("click", function(){
    //异步请求后台,创建订单
    $.ajax({
        type:'post',
        url:'confirm_order.php',
        async:true,
        dataType: 'json',
        data:{
            pay_fee: 100,  //元为单位,可以在前端用户自行填写,也可以直接写死
        },
        success: function(res){
            //判断订单创建是否成功res.state,1-成功,2-失败
            if (res.state == 1) {
                //以下这一部分不做修改
                jsApiParameters = {
                    "appId": res.result.jsApiParameters.appId,
                    "nonceStr": res.result.jsApiParameters.nonceStr,
                    "package": res.result.jsApiParameters.package,
                    "paySign": res.result.jsApiParameters.paySign,
                    "signType": res.result.jsApiParameters.signType,
                    "timeStamp": res.result.jsApiParameters.timeStamp,
                }
                out_trade_no = res.result.out_trade_no;
                mchid = res.result.mchid;
                //发起支付,后续文章说明
                callpay();
            }else if(res.state == 2){
                alert(data.msg);
            }else{
                alert("订单发起异常。");
            }
        },
        error: function(res){
            alert("订单发起失败。");
        }
    })
});

The second part, confirm_order.php receives the order creation request, sends further order information to the payment module program, and writes the returned order information into the database. The code is as follows:

//接收整理数据
$openId='03434343434343434343434';
$pay_fee=$_POST['pay_fee'];
$data=[
	'pay_name'=>'订单名称',
	'pay_tel'=>'订单联系电话',
	'pay_remark'=>'订单备注',
	'pay_fee'=>$pay_fee,
	'openId'=>$openId,
];
//发送订单数据
$result=curl_post('http://域名/项目/支付模块/create_order.php', $data);
//获取订单数据
$resultArr=json_decode($result, true);
$out_trade_no=$resultArr['out_trade_no'];
$mchid=$resultArr['mchid'];
$appid=$resultArr['appid'];
//数据库记录订单信息
//$sql = "insert into pay (funds_name,funds_fee,funds_tel,funds_remark,funds_openid,funds_out_trade_no,funds_mchid,funds_appid,funds_start_time,funds_state,funds_result_desc) values('".$data['pay_name']."','".($data['pay_fee'] * 100)."','".$data['pay_tel']."','".$data['pay_remark']."','$openId','$out_trade_no','$mchid','$appid',NOW(),'2','支付未完成')";
//$res = $db->execute($sql);
if(!empty($resultArr)){
	$arr['state']=1;
	$arr['result']=$resultArr;
}else{
	$arr['state']=2;
	$arr['msg']='订单创建失败';
}
echo json_encode($arr);
exit;

function curl_post($url, $data = array())
{
	$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);
	// POST数据
	curl_setopt($ch, CURLOPT_POST, 1);
	// 把post的变量加上
	curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
	$output = curl_exec($ch);
	curl_close($ch);
	return $output;
}

The third part, domain name/project/payment module/create_order.php, finally sends the order information to the WeChat interface, and obtains the result returned by the interface. First, you need to obtain and deploy the WeChat payment class.

1. Visit the WeChat Pay official website, and click on the "Development Documents" merchant or service provider at the bottom of the homepage. After entering, scroll to the bottom again and click "Development Documentation (V2 Version)". After entering, select any product and click. After entering the detailed description interface, click "SDK and DEMO download" to download the required program package.

2. Check the package php_sdk_v3.0.10, check the documentation in it, and deploy and configure according to the documentation.

(1) Modify lib/WxPay.Config.Interface.php to apply for the merchant number information, similar to this:

class WxPayConfigInterface
{
	//=======【基本信息设置】=====================================
	/**
	 * TODO: 修改这里配置为您自己申请的商户信息
	 * 微信公众号信息配置
	 * 
	 * APPID:绑定支付的APPID(必须配置,开户邮件中可查看)
	 * 
	 * MCHID:商户号(必须配置,开户邮件中可查看)
	 * 
	 */
	public function GetAppId(){
		return '12312312';
	}
	public function GetMerchantId()
	{
		return '12312313';
	}
	
	
	//=======【支付相关配置:支付成功回调地址/签名方式】===================================
	/**
	* TODO:支付回调url
	* 签名和验证签名方式, 支持md5和sha256方式
	**/
	public function GetNotifyUrl()
	{
		return '';
	}
	public function GetSignType()
	{
		return 'MD5'; 
	}

	//=======【curl代理设置】===================================
	/**
	 * TODO:这里设置代理机器,只有需要代理的时候才设置,不需要代理,请设置为0.0.0.0和0
	 * 本例程通过curl使用HTTP POST方法,此处可修改代理服务器,
	 * 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0,此时不开启代理(如有需要才设置)
	 * @var unknown_type
	 */
	public function GetProxy(&$proxyHost, &$proxyPort)
	{
		$proxyHost='0.0.0.0';
		$proxyPort=0;
	}
	

	//=======【上报信息配置】===================================
	/**
	 * TODO:接口调用上报等级,默认紧错误上报(注意:上报超时间为【1s】,上报无论成败【永不抛出异常】,
	 * 不会影响接口调用流程),开启上报之后,方便微信监控请求调用的质量,建议至少
	 * 开启错误上报。
	 * 上报等级,0.关闭上报; 1.仅错误出错上报; 2.全量上报
	 * @var int
	 */
	public function GetReportLevenl()
	{
		return '1';
	}


	//=======【商户密钥信息-需要业务方继承】===================================
	/*
	 * KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置), 请妥善保管, 避免密钥泄露
	 * 设置地址:https://pay.weixin.qq.com/index.php/account/api_cert
	 * 
	 * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置), 请妥善保管, 避免密钥泄露
	 * 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN
	 * @var string
	 */
	public function GetKey()
	{
		return 'sfdfsfddf213212312323';
	}
	public function GetAppSecret()
	{
		return 'sfdfsfddf213212312323';
	}


	//=======【证书路径设置-需要业务方继承】=====================================
	/**
	 * TODO:设置商户证书路径
	 * 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载,
	 * API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert,下载之前需要安装商户操作证书)
	 * 注意:
	 * 1.证书文件不能放在web服务器虚拟目录,应放在有访问权限控制的目录中,防止被他人下载;
	 * 2.建议将证书文件名改为复杂且不容易猜测的文件名;
	 * 3.商户服务器要做好病毒和木马防护工作,不被非法侵入者窃取证书文件。
	 * @var path
	 */
	public function GetSSLCertPath(&$sslCertPath, &$sslKeyPath)
	{
		$sslCertPath='cert.pem';
		$sslKeyPath='key.pem';
	}
}

Note: The configuration file in the demo is an interface class, and an abstract class is enough here; "MD5" must be capitalized, which is very tricky.

(2) Download the certificate and put it in the corresponding location

3. After the deployment is complete, add the main process create_order.php, and then need to access and debug step by step. There are many problems in the original demo package, mainly from the modification of the name and class of the configuration file (WxPay.Config.Interface.php) The name, and the calling method of APPID, etc., have not been changed and released... The main control program code is as follows:

require_once "lib/WxPay.Api.php";
$config = new WxPayConfigInterface();
require_once "example/WxPay.JsApiPay.php";
$tools = new JsApiPay();

//获取订单数据
$body=$_POST['pay_name'];
$pay_fee=$_POST['pay_fee'];
$pay_tel=$_POST['pay_tel'];
$pay_remark=$_POST['pay_remark'];
$openId=$_POST['openId'];
//补充订单数据
$out_trade_no=$config -> GetMerchantId().date("YmdHis");	  
$time_start=date("YmdHis");
// $time_expire=date("YmdHis", time() + 600);
//组织发送接口的数据   
$input = new WxPayUnifiedOrder();
$input->SetBody($body);  //商品描述
$input->SetAttach($pay_remark);  //附加数据
$input->SetOut_trade_no($out_trade_no);  //商户订单号
$input->SetTotal_fee($pay_fee*100);  //订单总金额,单位为分
$input->SetTime_start($time_start);  //交易起始时间
//$input->SetTime_expire($time_expire);  //交易结束时间
$input->SetGoods_tag("test");  //订单优惠标记
$input->SetNotify_url("http://域名/redcross/项目/notify.php");  //通知地址
$input->SetTrade_type("JSAPI");  //交易类型
$input->SetOpenid($openId);  //用户标识
//var_dump($input);
//发送创建订单请求
$order = WxPayApi::unifiedOrder($config, $input);
$jsApiParameters = $tools->GetJsApiParameters($order);
//获取创建订单数据
$arr['jsApiParameters']=json_decode($jsApiParameters,true);
$arr['out_trade_no']=$out_trade_no;
$arr['mchid']=$config -> GetMerchantId();
$arr['appid']=$config -> GetAppId();
echo json_encode($arr);
exit;

Here is a record of another type of bug, which comes from the inspection of https, and the error is curl, and the error code is 60. The solution found on the Internet is as follows:

//找到
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
//替换为		
if(stripos($url,"https://")!==FALSE){
	curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
}else{
	curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
	curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
} 

When doing it for the first time here, you need to leave a little more time and debug it step by step.

4. After the order is successfully created, initiate payment

Add the following code on the page, and you can click to initiate payment. code show as below:

//发起支付		
function callpay() {
	if (typeof WeixinJSBridge == "undefined") {
		if (document.addEventListener) {
			document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
		} else if (document.attachEvent) {
			document.attachEvent('WeixinJSBridgeReady', jsApiCall);
			document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
		}
	} else {
		jsApiCall();
	}
}
//调用微信JS api 支付
function jsApiCall() {
	WeixinJSBridge.invoke(
		'getBrandWCPayRequest',
		jsApiParameters,
		function(res) {
			if(res.err_msg=='get_brand_wcpay_request:cancel'){
				alert('您已取消支付。');
			//支付完成,跳转入支付结果php
			}else if(res.err_msg=='get_brand_wcpay_request:ok'){
				location.href = 'pay_result.php?out_trade_no=' + out_trade_no + '&mchid=' + mchid;
			}else{
				alert('支付出现异常。');
			}
		}
	);
}		

In this way, you can click to pay on the page and test it.

5. After the payment is completed, verify that the order is consistent with the configured return address, and update the database record

After the payment is completed, WeChat enters the configured return address nodify.php, the code is as follows:

//获取接口数据,如果$_REQUEST拿不到数据,则使用file_get_contents函数获取
$post = $_REQUEST;
if ($post == null) {
	$post = file_get_contents("php://input");
}
if ($post == null) {
	$post = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
}
if (empty($post) || $post == null || $post == '') {
	//阻止微信接口反复回调接口  文档地址 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_7&index=7,下面这句非常重要!!!
	$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';  
	echo $str;
	exit('Notify 非法回调');
}
/*****************微信回调返回数据样例*******************
 $post = '<xml>
	<return_code><![CDATA[SUCCESS]]></return_code>
	<return_msg><![CDATA[OK]]></return_msg>
	<appid><![CDATA[wx2421b1c4370ec43b]]></appid>
	<mch_id><![CDATA[10000100]]></mch_id>
	<nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
	<sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
	<result_code><![CDATA[SUCCESS]]></result_code>
	<prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
	<trade_type><![CDATA[APP]]></trade_type>
	</xml>';
	*************************微信回调返回*****************/

libxml_disable_entity_loader(true); //禁止引用外部xml实体
$xml = simplexml_load_string($post, 'SimpleXMLElement', LIBXML_NOCDATA);//XML转数组
$post_data = (array)$xml;
/** 解析出来的数组
	*Array
	* (
	* [appid] => 45465465456454
	* [bank_type] => CFT
	* [cash_fee] => 100
	* [fee_type] => CNY
	* [is_subscribe] => N
	* [mch_id] => 1297210301
	* [nonce_str] => 151351513
	* [openid] => 1215315351
	* [out_trade_no] => 12132131
	* [result_code] => SUCCESS
	* [return_code] => SUCCESS
	* [sign] => F6890323B0A6A3765510D152D9420EAC
	* [time_end] => 20200626170839
	* [total_fee] => 100
	* [trade_type] => JSAPI
	* [transaction_id] => 1541515315151513
	* )
**/
//订单号
$out_trade_no = isset($post_data['out_trade_no']) && !empty($post_data['out_trade_no']) ? $post_data['out_trade_no'] : 0;
$mch_id=$post_data['mch_id'];
if($post_data['return_code']=="SUCCESS" && $post_data['result_code']=="SUCCESS"){
	$err_code_des='支付成功';
	$result_code=$post_data['result_code'];
}else{
	$err_code_des='支付结果异常';
	$result_code=$post_data['result_code'];
}
//查询订单信息
//$sql = "select * from pay where funds_out_trade_no='$out_trade_no'  ";
//$order_info = $db->getAll($sql);
$order_info=[1,2,3]; //测试使用,上线前删掉
if(count($order_info) > 0){
	//平台支付key
	$wxpay_key = '1231231231231231231231'; //这里需要人工配置
	//接收到的签名
	$post_sign = $post_data['sign'];
	unset($post_data['sign']);
	//重新生成签名
	$newSign = MakeSign($post_data,$wxpay_key);
	//签名统一,则更新数据库
	if($post_sign == $newSign){
		if ($result_code == 'SUCCESS') {
			$funds_state = 1;
		} else if ($result_code == 'REFUND') {
			$funds_state = 3;
		} else {
			$funds_state = 2;
		}
		//调试代码,上线后删除
		$myfile = fopen("newfile.txt", "w");
		$txt = $funds_state."\n";
		fwrite($myfile, $txt);
		$txt = $out_trade_no."\n";
		fwrite($myfile, $txt);
		fclose($myfile);
		//数据库状态更新
		//$sql = "update pay set funds_state='".$funds_state."',funds_result_desc='".$err_code_des."',funds_complete_time=NOW() where funds_out_trade_no='".$out_trade_no."' and funds_mchid='".$mch_id."' ";
		//$res = $db[0]->execute($sql);
	}
}
//阻止微信接口反复回调接口  文档地址 https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_7&index=7,下面这句非常重要!!!
$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';  
echo $str;
exit;
		
function MakeSign($params,$key){
	//签名步骤一:按字典序排序数组参数
	ksort($params);
	$string = ToUrlParams($params);  //参数进行拼接key=value&k=v
	//签名步骤二:在string后加入KEY
	$string = $string . "&key=".$key;
	//签名步骤三:MD5加密
	$string = md5($string);
	//签名步骤四:所有字符转为大写
	$result = strtoupper($string);
	return $result;
}
				
function ToUrlParams( $params ){
	$string = '';
	if( !empty($params) ){
		$array = array();
		foreach( $params as $key => $value ){
			$array[] = $key.'='.$value;
		}
		$string = implode("&",$array);
	}
	return $string;
}

6. After the front-end operation is completed, because WeChat returns the payment result asynchronously, it actively inquires and prompts the user

The first part, pay_result.php obtains merchant and order information, and sends a request to the payment module. code show as below:

//获取订单与商户信息
$out_trade_no=$_GET['out_trade_no'];
$mchid=$_GET['mchid'];
//发送至支付模块查询,并返回
$data=[
	'out_trade_no'=>$out_trade_no,
];
$result=curl_post('http://域名/支付模块/check_order.php', $data);
$result=json_decode($result,true);
if($result['result_code'] == 'SUCCESS')
{
	echo("您已缴费成功");
}else{
	echo("已操作缴费,等待缴费确认。");
}
exit;
function curl_post($url, $data = array())
{
	$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);
	// POST数据
	curl_setopt($ch, CURLOPT_POST, 1);
	// 把post的变量加上
	curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
	$output = curl_exec($ch);
	curl_close($ch);
	return $output;
}

In the second part, check_order.php is added to the payment module, and the payment result is queried and returned.

require_once "lib/WxPay.Api.php";
$config = new WxPayConfigInterface();

$out_trade_no=$_POST['out_trade_no'];
$input = new WxPayUnifiedOrder();
$input->SetOut_trade_no($out_trade_no);  //商户订单号
$order = WxPayApi::orderQuery($config, $input);
echo json_encode($order);
exit;

So far, the WeChat web version payment function has been realized. A test demo has been formed based on the above code, and the operation is confirmed to be correct (2021-06-17).

 

 

 

 

Guess you like

Origin blog.csdn.net/HoD_DoH/article/details/118081237