zoukankan      html  css  js  c++  java
  • EasyWeChat使用(laravel框架下)

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

    接着注册ServiceProvider,由于看github文档说明时,文档上有错误LaravelWeChat写成了小写Laravelwechat 在phpstorm中按ctr点击鼠标也能跳转方法,但是在运行项目的时候报找不到这个类,最后看了github上的issue有人遇到同样的问题,发现了这个大小写问题,此为第一个坑.
    OvertrueLaravelWeChatServiceProvider::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 AppHttpControllers;
    use AppHttpControllersWebController;
    use IlluminateHttpRequest;
    use AppHttpControllersWebUserController as User;
    use AppModelsSetting;
    use AppModelsWechatReply;
    use EasyWeChatMessageNews;
    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 EasyWeChatFoundationApplication;
    use IlluminateHttpRequest;
    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 = AppModelsUser::where('id', $userId)->first();
        $app = new EasyWeChatFoundationApplication(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 EasyWeChatFoundationApplication;
    use EasyWeChatPaymentOrder as WechatOrder;
    use IlluminateSupportFacadesLog;
    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中, 但是实际不行.

  • 相关阅读:
    Spark:大数据的“电光石火”
    Android开发-取消程序标题栏或自定义标题栏
    Android中实现圆角矩形及半透明效果。
    Android中设定背景图片平铺。
    收到的电邮附件为Winmail.dat?
    Runas命令:能让域用户/普通User用户以管理员身份运行指定程序。
    AD域服务器|两台DC无法进行复制同步
    IIS服务器运行一段时间后卡死,且无法打开网站(IIS管理无响应,必须重启电脑)
    Outlook不能打开附件(提示:无法创建文件xx,请右键单击要在其中创建文件的文件夹..)
    点击自动显示/隐藏DIV代码。(简单实用)
  • 原文地址:https://www.cnblogs.com/binxyz/p/7718861.html
Copyright © 2011-2022 走看看