仔细看了一遍官方的那幅流程图,我来简化理解一下
(注意:我这里针对的是扫码支付模式一,模式二没什么说的)
网站后台生成二维码,当然是跟据前台传来的参数有条件的生成
买家扫描二维码,扫描过程中,微信后台系统回调我们预先设定的url地址
(栗如:http://xx.com/a.php)
在回调php文件中设置商品价格,商品信息,支付结果异步通知地址(比如:http://xx.com/b.php)等,
然后发送给微信系统
微信系统根据我们发送的信息 返回一个预支付id给我们
我们将收到的数据原封不动返回去(这一步我真的无力吐槽了,完全是凭
感觉猜的,文档写的是返回预支付id,我试了只返回id提示错误)
此时买家扫描界面已经出现支付界面了,买家支付后,微信系统将把付款结果发送到上面我们设置的异步通知url上
然后我们就可以在异步url上完成我们自己的业务,比如会员充值成功,我们在数据库对应的添加金币
需要注意的是收到异步通知后一定要返回一个success回去,告诉微信系统我们已经收到通知,否则微信将多次发送异步通知
有时候由于网络原因可能你返回了成功但是也有可能收到多次通知,所以你在为会员添加金币的时候一定要根据订单号添加日志
在接收异步通知的时候首先去日志表看看订单号是否已经存

============================================================================

枯草的说明结束了 然后我们用代码来完整走一遍

首先我们来一个这样的前端页面,当然了,正式环境下怎么可能让用户来填写会员id呢,必须把id字段隐藏了,根据当前登陆的用户自动确定

代码如下:

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>微信支付测试</title>
<script src="jquery.min.js" ></script>
<script src="jquery.qrcode.min.js" ></script>
</head>
<body>
<div>
金额:<input type="text" id="price"><br>
用户userid: <input type="text" id="userid" ><br>
<button onclick="getUrl()">支付</button>
<hr>
<div id="code"></div>
</div>
</body>
</html> <script>
function getUrl()
{
var url = "http://xxxx.com/xxx/action.php";
$.post(url,{'price':$("#price").val()*100,'userid':$("#userid").val()},function(re_url){
createQR(re_url);
})
}
function createQR(url)
{
$("#code").children().remove();
$("#code").qrcode({
// render: "table",
width: 200, //宽度
height:200, //高度
text: url
});
}
</script>

当用户输入金额和会员id后,点击支付,然后用ajax去请求action.php这个文件,这个文件负责返回一个二维码的url地址

前台接到url后使用query.qrcode.min.js 这个库生成二维码,

下面看看action.php完整代码:

 //接收两个参数  用addslashes过滤了一下数据
$p = addslashes($_POST['price']); //充值金额
$u = addslashes($_POST['userid']); //充值的用户(网站会员id) $tmpArr = array(
'appid'=>'xxxxxxxxxxxxxxxxxx', //不要填成了 公众号原始id
'mch_id'=>'xxxxxxxxxx',
'nonce_str'=>'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'time_stamp'=>time(),
'product_id'=>$p."s".$u,
);
// 生成签名需要上面五个参数,文档上没有,不要问我是怎么知道的,我只知道二维码格式是这样的:
// weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&
// product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX
// 还有就是看一下他的sdk也能看到他是怎么生成签名的,用了那些参数。
// 有好几个坑我都是通过看sdk明白的,所以你有必要看一下他的sdk // 注意:上面五个参数是固定的 这个地方不可以自己加额外参数 否则报 原生url参数错误
// 但是如果不传参数我的业务逻辑怎么做呢 我这里用了一个比较巧妙的方法,没错,我们可以在产品编号product_id上
// 做文章,看我这里产品编号是 价格+当前网站用户userid组成的字符串,用
// 字符s分割,方便后面我们拆开,文档中说明了在用户扫描后只会返回openid和
// product_id给回调地址,再次证明你即便在上面新增额外参数也没有任何意义。我们完全可以
// 把需要的参数组装成字符串,然后用product_id来传递 ksort($tmpArr); //根据键值排序数组
// 把数组转换成这种格式:appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA
$buff = "";
foreach ($tmpArr as $k => $v)
{
$buff .= $k . "=" . $v . "&";
}
$buff = trim($buff, "&");
// 这个地方有的人可能会想到 http_build_query 函数直接了当,干净利索
// 我刚开始就是用的这个函数,坑了老半天。。。 意外发现生成的字符串里面居然有几个字节的乱码
// 乍一看完全和上面生成的一样,各位可以尝试一下
// 这些步骤官方文档还是有的 不多说
$stringSignTemp=$buff."&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$sign= strtoupper(MD5($stringSignTemp)); // 生成的二维码url 到这里就可以返回给前台 前端使用 jquery.qrcode.min.js 这个库可以生成二维码了
// 我试了一下 url太长 生成的二维码太复杂 像素差的手机就悲哀了,接着往下看
$reurl = "weixin://wxpay/bizpayurl?appid=xxxxxxxxxxxxxxxxxx&mch_id=xxxxxxxxxx&nonce_str=".$tmpArr['nonce_str']."&
product_id=".$tmpArr['product_id']."&time_stamp=".$tmpArr['time_stamp']."&sign=".$sign; // 官方文档中介绍了有个长url转短url的API 写的还是很清楚的 没遇到坑
$posarr = array(
'appid'=>'xxxxxxxxxxxxxxxxxx',
'mch_id'=>'xxxxxxxxxx',
'nonce_str'=>'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'long_url'=>urlencode($reurl)
// 这个地方文档中也说了 长url地址需要urlencode一下,不然你很可能得到签名错误
);
ksort($posarr);
$buff = "";
foreach ($posarr as $k => $v)
{
$buff .= $k . "=" . $v . "&";
}
$buff = trim($buff, "&"); $stringSignTemp=$buff."&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$sign= strtoupper(MD5($stringSignTemp)); // 官方文档中说了 所有传输必须采用xml格式 post方式 https协议 $xml = "<xml>
<appid>xxxxxxxxxxxxxxxxxx</appid>
<mch_id>xxxxxxxxxx</mch_id>
<nonce_str>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</nonce_str>
<sign>".$sign."</sign>
<long_url>".$posarr['long_url']."</long_url>
</xml>"; // 短连接请求地址
$posturl = "https://api.mch.weixin.qq.com/tools/shorturl";
//下面使用curl来请求
$ch = curl_init($posturl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); //返回文件流
curl_setopt($ch, CURLOPT_POST, 1); //使用post提交
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //post数据
$response = curl_exec($ch);
curl_close($ch); // simplexml_load_string php内置的解析简单xml文件的扩展
$xmlobj = simplexml_load_string ($response, 'SimpleXMLElement', LIBXML_NOCDATA );
// 这个地方我直接输出 $xml->short_url 居然是空的 非要经过下面几步才得行 难道是我php版本低了
$arr = array();
foreach ($xmlobj as $key => $value) {
// file_put_contents("mylog.php", $value."\n",FILE_APPEND);
$arr[$key] = $value;
}
//这个链接就很短了 生成的二维码很简单 像素超低的手机都可以扫
echo $arr['short_url']; // 最后,扫码支付只需要设置回调地址 ,至于支付授权目录 测试目录 白名单那些 都不用设置

各种解释各种坑已经在代码里面言尽了,请仔细看

然后用户可以拿起手机扫描二维码了,扫描过程中微信系统会发送一个xml数据到你预先设置的回调地址上

比如我这个地方就是http://xxx.com/a.php

具体这个a.php要做些什么呢,官方文档有说明,文章开头也说了这个回调地址的任务是什么,所以我们直接看代码:

 // file_get_contents("php://input")  接收原始输入流
$postObj = simplexml_load_string (file_get_contents("php://input"), 'SimpleXMLElement', LIBXML_NOCDATA ); $arr = array();
foreach ($postObj as $key => $value) {
$arr[$key] = $value;
}
$pos = explode("s",$arr['product_id']);
$price = $pos[0];
// $price = $pos[0]*0.01;
$userid = $pos[1];
// 这些参数可以到文档去看看 有的参数是必填 有的是选填
$tmparr = array(
'appid'=>'xxxxxxxxxxxxxxxxxx',
'mch_id'=>'xxxxxxxxxx',
'nonce_str'=>'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'body'=>'情缘网现金充值',
'detail'=>'关注情缘网公众号,充值享8折优惠',
'product_id'=>$price,
'trade_type'=>'NATIVE',
'out_trade_no'=>time()."s".$userid.'s'.$price,
'spbill_create_ip'=>'211.149.241.155',
'notify_url'=>'http://xxx.com/b.php',
'total_fee'=>$price
);
// 注意这个地方我在订单号out_trade_no上做文章了,因为支付结果异步通知到另外一个url上,
// 而那个url上只能收到订单号而不是产品编号,所以我把运载在产品编号上的信息转移到了订单号上
// 我在异步url上才能处理我的业务 // 当然你在这个页面处理业务也是可以的,比如你现在就把订单号,价格,会员信息等插入数据库,在异步url上在根据订单号查询出本次
// 订单信息,然后操作,两种放啊都是可取的
// 我这里业务简单,直接把数据寄托在订单号上了,最好是在这个页面处理一部分逻辑,在异步通知后完善逻辑 ksort($tmparr);
$buff = "";
foreach ($tmparr as $k => $v)
{
$buff .= $k . "=" . $v . "&";
}
$buff = trim($buff, "&"); $stringSignTemp=$buff."&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$sign= strtoupper(md5($stringSignTemp));
$xml = "
<xml>
<appid>xxxxxxxxxxxxxxxxxx</appid>
<mch_id>xxxxxxxxxx</mch_id>
<nonce_str>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</nonce_str>
<sign>".$sign."</sign>
<body>商品标题</body>
<detail>商品描述</detail>
<product_id>".$price."</product_id>
<out_trade_no>".$tmparr['out_trade_no']."</out_trade_no>
<total_fee>".$price."</total_fee>
<spbill_create_ip>xxx.xxx.xxx.xxx</spbill_create_ip>
<notify_url>http://xxx.com/b.php</notify_url>
<trade_type>NATIVE</trade_type>
</xml>
";
// 调用统一下单 接口
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$ch = curl_init ($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
$response = curl_exec($ch);
// if(curl_errno($ch)){
// print curl_error($ch);
// }
curl_close($ch);
// 将接收到的数据直接返回,这一步官方没说明,大坑
echo $response;

有了第一个php文件的详细说明,相信这个文件中的代码各位可以自行理解了,这里代码简单,没有使用类对象这些东西,

全是过程,一步步往下读就ok了

回调这一步完成过后用户应该已经支付成功了,接下来微信系统还会将支付成功的喜讯发送到我们统一下单中设置的回调地址上

我这里就是http://xxx.com/b.php,直接看代码:

 $postObj = simplexml_load_string     (file_get_contents("php://input"), 'SimpleXMLElement', LIBXML_NOCDATA );
$arr = array();
foreach ($postObj as $key => $value) {
$arr[$key] = $value;
}
$status = $arr['result_code'];
$xml = "
<xml>
<return_code><![CDATA[".$status."]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>";
echo $xml; // 下面就根据$status来处理业务逻辑 //实例化数据库连接句柄
$mysqli = new mysqli("localhost", "username", "password", "dbname"); $mysqli->query("set names 'utf8'"); $danhao = $arr['out_trade_no']; //订单号
$sql = " select * from wxpaylog where orderid = '".$danhao."' ";
$res = $mysqli->query($sql);
//如果已经处理过这个订单号了 就直接忽视
if($res->num_rows > 0 )
{
return false; die;
} $pos = explode("s", $danhao);
$userid = $pos[1]; //用户id
$price = round($pos[2]*0.01); //充值金额
// $price = round($pos[2]*100); //充值金额
$zengsong = 0; //赠送额度 //充值金额 和送金币 活动规则
if($price<50) $zengsong = round($price*0.2);
if($price>=50 && $price <100) $zengsong = round($price*0.25);
if($price>=100 && $price <200) $zengsong = round($price*0.35);
if($price>200) $zengsong = round($price*0.5); $sql = " select money from oepre_user where userid = '".$userid."'"; $result = $mysqli->query($sql);
$row = $result->fetch_row();
$gold_num = $row[0]; //当前的金币
if($status == "SUCCESS")
{
//要设置的金币数量
$now_count = (int)$gold_num+(int)$price+(int)$zengsong;
}else
{
$now_count = (int)$gold_num;
} $sql = " update oepre_user set money = ".$now_count." where userid = '".$userid."'";
$result = $mysqli->query($sql);
//添加记录
$sql = " insert into wxpaylog (userid,money,zengsong,atime,orderid,remark)
values(".$userid.",".$price.",".$zengsong.",'".date("Y-m-d H:i:s")."','".$danhao."','".$arr['result_code']."') ";
$result = $mysqli->query($sql);

我试了多次充值,异步通知全都是成功支付的,如果支付到一班取消,或者其他原因,异步通知是不会到的,所以这个地方我处理的比较粗暴,无论他

来的是成功或者失败,我都是直接返回去,然后把日志写到数据库,如果充值出了问题我在到数据库里面去检查,我们网站一年也没几个充值的,所以,恕我偷懒咯。

正常情况下如果收到的不是success,那么需要继续调用查询订单api,看看具体情况在继续做处理。

微信扫码支付 php的更多相关文章

  1. MVC 微信扫码支付

    微信扫码支付有两种模式, 模式一和模式二, 两者具体的区别可参考官网文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4 微 ...

  2. C# 微信扫码支付 回调页面

    .NET版 微信扫码支付,官方推荐使用[模式二] 一.微信扫码支付模式一: 1.回调页面:官方demo中example文件下的NativeNotifyPage.aspx 2.微信回调地址:http:/ ...

  3. C# 微信扫码支付API (微信扫码支付模式二)

    一.SDK下载地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1,下载.NET C#版本: 二.微信相关设置:(微信扫码 ...

  4. Net MVC微信扫码支付

    微信扫码支付+Asp.Net MVC 这里的扫码支付指的是PC网站上面使用微信支付,也就是官方的模式二,网站是Asp.net MVC,整理如下. 一.准备工作 使用的微信API中的统一下单方法,关键的 ...

  5. 微信公众号支付|微信H5支付|微信扫码支付|小程序支付|APP微信支付解决方案总结

    最近负责的一些项目开发,都用到了微信支付(微信公众号支付.微信H5支付.微信扫码支付.APP微信支付).在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存. 先说注意 ...

  6. php微信扫码支付

    一 概述 扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信"扫一扫"完成支付的模式.该模式适用于PC网站支付.实体店单品或订单支付.媒体广告支付等场景.前几天公司需要做 ...

  7. ASP.NET Core Web 支付功能接入 微信-扫码支付篇

    这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入微信-扫码支付及异步通知功能. 开发环境:Win 10 x64.VS2017 15.6.4..NET Core SDK ...

  8. 【转载】ASP.NET Core Web 支付功能接入 微信-扫码支付篇

    转自:http://www.cnblogs.com/essenroc/p/8630730.html 这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入微信-扫码支付及异步 ...

  9. Thinkphp5整合微信扫码支付开发实例

    ThinkPHP框架是比较多人用的,曾经做过的一个Thinkphp5整合微信扫码支付开发实例,分享出来大家一起学习 打开首页生成订单,并显示支付二维码 public function index() ...

  10. C#版微信公众号支付|微信H5支付|微信扫码支付问题汇总及解决方案总结

    最近负责的一些项目开发,都用到了微信支付(微信公众号支付.微信H5支付.微信扫码支付).在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存.代码在文章结尾处,有需要的 ...

随机推荐

  1. 第四章 Hibernate入门

    1.构建了一个Student实体类 public class Student { private Integer id; //name private String name; //age priva ...

  2. VS的快捷键F12改成和ECLIPSE一样用ctrl+点击下载线

    安装resharper 插件即可 不过这个插件是收费的,可免费体验30天

  3. Maven系列二setting.xml 配置详解

    文件存放位置 全局配置: ${M2_HOME}/conf/settings.xml 用户配置: ${user.home}/.m2/settings.xml note:用户配置优先于全局配置.${use ...

  4. Google Guava官方教程(中文版)

    Google Guava官方教程(中文版) 原文链接  译文链接 译者: 沈义扬,罗立树,何一昕,武祖  校对:方腾飞 引言 Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库, ...

  5. AWS CLI 中使用S3存储

    登录 通过控制面板, 在S3管理器中创建一个新的bucket 所有AWS服务 -> 安全&身份 -> IAM -> 组, 创建一个新的组, 例如 "s3-user& ...

  6. 浅析jQuery删除节点的三个方法

    jQuery提供了三种删除节点的方法,即remove(),detach()和empty().测试所用HTML代码:[html] view plaincopy<p title="选择你最 ...

  7. VS2013使用EF6与mysql数据库

      您的项目引用了最新实体框架:但是,找不到数据链接所需的与版本兼容的实体框架数据库 EF6使用Mysql的技巧   在vs2013中使用mysql连接entityFramework经常会遇到这个问题 ...

  8. Query on a tree——树链剖分整理

    树链剖分整理 树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护. 通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点v,size[v]最大的v与u的边是重边,其它边是轻边,其中s ...

  9. BZOJ 1191 【HNOI2006】 超级英雄Hero

    Description 现在电视台有一种节目叫做超级英雄,大概的流程就是每位选手到台上回答主持人的几个问题,然后根据回答问题的多少获得不同数目的奖品或奖金.主持人问题准备了若干道题目,只有当选手正确回 ...

  10. Java多线程之Runable与Thread

    Java多线程是Java开发中的基础内容,但是涉及到高并发就有很深的研究可做了. 最近看了下<Java并发实战>,发先有些地方,虽然可以理解,但是自己在应用中很难下手. 所以还是先回顾一下 ...