PHP实现一个二维码同时支持支付宝和微信支付
实现思路
- 生成一个二维码,加入要处理的url连接
- 在用户扫完码后,在对应的脚本中,判断扫码终端,调用相应的支付
- 若能够扫码之后能唤起相应app,支付宝要用手机网站支付方式,微信要使用jsapi支付方式
效果展示

提示: 因为项目即将上线,所以上面的支付二维码连接被我替换了(注意在生成二维码时加入的连接,要带上http协议)


实现步骤
生成二维码
//我的url指向了checkTerrace方法
$url = self::ADMIN_URL . 'params=' . $params;
//ADMIN_URL是生成二维码的url,请替换成自己
处理用户扫码操作(checkTerrace方法)
public function checkTerrace()
{
$pay_type = $this->getPayType(); //该方法使用来判断用户扫码终端的
$params = $this->request->get('params'); //生成二维码url带的参数(看个人需求,我的项目需要额外参数)
$params = $this->desDecode($params); //这里是因为我对参数进行了desc加密,看个人需求
if ($pay_type === 'alipay') { //如果用户是通过支付宝扫码,进行支付宝相关操作
if ($params === false) {
echo "系统错误!,请稍后重试";
exit;
}
$res = $this->createOrder($pay_type, $params);
if (!$res) {
echo "系统错误,请稍后重试";
exit;
}
$this->aliPay($res);
} elseif ($pay_type === 'wechat') { //如果用户是通过微信扫码,进行微信相关操作
if ($params === false) {
echo "系统错误,请稍后重试";
exit;
}
$prepare = $this->wechat($pay_type, $params);
$this->assign('json', $prepare);
return $this->display('wpay.html');
} elseif ($pay_type === false) {
echo "请使用支付宝或微信进行扫码";
exit;
}
}
判断扫码终端
/**
* 判断扫码终端
*
* @return string|boolean
* @date 2021-02-04
*/
private function getPayType()
{
if (strstr($_SERVER['HTTP_USER_AGENT'], 'AlipayClient')) {
return "alipay";
} elseif (strstr($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger')) {
return "wechat";
} else {
return false;
}
}
生成订单
/**
* 生成订单
*
* @param string $pay_type
* @param json $params
* @return void
* @date 2021-02-04
*/
//这个逻辑就不贴代码了
private function createOrder($pay_type, $params)
{
/*生成订单相关逻辑代码*/
}
支付宝支付
/**
* 唤起支付宝app
*
* @param array $api_params
* @return void
* @date 2021-02-04
*/
private function aliPay($api_params)
{
$config = [
'notify_url' => '异步回调地址',
'is_open_certificate' => true
];
$domain = urlencode($api_params['domain']);
$api = [
'out_trade_no' => $api_params['trade_no'],
'total_amount' => '0.01',
'subject' => '商品标题',
'passback_params' => $domain
];
$pay = new Pay($config);
$res = $pay->driver('alipay')->gateway('wap')->pay($api); //调用支付宝手机网站支付
echo $res;
}
微信支付
/**
* 唤起微信app
*
* @return void
* @date 2021-02-04
*/
public function wechat($pay_type, $params)
{
$opend_id = $this->getOpenId(); //处理微信jsapi支付之前,要先获取用户的openID
if (!$opend_id) {
echo "微信授权失败...";
exit;
}
$api_params = $this->createOrder($pay_type, $params); //用户openID获取成功后才进行订单生产操作
if (!$api_params) {
echo "系统错误,请稍后重试";
exit;
}
$config = ['notify_url' => '微信异步回调地址'];
$api = [
'body' => '我是标题',
'out_trade_no' => $api_params['trade_no'],
'total_fee' => 1,
'openid' => $opend_id,
'attach' => $api_params['domain']
];
$pay = new Pay($config);
$res = $pay->driver('wechat')->gateway('mp')->pay($api); //调用微信jsapi支付
return $res;
}
静默获取openID
/**
* 获取用户的openid
*
* @return void
* @date 2021-02-04
*/
public function getOpenId()
{
if (isset($_SESSION['open_id']) && $_SESSION['open_id']) {
return $_SESSION['open_id'];
}
if (!$this->request->get('code')) {
$redirect_uri = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; //这里授权后微信跳转的地址,要写在订单处理处,否则会造成因为程序跳转到微信授权页面,导致脚本逻辑终止
$redirect_uri = urlencode($redirect_uri);
$url = $this->codeUrl . 'redirect_uri=' . $redirect_uri . '&appid=' . $this->appId . '&scope=snsapi_base&response_type=code&state=STATE#wechat_redirect'; //使用用户静默授权模式(因为我不需要获取用户信息所有就没采用用户手段授权模式)
header("location:{$url}"); //跳转到微信授权页面
} else {
$openidurl = $this->openidUrl . 'appid=' . $this->appId . '&secret=' . $this->appSecret . '&code=' . $this->request->get('code') . '&grant_type=authorization_code';
$data = Http::get($openidurl);
$data = json_decode($data, true);
if ($data['openid']) {
$_SESSION['open_id'] = $data['openid']; //获取到的用户openID存储到session中
} else {
$_SESSION['open_id'] = false;
}
return $_SESSION['open_id'];
}
}
前端轮询判断监听订单支付状态
$(function() {
$("#code").qrcode({
//jQuery生成二维码
width: 165, //宽度
height: 167, //高度
text: $('input[name="url"]').val()
});
var startTime = Date.parse(new Date())/1000;
//设置定时器
var poll_request = setInterval( function() {
$.ajax({
url: '/company/StoreSetting/checkStatus',
data:{time:startTime},
dataType:'json',
type:'get',
success:function(res) {
if (res.code == 400) {
var result = clearTimer(poll_request, startTime);
if (result) {
var html = `<img src="/Static/images/paybg.png">`+
`<div class="notify" id="notify">`+
`<img src="/Static/images/pay_time_out.png" alt="">`+
`<span class="pay_tip">点击重新获取</span>`+
`</div>`;
$('.qrcode-img').empty();
$('.qrcode-img').append(html);
}
} else if(res.code == 500) {
var html = `<img src="/Static/images/paybg.png">`+
`<div class="notify">`+
`<img src="/Static/images/pay_error.png" alt="">`+
`<span class="pay_tip">已扫码<br>请在手机端操作</span>`+
`</div>`;
$('.qrcode-img').empty();
$('.qrcode-img').append(html);
clearTimer(poll_request, startTime);
} else if(res.code == 200) {
clearInterval(poll_request)
layer.msg("支付成功", {icon:6}, function() {
window.location.reload()
})
// layer.msg("支付成功", {icon:6}, function() {
// })
}
}
})
}, 2000);
})
function clearTimer(index, startTime) {
if (((Date.parse(new Date())/1000) - startTime) > 60) {
clearInterval(index)
return 'reload';
}
return false;
}
//刷新二维码
$('.qrcode-img').on("click", '#notify', function() {
$('.qrcode-img').empty()
$("#code").qrcode({
width: 165, //宽度
height: 167, //高度
text: $('input[name="url"]').val()
});
var startTime = Date.parse(new Date())/1000;
var poll_request = setInterval( function() {
$.ajax({
url: '/company/StoreSetting/checkStatus',
data:{time:startTime},
dataType:'json',
type:'get',
success:function(res) {
if (res.code == 400) {
var result = clearTimer(poll_request, startTime);
if (result) {
var html = `<img src="/Static/images/paybg.png">`+
`<div class="notify" id="notify">`+
`<img src="/Static/images/pay_time_out.png" alt="">`+
`<span class="pay_tip">点击重新获取</span>`+
`</div>`;
$('.qrcode-img').empty();
$('.qrcode-img').append(html);
}
} else if(res.code == 500) {
var html = `<img src="/Static/images/paybg.png">`+
`<div class="notify">`+
`<img src="/Static/images/pay_error.png" alt="">`+
`<span class="pay_tip">已扫码<br>请在手机端操作</span>`+
`</div>`;
$('.qrcode-img').empty();
$('.qrcode-img').append(html);
clearTimer(poll_request, startTime);
} else if(res.code == 200) {
clearInterval(poll_request)
layer.msg("支付成功", {icon:6}, function() {
window.location.reload()
})
// layer.msg("支付成功", {icon:6}, function() {
// })
}
}
})
}, 2000);
})
前端效果:
- 用户进入支付页面但是一直为扫码,超过一定时间

- 用户扫码后一直未进行支付,超过一定时间

只是给大家提供了一个思路,希望能够对需要的人有些帮助
PHP实现一个二维码同时支持支付宝和微信支付的更多相关文章
- 公司开发的APP,如何生成一个二维码,供客户下载使用
1.其实和简单,因为一般的用户使用扫一扫,大多数都是用微信自带的扫一扫工具 而,微信打开的二维码页面,会自动屏蔽apk文件,所以显然把apk的url生成一个二维码,让用户扫一扫就能直接下载,这样是行不 ...
- 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码
详解C#泛型(二) 一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...
- 二维码合成,将苹果和安卓(ios和android)合成一个二维码,让用户扫描一个二维码就可以分别下载苹果和安卓的应用
因为公司推广的原因,没有合适的将苹果和安卓(ios和android)合成一个二维码的工具. 因为这个不难,主要是根据浏览器的UA进行判断,所以就自己开发了一个网站 网站名称叫:好推二维码 https ...
- 用Arduino制作一个二维码显示器
先上图 场景是这样的, 这几天给CS系统做一个微信支付的功能, 但是生成的二维码是在前台的电脑上..不可能让用户跑到前台的电脑上去扫描...然后拿出了N年前买的Arduino 做了一个二维码显示器. ...
- 一个二维码如何自动识别是安卓(Android)还是苹果(IOS)
思考问题: 通常,我们开发一个APP,有Android版本.IOS版本. 但是只有一个二维码?怎么办呢? 怎么让IOS用户扫描二维码下载IOS版本,Android用户扫描二维码下载到Android版本 ...
- 用一个二维码做下载地址,自动区分是 ios 还是 android
用一个二维码做下载地址,自动区分是 ios 还是 android, 甚至区分 iphone 和 ipad. <html> <head> <meta http-equiv ...
- 用lua nginx module搭建一个二维码
用lua nginx module搭建一个二维码(qr code)生成器 作者 vinoca 發布於 2014年10月31日 如果有VPS,或者开源的路由器,安装一个nginx,添加lua-nginx ...
- 安卓app和苹果app共用一个二维码
应项目要求,现在安卓app和苹果app共用一个二维码,对外提供下载: <html> <head> <meta http-equiv="Content-Type& ...
- react页面内嵌微信二维码 和 自定义样式 以及 微信网页共用unionId问题
在react页面内嵌“微信二维码”,实现PC端通过微信扫码进行登录.首先去微信开放平台注册一个账号,创建一个网站应用,提交网站备案审核,获取appid和appsecret:其他开发流程根据微信文档来进 ...
随机推荐
- HTML5的表单input元素的新属性
知识点 <HTML5的表单input元素的新属性>,留待学习分享... <!-- HTML5的表单input元素的新属性 Autocomplete:自动完成功能 Autofocus: ...
- 利用Javascript制作网页特效(其他常见特效)
设置为首页和加入收藏夹 ①:在body标签内输入以下代码: <a onclick="this.style.behavior='url(#default#homepage)'; this ...
- hadoop的hdfs中的namenode和datanode知识总结
一,NameNode: 1, Namenode是中心服务器,单一节点(简化系统的设计和实现),负责管理文件系统的名称空间(namespace)以及客户端对文件的访问. 2, 文件操作,Namenod ...
- cocos2d-x 调试问题
1.昨天一个新功能,在xcode模拟器上测试没问题.后来打包安卓后,一直有问题 就又添加日志功能 # define CCLOGFUNC(s) ...
- 调用个别f5 负载端口为80的vs时,返回值为空的问题
现状: vs负载端口为80并添加XFF,pool包含2个member,member的monitor端口为80&9000. 故障现象: 应用同事描述说再完全复制了一个member并添加到pool ...
- HTML定位
定位(position) 定位是一种更加高级的布局手段,通过定位可以将元素摆放到元素的任何位置 使用position属性来设置定位 可选值:static 默认值,元素是静止的没有开启定位 relati ...
- Web APP和原生 APP的不同
我们现在手机中的APP,大部分都是混合APP,也就是既用到了原生APP的基础,又用到了Web APP的基础,混合的比例从0%到100%之间不等.更好的了解APP的类型,有助于我们学则合适的测试策略.今 ...
- Linux下diff的操作详解
总述 Linux diff命令用于比较文件的差异.diff以逐行的方式,比较文本文件的异同处.特别是比较两个版本不同的文件,如果指定要比较目录,则diff会比较目录中相同文件名的文件,但不会比较其中子 ...
- mybatis Sql语句配置详解
sql语句配置 id sqlSession执行的唯一标识 resultMap 结果集封装映射,可用于内部对象一对多封装 resultType 返回的结果类型,直接就是一个po对象 resultSets ...
- 2019牛客暑期多校训练营(第十场)B-Coffee Chicken
>传送门< 题意:S(1)="COFFEE",S(2)="CHICKEN" ,S(n) = S(n−2)+S(n−1),请输出 S(n) 中从第 k ...