• 最近做了个项目是关于微信网页开发的,今天记录下在做项目中的关于微信这块遇到的一些坑
  1. 关于微信这块,用的是EasyWeChat,提高了开发的效率.在看EasyWeChat这个文档的时候发现了有专门针对laravel 框架的包,所以就用了laravel-wechat

    首先是安装这个composer包
composer require "overtrue/laravel-wechat:~3.0"

接着注册ServiceProvider,由于看github文档说明时,文档上有错误LaravelWeChat写成了小写Laravelwechat 在phpstorm中按ctr点击鼠标也能跳转方法,但是在运行项目的时候报找不到这个类,最后看了github上的issue有人遇到同样的问题,发现了这个大小写问题,此为第一个坑.

Overtrue\LaravelWeChat\ServiceProvider::class

  1. 接着是token验证,该配置的地方都没问题了,但是这个token验证老师失败,最后发现由于laravel 自带的CSRF 中间件要排除路由,和这个中间件平级,同时CSRF排除这个url
Route::any('/wechat', 'WeChatController@serve');
Route::group(['middleware' => 'web'], funcation(){})

过滤url

class VerifyCsrfToken extends BaseVerifier
{
protected $except = [
//
'wechat', 'web/pay/callback', 'web/pay/signCallback'
];
}
  1. 关注回复相关代码
namespace App\Http\Controllers;
use App\Http\Controllers\Web\Controller;
use Illuminate\Http\Request;
use App\Http\Controllers\Web\UserController as User;
use App\Models\Setting;
use App\Models\WechatReply;
use EasyWeChat\Message\News;
class WechatController extends Controller
{
public function serve()
{
$app = app('wechat');
$app->server->setMessageHandler(function($message) use ($app){
if ($message->MsgType=='event') {
$userOpenid = $message->FromUserName;
switch ($message->Event) {
case 'subscribe':
$userInfo['openid'] = $userOpenid;
$userService = $app->user; //获取用户服务
$user = $userService->get($userInfo['openid']);
$userInfo['nickname'] = $user['nickname'];
$userInfo['headimgurl'] = $user['headimgurl'];
if (userAttention($userInfo)) {
return $this->reply('follow_keyword');
}else{
return '您的信息由于某种原因没有保存,请重新关注';
}
break;
case 'unsubscribe':
if (userCancelAttention($userOpenid)) {
return '已取消关注';
}
break;
default:
# code...
break;
}
} elseif ($message->MsgType=='text') { //关键字回复
$replay = WechatReply::where('keyword', $message->Content)->first();
if ($replay) { } else { }
}
}); return $app->server->serve();
}
  1. 网页授权,EasyWeChat进行网页授权非常简单,这个项目中,我主要用了静默授权,只要将路由包含在这个中间件中,通过session就可以获得这个用户的openid
Route::group(['middleware' => 'wechat.oauth'], function () {
}); $user = session('wechat.oauth_user
$openid = $user['id'];
  1. 菜单的设置 ,通过注入的方式获得菜单的实例
use EasyWeChat\Foundation\Application;
use Illuminate\Http\Request;
class MenuController extends Controller
{
public $menu;
public function __construct(Application $app)
{
$this->menu = $app->menu;
}
public function create()
{
//这里进行菜单数组格式的组装
$this->menu->add($buttons);
}
  1. 模板消息 ,写一个全局辅助函数,来发送模板消息,获得模板消息应用实例
function sendTemplateMsg($templateId, $userId, $data, $url)
{
$user = \App\Models\User::where('id', $userId)->first();
$app = new \EasyWeChat\Foundation\Application(config('wechat'));
$notice = $app->notice;
$result = $notice->uses($templateId)->withUrl($url)->andData($data)->andReceiver($user->openid)->send();
return $result;
}
  1. 支付,支付这边用的是jssdk方式支付,最主要的是要获得统一下单的prepay_id.

    前端页面这个$js要在控制器传到这个待支付界面
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js" type="text/javascript" charset="utf-8"></script> //微信的js库文件
wx.config(<?php echo $js->config(array('chooseWXPay'), false) ?>);
点击支付调用此方法
function pay() {
$.ajax({
url: siteUrl + '/web/pay/create',
type: 'get',
dataType: 'json',
data: {
'id': "{{ $order->id }}",// 订单ID
'coupon_id': "{{ $coupon_id }}",// 优惠券ID
'actual_payment_amount': deposit // 实际支付金额
},
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
success: function (data) {
if (data['code'] === 0) {
data = data['data'];
wx.ready(function() {
wx.chooseWXPay({
timestamp: data['timestamp'],
nonceStr: data['nonceStr'],
package: data['package'],
signType: data['signType'],
paySign: data['paySign'],
success: function (res) {
// 支付成功跳转新页面
window.location.href = siteUrl + "/web/order/paySuccess";
}
});
});
} else {
// 统一下单失败
alert(data['data']['err_code_des']);
}
}
});
}

create方法代码

use EasyWeChat\Foundation\Application;
use EasyWeChat\Payment\Order as WechatOrder;
use Illuminate\Support\Facades\Log;
class PayController extends Controller
{
private $app;
public function __construct()
{
$this->app = new Application(config('wechat'));
}
// 微信统一下单
public function createOrder(Request $request)
{
$order = Order::find($request->id);
$attributes = [
'trade_type' => 'JSAPI',
'body' => '',
'detail' => '订单号: ' . $order->order_no,
'out_trade_no' => $order->order_no,
'total_fee' => $request->actual_payment_amount * 100, // 单位:分
'openid' => getOpenId(), // trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识,
'notify_url' => url('/web/pay/callback'), //支付回调方法
'attach' => '' //这里是带给回调方法的一些字段,用于业务逻辑处理
];
$order = new WechatOrder($attributes);
$result = $this->app->payment->prepare($order);
if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS') {
$prepayId = $result->prepay_id;
$config = $this->app->payment->configForJSSDKPayment($prepayId); // 返回数组
return apiReturn(SUCCESS, '统一下单成功', $config);
}
return apiReturn(ERROR, '统一下单失败', $result);
}

由于callback方法也是post类型所以在CSRF中间件中也要过滤,这也是有的人发现支付完了,但是没有调用callback方法

class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
//
'wechat', 'web/pay/callback'
];
}

callback方法

// 微信支付回调
public function callback(Request $request)
{
$response = $this->app->payment->handleNotify(function($notify, $successful) {
// 记录日志
Log::info('微信支付: ' . json_encode($notify));
// 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
$order = Order::where('order_no', $notify->out_trade_no)->first();
// 检查订单是否已经更新过支付状态
if ($order->pay_time) {
return true;
}
// 用户是否支付成功
if ($successful) {
//业务逻辑代码,处理
$attach = json_decode($notify->attach);
apiReturn(SUCCESS, '支付成功', $notify);
} else {
apiReturn(SUCCESS, '支付失败', $notify);
}
$order->save(); // 保存订单
$this->sendPayMessage($order->id);
return true; // 返回处理完成
});
return $response;
}
  1. 分享朋友,分享朋友圈,本来以为点击页面中的某一个按钮能直接分享给朋友呢,其实不是的,要使用微信的发送朋友,使用jssdk可以自定义分享的地址
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js" type="text/javascript" charset="utf-8"></script>
wx.config(<?php echo $js->config(array('onMenuShareTimeline', 'onMenuShareAppMessage'), false) ?>);
wx.ready(function() {
wx.onMenuShareAppMessage({
title: '', // 分享标题
desc: '', // 分享描述
link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: '', // 分享图标
type: 'link', // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () {
alert('分享成功');
// 用户确认分享后执行的回调函数
},
cancel: function () {
// 用户取消分享后执行的回调函数
},
fail: function () {
alert('fail');
}
});
wx.onMenuShareTimeline({
title: '推荐拿红包', // 分享标题
link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: '', // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
alert('分享成功')
},
cancel: function () {
// 用户取消分享后执行的回调函数
alert('请重新分享')
}
});
});

起初我没有用wx.ready包住这两段代码,结果是分享的是我当前界面,而不是自定义的url地址,但是微信开发文档写的是:

  • wx.ready(function(){

    // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。

    });

我理解的是分享也是手动触发的应该也不需要放在ready中, 但是实际不行.

EasyWeChat使用(laravel框架下)的更多相关文章

  1. Laravel框架下容器Container 的依赖注入和反射应用

    依赖注入,简单说是把类里头依赖的对象,置于类外头,即客户端调用处.相当于把类与类解耦. 一个简单的例子: class A { public function __construct() { // 这种 ...

  2. 基于Laravel框架下使用守护进程supervisor实现定时任务(毫秒)

    本篇文章给大家带来的内容是关于基于Laravel框架下使用守护进程supervisor实现定时任务(毫秒),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 公司需要实现X分钟内每隔Y秒 ...

  3. Laravel框架下路由的使用(源码解析)

    本篇文章给大家带来的内容是关于Laravel框架下路由的使用(源码解析),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 前言 我的解析文章并非深层次多领域的解析攻略.但是参考着开发文 ...

  4. 装饰器模式以及Laravel框架下的中间件应用

    Laravel框架的中间件使用:从请求进来到响应返回,经过中间件的层层包装,这种场景很适合用到一种设计模式---装饰器模式. 装饰器模式的作用,多种外界因素改变对象的行为.使用继承的方式改变行为不太被 ...

  5. 解析Laravel框架下的Contracts契约

    Contracts Laravel 的契约是一组定义框架提供的核心服务的接口, 例如我们在介绍用户认证的章节中到的用户看守器契约IllumninateContractsAuthGuard 和用户提供器 ...

  6. laravel 框架 下拉分页

    <!doctype html><html lang="en"><head> <meta charset="UTF-8" ...

  7. laravel框架中所用到的依赖注入

    用Laravel开发前前后后有2个月左右了,之前一直写Java,就像找到Java和PHP之前的共同点,用Java的某些原理去理解PHP会发现还是有很多共通之处的.Java的依赖注入已经是一个很常见的概 ...

  8. Laravel框架使用融云服务端SDK

    方法一:  使用第三方依赖安装(在项目根目录) 第一步:安装        composer require latrell/rongcloud dev-master 第二步:更新依赖包      c ...

  9. nginx下laravel框架rewrite的设置

    nginx下laravel框架rewrite的设置 百牛信息技术bainiu.ltd整理发布于博客园 在nginx的vhost站点配置文件中加入以下内容即可 1 2 3 4 5 6 7 8 9 10 ...

随机推荐

  1. NodeJS入门教学

    http://v.baidu.com/v?word=Node.JS+%2B+MongoDB&ct=301989888&rn=27&pn=0&db=0&s=0&a ...

  2. SpringBoot Web开发(4) Thymeleaf模板与freemaker

    SpringBoot Web开发(4) Thymeleaf模板与freemaker 一.模板引擎 常用得模板引擎有JSP.Velocity.Freemarker.Thymeleaf SpringBoo ...

  3. PAT 甲级 1027 Colors in Mars (20 分)

    1027 Colors in Mars (20 分) People in Mars represent the colors in their computers in a similar way a ...

  4. MySQL 大数据备份方案之Percona XtraBackup

    Xtrabackup介绍 1.Xtrabackup是什么 Xtrabackup是一个对InnoDB做数据备份的工具,支持在线热备份(备份时不影响数据读写),是商业备份工具InnoDB Hotbacku ...

  5. webservice的简单使用,cxf框架的的使用

    Web service是一个平台独立的,低耦合的,自包含的.基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述.发布.发现.协调和配置这些应用程序,用于开发分布 ...

  6. Android Studio 制作一个循环播报的效果

    这个就是用到了一个TextView 控件,直接上代码. <TextView android:id="@+id/tv_7" android:layout_width=" ...

  7. win7 64 位 + vs2015 + opencv3.2

    下载OpenCv_3.2_vc14 链接:http://pan.baidu.com/s/1eSBu1NG 密码:104g 1.下载好后,进行解压到自己指定的目录: 解压后可以得到: 2.添加环境变量 ...

  8. SpringMVC 数组类型的参数: Cannot generate variable name for non-typed Collection parameter type

    我只想安静的传个数组类型的参数, 为什么各种报错... @DeleteMapping("del") @ApiOperation(value = "删除") pu ...

  9. @Bean 的用法

    @Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里.添加的bean的id为方法名 定义bean 下面是@Configuratio ...

  10. python 数据分析库介绍

    1 引言 高效处理数据的python工具: 与外界进行交互: 读写各种文件格式和数据库 准备: 对数据进行清理.修整.整合.规范化.重塑.切片切换.变形等处理以便进行分析 转换: 对数据集做一些数学和 ...