实现思路

  • 生成一个二维码,加入要处理的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实现一个二维码同时支持支付宝和微信支付的更多相关文章

  1. 公司开发的APP,如何生成一个二维码,供客户下载使用

    1.其实和简单,因为一般的用户使用扫一扫,大多数都是用微信自带的扫一扫工具 而,微信打开的二维码页面,会自动屏蔽apk文件,所以显然把apk的url生成一个二维码,让用户扫一扫就能直接下载,这样是行不 ...

  2. 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码

    详解C#泛型(二)   一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...

  3. 二维码合成,将苹果和安卓(ios和android)合成一个二维码,让用户扫描一个二维码就可以分别下载苹果和安卓的应用

    因为公司推广的原因,没有合适的将苹果和安卓(ios和android)合成一个二维码的工具. 因为这个不难,主要是根据浏览器的UA进行判断,所以就自己开发了一个网站 网站名称叫:好推二维码  https ...

  4. 用Arduino制作一个二维码显示器

    先上图 场景是这样的, 这几天给CS系统做一个微信支付的功能,  但是生成的二维码是在前台的电脑上..不可能让用户跑到前台的电脑上去扫描...然后拿出了N年前买的Arduino 做了一个二维码显示器. ...

  5. 一个二维码如何自动识别是安卓(Android)还是苹果(IOS)

    思考问题: 通常,我们开发一个APP,有Android版本.IOS版本. 但是只有一个二维码?怎么办呢? 怎么让IOS用户扫描二维码下载IOS版本,Android用户扫描二维码下载到Android版本 ...

  6. 用一个二维码做下载地址,自动区分是 ios 还是 android

    用一个二维码做下载地址,自动区分是 ios 还是 android, 甚至区分 iphone  和 ipad. <html> <head> <meta http-equiv ...

  7. 用lua nginx module搭建一个二维码

    用lua nginx module搭建一个二维码(qr code)生成器 作者 vinoca 發布於 2014年10月31日 如果有VPS,或者开源的路由器,安装一个nginx,添加lua-nginx ...

  8. 安卓app和苹果app共用一个二维码

    应项目要求,现在安卓app和苹果app共用一个二维码,对外提供下载: <html> <head> <meta http-equiv="Content-Type& ...

  9. react页面内嵌微信二维码 和 自定义样式 以及 微信网页共用unionId问题

    在react页面内嵌“微信二维码”,实现PC端通过微信扫码进行登录.首先去微信开放平台注册一个账号,创建一个网站应用,提交网站备案审核,获取appid和appsecret:其他开发流程根据微信文档来进 ...

随机推荐

  1. 高性能缓存 Caffeine 原理及实战

    一.简介 Caffeine 是基于Java 8 开发的.提供了近乎最佳命中率的高性能本地缓存组件,Spring5 开始不再支持 Guava Cache,改为使用 Caffeine. 下面是 Caffe ...

  2. MySql(二)索引的设计与使用

    MySql(二)索引的设计与使用 一.索引概述 二.设计索引的原则 三.BTREE索引与HASH索引 一.索引概述 所有Mysql列类型都可以被索引,对相关列使用索引时提高select操作性能的最佳途 ...

  3. 小白搭建WNMP详细教程---NGINX、MYSQL、PHP的整合配置

    我自定义安装后的目录结构如下: 安装在D盘的WNMP下,其中WWW是网站的目录.ZIPS是放压缩包文件. 一.配置环境变量 在桌面右击我的电脑,选择属性,出现窗口后,按下图所示操作: 点击编辑后,会出 ...

  4. Flink-v1.12官方网站翻译-P013-Timely Stream Processing

    及时的流处理 介绍 及时流处理是有状态流处理的一种扩展,其中时间在计算中起着一定的作用.其中,当你做时间序列分析时,当做基于某些时间段(通常称为窗口)的聚合时,或者当你做事件处理时,事件发生的时间很重 ...

  5. MySQL 中的自增主键

    MySQL 的主键可以是自增的,那么如果在断电重启后新增的值还会延续断电前的自增值吗?自增值默认为1,那么可不可以改变呢?下面就说一下 MySQL 的自增值. 特点 保存策略 1.如果存储引擎是 My ...

  6. Azure Functions(一)什么是 ServerLess

    一,引言 自去年4月份分享过3篇关于 Azure Functions 的文章之后,就一直没有再将 Azure Functions 相关的内容了.今天再次开始将 Azure Functions 相关的课 ...

  7. zjnuSAVEZ (字符串hash)

    Description There are eight planets and one planetoid in the Solar system. It is not a well known fa ...

  8. win10 远程桌面 ubuntu

    一.获取本机ip 通过ip查询网址来查询本机外网地址 二.下载远程链接软件 下载向日葵,注册账号 三.远程链接 将连接端与被连接端分别绑定账号,输入相应ip地址,即可连接.

  9. K8S(03)核心插件-Flannel网络插件

    系列文章说明 本系列文章,可以基本算是 老男孩2019年王硕的K8S周末班课程 笔记,根据视频来看本笔记最好,否则有些地方会看不明白 需要视频可以联系我 K8S核心网络插件Flannel 目录 系列文 ...

  10. Kubernets二进制安装(16)之安装部署traefik(ingress)

    K8S的DNS实现了服务在集群"内"被自动发现,如何使得服务在Kuberneters集群"外"被使用和访问呢,有二种方法 1)使用NodePort型的Servi ...