laravel guzzle 使用

安装:
composer.json 添加  "guzzlehttp/guzzle": "~6.0",
然后执行:
composer update guzzlehttp/guzzle
完成安装
使用:
首先 use:
use GuzzleHttp\Client;
使用举例:
$client = new Client();
try {
    $response = $client->request('PUT',$url,[
        'body' => json_encode(['foo' => 'bar'])
    ]
    );
    Log::info('返回');
    $statusCode = $response->getStatusCode();
    $rsp = $response->getBody()->getContents();
    //$arrRes = json_decode($rsp, true);
    Log::info($statusCode);
    Log::info($rsp);
} catch (\Exception $e) {
    echo 'error:  ';
    echo $e->getMessage();
}
try catch 获取错误
put 的url 使用如下方式获取 body 数据:
$rsp = file_get_contents("php://input");
提交 form 表单:
$response = $client->request('POST',$url,[
    'form_params' => [
        'accountId' => $accountId,
        'orderId' => $orderId,
        'mac' => $mac
    ]
那么 $request->all() 或者 $_POST 即可获取值
注意:Laravel 或者其他框架一般会开启 CSRF验证,这么提交是不会通过验证的,可能会返回500 这个时候 被请求方应该 关闭这个验证
1.laravel 可以直接注释掉这个中间件 \App\Http\Middleware\VerifyCsrfToken::class,
2.laravel 在这个中间件里面添加排除路由
3.laravel 使用 api 接口
请求数据 json格式:
$response = $client->request('POST',$url,[
    'json' => ['foo' => 'bar']
]
此时请求方 $request->all() 或者 php://input 都能获取到数据  但是 $_POST 获取不到数据
例如结果打印:
request->all() 的数据
array (
  'foo' => 'bar',
)
$_POST 的数据
array (
)
file_get_contents("php://input")的数据:
{"foo":"bar"}
获取请求的 方法:
打印 $_SERVER['REQUEST_METHOD'] 即可
例如:
'REQUEST_METHOD' => 'DELETE',

laravel composer 扩展包开发(超详细)

laravel composer package develop
文章适用于laravel 包开发,当然如果你理解着完成一遍,就可以发现他也适用于 composer 扩展包开发,不是必须在laravel 下。
首先在 laravel 根目录创建文件夹 packages 这里放置我们准备创建的扩展包,这个目录只是暂时存放我们的扩展包代码,等我们开发完成配置好了,就不需要他了。
当然如果你不需要发布你的包,以后也可以就使用这个目录。packages 目录和 laravel 的 app 目录同级
然后进入packages 创建目录  aex 当然这个名字可以随意起(最好是作者的名之类的),
接着进入 aex 目录创建目录 packagetest 这个目录的名称最好是你的扩展包名称,有点意义。 我就是为了测试,所以就叫做 packagetest 好了
然后创建目录 src 这里就是我们放置代码的地方啦。
接着命令行下进入 packages/aex/packagetest 执行 composer init  他会一步步询问你要填写的信息:
1
执行完成你会在 packagetest 目录下看到 composer.json 内容和上图一致。 当然其实你也可以直接复制一个 composer.json 不需要 composer init
我的 composer.json 内容如下:
{
    "name": "aex/packagetest-for-laravel",
    "authors": [
        {
            "name": "aex",
            "email": "email@email.com"
        }
    ],
    "require": {}
}
你也可以根据 composer.json 的规则添加相应的其它配置
目前目录结构是这样的:
2
虽然你知道代码都在 src目录下,但是 laravel 不知道,所以你要告诉他,即配置 laravel 根目录的 composer.json
修改 autoload 改为类似如下:
"autoload": {
    "classmap": [
        "database"
    ],
    "psr-4": {
        "App\\": "app/",
        "Aex\\Packagetest\\": "packages/aex/packagetest/src/"
    }
},
然后创建服务:使用 artisan 命令
php artisan make:provider PackagetestServiceProvider
执行完成,laravel 在 app/Providers下会生成 PackagetestServiceProvider.php 然后你把他剪切到 你的 src目录:packages/aex/packagetest/src
同时修改代码的命名空间为你刚刚定义的:namespace Aex\Packagetest;  顺便把注册服务等都写完吧,完成代码如下:
<?php
namespace Aex\Packagetest;
use Illuminate\Support\ServiceProvider;
class PackagetestServiceProvider extends ServiceProvider
{
    /**
     * 服务提供者加是否延迟加载.
     *
     * @var bool
     */
    protected $defer = true; // 延迟加载服务
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->loadViewsFrom(__DIR__ . '/views', 'Packagetest'); // 视图目录指定
        $this->publishes([
            __DIR__.'/views' => base_path('resources/views/vendor/packagetest'),  // 发布视图目录到resources 下
            __DIR__.'/config/packagetest.php' => config_path('packagetest.php'), // 发布配置文件到 laravel 的config 下
        ]);
    }
    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
         // 单例绑定服务
        $this->app->singleton('packagetest', function ($app) {
            return new Packagetest($app['session'], $app['config']);
        });
    }
    /**
     * Get the services provided by the provider.
     *
     * @return array
     */
    public function provides()
    {
        // 因为延迟加载 所以要定义 provides 函数 具体参考laravel 文档
        return ['packagetest'];
    }
}
自问自答:
1.为什么创建的服务要放到src 下? -- 你要开发扩展包,放到laravel下面就不算扩展包了,你的包以后要给别人用,别人会统一安装到vendor下的,总不能单独把 service 文件也打包上传吧。
同理服务定义了 publish , 配置和视图不同系统需求肯定不一样,为了让人家修改,所以我们提供发布到laravel 原始视图和配置路径的方法,总不能让人家下载了你的到 到 vendor下修改吧。
2.那么 composer.json 里的命名空间为什么修改的是laravel 根目录的? -- 啪!多嘴!哦,不对,啪啪啪啪!!! 问的好!,这个我们还没讲完嘛,后面会给他提出来的,我们需要先跑通我们的代码,再完善成可发布的
接下来注册我们的服务到 config/app.php (你使用别人家的包都需要这步的)
添加一行 Aex\Packagetest\PackagetestServiceProvider::class
下一步添加配置文件:
在 src 目录下添加 config 目录然后添加文件 packagetest.php 内容如下:
<?php
return [
    'options' => [] // 只是为了演示
];
下一步创建我们的服务真正逻辑实现的代码: 在src目录下创建文件 Packagetest.php 内容如下:
<?php
namespace Aex\Packagetest;
use Illuminate\Session\SessionManager;
use Illuminate\Config\Repository;
class Packagetest
{
    /**
     * @var SessionManager
     */
    protected $session;
    /**
     * @var Repository
     */
    protected $config;
    /**
     * Packagetest constructor.
     * @param SessionManager $session
     * @param Repository $config
     */
    public function __construct(SessionManager $session, Repository $config)
    {
        $this->session = $session;
        $this->config = $config;
    }
    /**
     * @param string $msg
     * @return string
     */
    public function test_rtn($msg = ''){
        $config_arr = $this->config->get('packagetest.options');
        return $msg.' <strong>from your custom develop package!</strong>>';
    }
}
下一步创建视图文件:在src目录下添加views目录然后添加 packagetest.blade.php
@extends('layouts.app')
@section('content')
    <h1>Packagetest Message</h1>
    {{$msg}}
@endsection
下一步创建门面(Facades): 在src目录下添加 Facades目录然后添加 Packagetest.php
<?php
namespace Aex\Packagetest\Facades;
use Illuminate\Support\Facades\Facade;
class Packagetest extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'packagetest';
    }
}
然后命令行执行 :
composer dump-autoload
这样就能够使用命名空间 Aex\Packagetest 了,上面在 config/app.php 下添加的服务那行就真正生效了。(如果不执行 dump-autoload 运行程序会报错,说找不到类)
既然我们定义了门面 那么我们就可以为这个服务添加别名了。在 config/app.php  的 aliases 数组添加一行:
'Packagetest' => Aex\Packagetest\Facades\Packagetest::class
现在我们的目录结构类似:
3
至此代码其实就已经跑通了,但是还没有完全完成。我们先测试下试试,随便找个 controller 当然 route要定义好:例如:TestController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Packagetest;
class TestController extends Controller
{
    public function test(Request $request){
        $a = Packagetest::test_rtn('Aex');
        return view('Packagetest::packagetest',['msg'=>$a]);
    }
}
然后根据路由访问就可以看到效果啦。为什么说没有完全完成呢?因为 视图文件和 config 配置文件还在我们的包里定义,以后发布出去,包会在 vendor目录下,这些文件不应该在vendor下修改
所以命令行执行:
php artisan vendor:publish --provider="Aex\Packagetest\PackagetestServiceProvider"
//  --provider 参数指定了要发布的服务 你也可以省略来发布所有的
发布后你就会在 laravel 本身的 config目录 和 views/vendor/packagetest 下看到你的文件了,也就可以按照需求随意修改了。
最后我们说 修改的laravel 的composer.json ,我们要发布我们的包,让所有人都能使用 composer 安装,那么执行如下步骤
去掉 添加的 那行 "Aex\\Packagetest\\": "packages/aex/packagetest/src/" 然后 修改 packages/aex/packagetest/composer.json
添加 autoload:
"autoload": {
    "psr-4": {
        "Aex\\Packagetest\\": "src/"
    }
}
这样包就是一个完整独立的包了,然后把他提交到你的 GitHub 上。
提交到 github 上的我的目录结构是:
4
我的地址是:https://github.com/ALawating-Rex/packagetest-for-laravel 有需要参考的可以参考下。
接着就是把包提交到 packagelist了, 网址: https://packagist.org/ 如果没有账户则注册一个
然后点击 submit ,填写项目URL,点击check
5
成功后点击  submit 就完成了。 至此你的包就可以像其它人的一样通过 composer require 安装了 6
如上图,两个箭头分别代表了包名称 和 版本
所以安装这个包的时候你的 composer.json 在require可以加这样一行:
"aex/packagetest-for-laravel": "dev-master"
安装之前我们先把我们之前开发的这个包都删除吧,就假设是一个别人的 laravel 框架要用我们的包: 删除 packages 文件夹
删除 config/packagetest.php
删除 resources/views/vendor/packagetest
conifg/app.php 里面删除添加的服务和别名
controller 里的改动就保留吧,因为安装完还是要这么写一遍 最后执行 composer dump-autoload
下面安装这个自定义包吧: composer update aex/packagetest-for-laravel
然后添加服务: 修改 config/app.php 添加
Aex\Packagetest\PackagetestServiceProvider::class
和别名的配置:
'Packagetest' => Aex\Packagetest\Facades\Packagetest::class
执行 composer dump-autoload
发布资源文件: php artisan vendor:publish --provider="Aex\Packagetest\PackagetestServiceProvider"
测试通过 大功告成!
额外的:
1.在 packagelist 你的这个包页面可以看到提示了 Set Up GitHub Service Hook 你可以按照提示办法安装,安装完成后,一旦你的项目有push,这里就会跟着更新。
2.还是 packagelist 页面,可以看到目前你只有  dev-master 版本,假设你需要其它的版本 你可以去你的 github 项目添加 tag
git tag 1.0.0 && git push --tags
这样composer require 就可以指定别的版本了。
3.为了别人能够更加清晰的使用你的包,完善你的 Readme 吧
4.不是必须laravel 框架,单纯的 composer 扩展包开发也是按照这个步骤来,只不过需要你摘出 laravel 结合的部分。

laravel blade 模版引擎指令详解和使用举例

laravel blade 模版引擎
laravel blade 模版引擎
定义片段
@section('content')
   xxxx
@show
show 表示同时显示
@section('ccc')
     xxxx
@endsection
使用 endsection 不会主动显示,只有 yield 指定的地方 和 @section ...@show 的地方会显示
显示指定片段的内容
@yield('content')
扩展布局 (继承)
// 指定子页面所继承的布局
@extend('layouts.app')
此时 这个 子页面(视图)将会使用 @section 指令注入内容到布局的片段中
子页面和父页面都有定义 @setion('content') 片段,则子页面会覆盖父页面
如果想使用追加可以使用 @parent
@section('content')
   @parent
   new - xxxx
@show
@parent指令在渲染的时将被替换为布局中的内容
注:
使用 section ... show  然后又使用了 section ... endsection (无论是在布局还是子视图中)那么show 里面的会被 endsection 定义的 覆盖
想要追加 那么要在 endsection 区添加 @parent
如果是
section ... show
section ... endsection
section ... show
那么两处show 都会被 endsection 里的内容替代。 但是要注意此时 第二个 section...show 会跑到页面的最上面,可以通过查看网页源代码发现。
 
输出 php 代码有以下方式:
1.{{ $var }}
2.{!! $var !!}
3.<?php echo $var; ?>
第三种就不用说了,第一种和第二种其实都是相当于 echo 但是第一种是 echo e($var) 调用了辅助函数e()  --> 定义:
function e($value)
{
    if ($value instanceof Htmlable) {
        return $value->toHtml();
    }
    return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
}
即有经过 html实体字符转换
第二种就是 echo $var 了  具体这些都可以参考 storage/framework/views/下编译出来的文件查看 原生内容 。
如果想让 {{}} 不被 slade 引擎转义 那么可以使用 @符号 @{{$var}} 这样就会原样输出
如果很大一块代码需要原样输出:  verbatim 指令
@verbatim
    <div class="container">
        Hello, {{ name }}.
    </div>
@endverbatim
组件和插槽:
组件和插槽可以是 section 和 layout应用更灵活,且 也很容易理解。页面需要复用的东西可以通过 组件和插槽实现,例如:
我们在 controller 里定义要渲染的视图是 test 即 return view('test')
然后在 test.blade.php 里首先 extends('layouts.app') 然后需要引入提示消息的视图,明显这个可以在各个页面复用,那么可以使用 组件和插槽的方式:
test.blade.php:
@extends('layouts.app')
@section('content')
    <h1>test blade2</h1>
    @parent
    <hr/>
    @component('alert',['foo' => 'bar','tt'=>'TTTTHHH'])
        aaaa
        @slot('title')
            Forbidden
        @endslot
        @slot('foo')
            barbarbar
        @endslot
        @slot('aex','哈哈哈哈')
        You are not allowed to access this resource!
    @endcomponent
@endsection
alert.blade.php:
<div class="alert alert-danger">
    <div class="alert-title">{{ $title }}</div>
    {{ $slot }}
    <h1>{{$foo}}</h1>
    <h1>{!! $foo !!}</h1>
    <h2>{{$tt}}</h2>
    <h3>{{$aex}}</h3>
</div>
那么由此 上面的 section 会被放到 layouts.app 里 yield 定义的content 这些都是之前的概念了。
componet 定义了 alert 于是就会去渲染 alert.blade.php 后面的数组可以不定义,定义了就是往alert视图注入变量 即:$foo = 'bar', $tt = 'TTTTHHH' 这样就可以做到把 controller里的变量传递给 alert视图了
后面的 @slot 指令其实也是相当于 $title = 'Forbidden'
还可以写为 @slot('aex','哈哈哈哈')  即 $aex = '哈哈哈哈';
最后你会发现还有不再 @slot指令里的内容 即 aaaa 和 You are not allowed to access this resource! 他们会被放到 alert 模版 $slot 指定的地方。
感兴趣的仍然可以去看下 framework 编译的原始文件 ,大致会看到 $__env->startComponent('alert',['foo' => 'bar','tt'=>'TTTTHHH']) ... 这样的内容,
然后看下 startComponent 的定义以及其它的函数定义 就大致理解工作流程了(主要使用了 ob_start 和 ob_get_clean 等)。 定义文件在:\vendor\laravel\framework\src\Illuminate\View\Concerns\ManagesComponents.php
子视图:
Blade 的 @include 指令允许你很轻松地在一个视图中包含另一个 Blade 视图,所有父级视图中变量在被包含的子视图中依然有效:
<div>
    @include('shared.errors')
    <form>
        <!-- Form Contents -->
    </form>
</div>
尽管被包含的视图可以继承所有父视图中的数据,你还可以传递额外参数到被包含的视图:
@include('view.name', ['some' => 'data'])
堆栈:
前端页面  js我们通常希望放到页脚,不同页面引入的 js 又不一样,想要做到这点就可以使用堆栈功能。
例如在布局文件 app.blade.php 最下面 </body> 上面加入 @stack('scripts') 然后在你要渲染的视图 例如 test.blade.php 将js推送入栈即可
@push('scripts')
    <script type="text/javascript" src="/jquery.min.js"></script>
@endpush
服务注入:
视图中大部分变量是通过 controller 传递的,但是有的时候我们仍需要调用 laravel 的一些类获取相应的值,那么可以使用 @inject 指令
@inject 指令可以用于从服务容器中获取服务,传递给 @inject 的第一个参数是服务将要被分配到的变量名,第二个参数是要解析的服务类名或接口名:
@inject('metrics', 'App\Services\MetricsService')
<div>
    Monthly Revenue: {{ $metrics->monthlyRevenue() }}.
</div>
你也可以这样使用:
<?php
    var_dump(App\User::where('id',1)->first()->toArray());
?>

laravel 框架以及关键概念重新理解

laravel 重新理解
首先 laravel 核心就是一个 Ioc (依赖注入 )容器。框架本身需要的很多东西其实就是各个服务,每一个服务的实现我们可以称为服务容器。
服务举例,有auth 认证服务、cache缓存服务、DB数据库服务、fileSystem文件系统服务 等等,当然你也可以自己添加第三方的 excel 服务 zip服务等。
有了各种各样的服务来实现我们的需求,那么如何使用这些服务呢?即服务的绑定和解析。
首先服务需要被绑定,类似变量赋值一样,我们使用这个变量就相当于用这个值,那么服务绑定到某个名称上,例如:

$this->app->singleton('cache', function ($app) {
return new CacheManager($app);
});

这样当我们通过某种方式调用 cache这个名称 的时候就相当于调用了 cache 服务。cache 服务的实现主要就是 上述代码 CacheManager 中实现的。
服务绑定的方式:
简单绑定:
$this->app->bind('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});
第一个参数是想要注册的类名或者接口名,第二个参数是返回类的实例的闭包。
绑定一个单例:
singleton 方法绑定一个只需要解析一次的类或接口到容器,然后接下来对容器的调用将会返回同一个实例:
$this->app->singleton('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});
绑定实例 :
你还可以使用 instance 方法绑定一个已存在的对象实例到容器,随后调用容器将总是返回给定的实例:
$api = new HelpSpot\API(new HttpClient);
$this->app->instance('HelpSpot\Api', $api);
绑定原始值 :
你可能有一个接收注入类的类,同时需要注入一个原生的数值比如整型,可以结合上下文轻松注入这个类需要的任何值:
$this->app->when('App\Http\Controllers\UserController')
    ->needs('$variableName')
    ->give($value);
类似的还有上下文绑定
有时侯我们可能有两个类使用同一个接口,但我们希望在每个类中注入不同实现,
例如,两个控制器依赖 Illuminate\Contracts\Filesystem\Filesystem 契约的不同实现。
Laravel 为此定义了简单、平滑的接口:
use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\VideoController;
use App\Http\Controllers\PhotoControllers;
use Illuminate\Contracts\Filesystem\Filesystem;
$this->app->when(PhotoController::class)
    ->needs(Filesystem::class)
    ->give(function () {
        return Storage::disk('local');
    });
$this->app->when(VideoController::class)
    ->needs(Filesystem::class)
    ->give(function () {
        return Storage::disk('s3');
    });
绑定接口到实现
服务容器的一个非常强大的功能是其绑定接口到实现。我们假设有一个 EventPusher 接口及其实现类RedisEventPusher ,
编写完该接口的 RedisEventPusher 实现后,就可以将其注册到服务容器:
$this->app->bind(
    'App\Contracts\EventPusher',
    'App\Services\RedisEventPusher'
);
这段代码告诉容器当一个类需要 EventPusher 的实现时将会注入 RedisEventPusher,
现在我们可以在构造器或者任何其它通过服务容器注入依赖的地方进行 EventPusher 接口的依赖注入:
use App\Contracts\EventPusher;
/**
 * 创建一个新的类实例
 *
 * @param  EventPusher  $pusher
 * @return void
 */
public function __construct(EventPusher $pusher){
    $this->pusher = $pusher;
}
绑定有了 ,接下来就是考虑怎么使用的问题了。即 解析:
make方法 :
有很多方式可以从容器中解析对象,首先,你可以使用 make 方法,该方法接收你想要解析的类名或接口名作为参数:
$fooBar = $this->app->make('HelpSpot\API');
如果你所在的代码位置访问不了 $app 变量,可以使用辅助函数resolve
$api = resolve('HelpSpot\API');
自动注入 :
最后,也是最常用的,你可以简单的通过在类的构造函数中对依赖进行类型提示来从容器中解析对象,
控制器、事件监听器、队列任务、中间件等都是通过这种方式。在实践中,这是大多数对象从容器中解析的方式。
容器会自动为其解析类注入依赖,例如,你可以在控制器的构造函数中为应用定义的仓库进行类型提示,该仓库会自动解析并注入该类:
<?php
namespace App\Http\Controllers;
use App\Users\Repository as UserRepository;
class UserController extends Controller{
    /**
     * 用户仓库实例
     */
    protected $users;
    /**
     * 创建一个控制器实例
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }
}
至此服务的使用也就理清楚了。回过头来,再考虑我们说了服务绑定,但是细致的说 还要讲明白绑定声明在哪里。
也就是概念:服务提供者  (serviceProvider) 所有的的 laravel 服务都是通过服务提供者启动的。
即所有的服务都继承自 Illuminate\Support\ServiceProvider 类。大部分服务提供者都包含两个方法: register 和 boot 。
在 register 方法中,你唯一要做的事情就是绑事物到服务容器,不要尝试在其中注册事件监听器,路由或者任何其它功能。
通过 Artisan 命令 make:provider 即可生成一个新的提供者:
php artisan make:provider RiakServiceProvider
register方法
正如前面所提到的,在 register 方法中只绑定事物到服务容器,而不要做其他事情,否则,一不小心就能用到一个尚未被加载的服务提供者提供的服务。
现在让我们来看看一个基本的服务提供者长什么样:
<?php
namespace App\Providers;
use Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider{
    /**
     * 在容器中注册绑定.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton(Connection::class, function ($app) {
            return new Connection(config('riak'));
        });
    }
}
该服务提供者只定义了一个 register 方法,并使用该方法在服务容器中定义了一个 Riak\Contracts\Connection 的实现。
boot方法
如果我们想要在服务提供者中注册视图 composer 该怎么做?这就要用到 boot 方法了。
该方法在所有服务提供者被注册以后才会被调用,这就是说我们可以在其中访问框架已注册的所有其它服务:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider{
    /**
     * Perform post-registration booting of services.
     *
     * @return void
     */
    public function boot()
    {
        view()->composer('view', function () {
            //
        });
    }
}
我们也可以在boot方法中类型提示依赖
use Illuminate\Contracts\Routing\ResponseFactory;
public function boot(ResponseFactory $response){
    $response->macro('caps', function ($value) {
        //
    });
}
这样服务提供者有了,服务就能顺利的被调用了。再接着往回想,那么服务提供者又是怎么注册声明的呢?
即:注册服务提供者
所有服务提供者都是通过配置文件 config/app.php 中进行注册,该文件包含了一个列出所有服务提供者名字的 providers 数组,
默认情况下,其中列出了所有核心服务提供者,这些服务提供者启动 Laravel核心组件,比如邮件、队列、缓存等等。
要注册你自己的服务提供者,只需要将其追加到该数组中即可:
'providers' => [
    // 其它服务提供者
    App\Providers\ComposerServiceProvider::class,
],
注: 在这里声明的服务,laravel在启动的时候会自动的运行所有服务提供者的内容。
延迟加载服务提供者
框架启动的时候只启动必要的服务,其它不必要的服务在用的时候启动这就是延迟加载服务提供者,这才是合理的或者说好的启动方案。
想要延迟加载一个提供者,设置 defer 属性为 true 并定义一个 provides 方法,该方法返回该提供者注册的服务容器绑定,
可以自行参考 RedisServiceProvider (声明 defer 以及 注册了多个服务绑定)
终于一个完整的服务提供,注册,绑定,解析流程完成了。
接下来为了使服务的调用更加简单,Laravel 使用了 Facades(门面)
门面:
门面为应用服务容器中的绑定类提供了一个“静态”接口。我们完全可以只使用服务,例如
$value = resolve('cache')->get('key'); // laravel 要解析 cache ,通过服务提供者列表找到 cache 的服务提供者 进而找到 服务绑定 的类实现,然后最终调用其 get方法
但是有了门面,我们调用起来就更加简单明了
use cache;
$value = cache::get('key');
上面的例子,内部工作流程实际上是:
laravel 在 config/app.php 里面声明的别名 aliases 找到 cache对应的门面类: Illuminate\Support\Facades\Cache::class
这个里面主要声明了方法:getFacadeAccessor   返回 cache  这次这个cache对应的就是服务容器绑定的名称了,
服务提供者会在启动的时候绑定 cache 对应的类实现,那么laravel自然就找到了对应的实现,进而调用其 get方法。
而对开发来说表面看起来就像是直接调用 cache这个类的 get静态方法一样,语义明确。
其实浏览门面的基类代码,Facade 就会发现魔术方法 __calStatic() 这里去调用指定的方法。
契约:
Laravel 中的契约是指框架提供的一系列定义核心服务的接口,Contracts(契约)使得实现更加的松耦合和简单
举例看一个紧耦合代码:
<?php
namespace App\Orders;
class Repository
{
    protected $cache;
    /**
     * 创建一个新的Repository实例
     *
     * @param  \SomePackage\Cache\Memcached  $cache
     * @return void
     */
    public function __construct(\SomePackage\Cache\Memcached $cache)
    {
        $this->cache = $cache;
    }
    /**
     * 通过ID获取订单
     *
     * @param  int  $id
     * @return Order
     */
    public function find($id)
    {
        if ($this->cache->has($id))    {
            //
        }
    }
}
在类初始化的时候自动注入 \SomePackage\Cache\Memcached 类对象,但是如果下次想换成 redis 等,就不得不来这里修改 注入的类。
那么如果使用契约呢:
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository
{
    /**
     * 创建一个新的Repository实例
     *
     * @param  Cache  $cache
     * @return void
     */
    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }
}
松耦合:
无论 cache 怎么变,实现处不用修改代码,所以降低了耦合性。
简单:
当所有 Laravel 服务都统一在简单接口中定义,很容易判断给定服务提供的功能。契约可以充当框架特性的简明文档
契约的使用:
Laravel中很多类都是通过服务容器进行解析,包括控制器,以及监听器、中间件、队列任务,甚至路由闭包。所以,要实现一个契约,需要在解析类的构造函数中类型提示这个契约接口。
契约和门面不同之处:
从使用上的不同,我们在使用门面的时候都是主动 声明 哪个门面,然后调用其"静态"方法,
而门面更多的是声明到类的构造函数中,系统会自动注入,接着就可以使用了。
契约允许你在类中定义显示的依赖,这样更加明确。

 

 

标准数据中国省市区+银行支行数据

之前做地址级联,总是需要省市区地址,网上的各种数据又不甚相同。一直想如果有一个统一的标准就好了,经过一番搜索,果然有官方版本:中华人民共和国行政区划代码 那么剩下的工作就是把他整理出来了。满足跨平台使用且可以灵活操作修改,甚至整理后导入数据库的就是 json 啦。所以整理如下: gitHub地址:ALawating-Rex/AreaAndBanks

码云地址: Aex / AreaAndBanks

当然支行数据是通过简单的爬虫程序爬下来的,并不是官方数据,因为官方数据我也找不到 = =!

抛砖引玉:
现在省市区地址的需求越来越多,但是始终缺少一个官方标准,当然 中华人民共和国民政部-中华人民共和国行政区划代码 这里有官方数据,
却不是程序员拿来即用的,开发也不应该在这里浪费时间,所以希望能够有一个统一的标准数据,value和code能够统一真的能够带来极大的便利。
尤其和快递对接的系统多少也会遇到过城市名不匹配的情况吧。
虽然银行支行数据这个使用案例相对较少了,但是标准数据几乎找不到。以上这些数据难道不是应该公开透明且以服务为目的的吗?
所以希望抛砖引玉,有人能整理一个标准数据。

map

Magento 手机支付 (支付宝无线支付)

 现在magento 支付宝插件在网上还是很多的。但是支付宝无线支付貌似还不多。这里我只是根据官方文档和其它

支付方式编写出无线支付的雏形,可用但是不完善,先记录在这里。(当然文章是点到为止,只给我这种菜鸟看看,大神无视我吧。)

---------------------------------------------------我就是分割线怎么了---------------------------------------------------

首先肯定是要熟悉支付宝官方给出的文档

手机网页即时到账接口 和 手机网页支付接入与使用规则 以及 demo 代码

之后开始梳理流程:

(一下是在默认情况下,不要纠结个别变量的不同)

1. 在magento中我们点击结账,如果支付方式选择的是第三方支付方式会跳转到相应支付方式Paymentcontroller里的redirectAction

在模块的system.xml 中加入你自己的后台配置代码

2. 在这里magento最主要的是 记录$order以及createBlock 输出html

$this->getResponse()
            ->setBody($this->getLayout()
                ->createBlock('alipaymobile/redirect')
                ->setOrder($order)
                ->toHtml());

3. 在输出的页面 例如是支付宝或者快钱等支付方式通常是隐藏的form表单包含要提交的信息,以及自动提交的js

但是支付宝无线支付有些不同,从官方文档中得知 我们首先需要 获得授权即调用接口手机网页即时到账授权接口

然后才是 调用接口手机网页即时到账交易接口

那么我的解决思路是在 Block/Redirect.php 中先去掉用授权接口然后是代码自动调用即时到帐接口完成结账。

这时候看到的网页是要跳转两次的。

4. 首先调用授权接口 隐藏的form表单提交的 url地址应当是 里一个例如AlipayapiController

并且主要代码都是在这个controller中实现的

//建立请求
        $alipaySubmit = new AlipaySubmit($alipay_config);
        $html_text = $alipaySubmit->buildRequestHttp($para_token);

 

//获取request_token
        $request_token = $para_html_text['request_token'];

 

//建立请求
        $alipaySubmit = new AlipaySubmit($alipay_config);
        $html_text = $alipaySubmit->buildRequestForm($parameter, 'get', '确认');

注意:按这种方法其中出现的一个重要问题是会获取不到 notifyUrl 和 SuccessUrl, (除非你直接写的绝对路径,而不是从model中获取的)

是因为 在model中 getSuccessURL 和 getNotifyURL 都是使用protected声明的。 最简单的解决办法是改成public

以我之愚见好像没必要声明成protected

5. 处理success代码 和 notify代码即可。

 

 

 

 

这完成了结账流程没错,但是只是90%吧,因为还有重要的前端处理流程。

即PC端 是不需要显示这个支付方式的 一种解决办法是

在template/checkout/onpage/payment/methods.phtml 中找到代码

foreach ($methods as $_method):
        $_code = $_method->getCode();

在后面加上

if($_code == "alipaymobile_payment"){
            continue;
        }

即可,当然这里的 alipaymobile_payment  要自己根据实际情况来定了。

另一个需要修改的地方是我的订单包括历史订单页面(我这里实在之前开发了继续支付功能)

当你在手机选择了 支付宝无线支付 但是没有做付款操作,转而用电脑继续,那么此时在订单付款按钮处就

不应该选择支付宝无线支付了。可以使用

$paymentsAllMethods = Mage::getSingleton('payment/config')->getActiveMethods();

过滤出可用的支付方式即可。

至此 就基本完成了。大家凑合看吧,其实有了官方文档,加上对magento有些了解花点时间就可以写出来的

我只不过写下我自己的解决办法。 不喜勿喷!欢迎指正!

 

懒得开发的可以联系我

但我不会去帮你定制。(哎呀,我的服务竟然这么差!)

 

---------------------------------------------------我就是分割线怎么了---------------------------------------------------

这是发在CSDN的第一篇文章,所以必须是原创的!

最开始在新浪上写博客,后来到百度空间,我只想说百度空间让我几乎没有了写博客的欲望。

总之现在又想乱写点东西了,给自己看也好。

PS: 会陆续把我印象笔记里的东西拿过来。

 

 

此文章通过 python 爬虫创建,原文是自己的csdn 地址: Magento 手机支付 (支付宝无线支付)

 

微信支付 —— 公众号支付代码详解

在微信支付 开发者文档页面 下载最新的 php SDK

http://mch.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

这里假设你已经申请完微信支付

1. 微信后台配置  如图

我们先进行测试,所以先把测试授权目录和 测试白名单添加上。测试授权目录是你要发起微信请求的哪个文件所在的目录。

例如jsapi 发起请求一般是jsapi.php所在目录 为测试目录,测试白名单即开发人员的微信号。

正式的支付授权目录不能和测试的一样否则会报错。不填写或者填错授权目录以及测试白名单都会报错。

报错样例:

NaNsystem:access_denied

不在测试白名单

2. 配置 lib/WxPay.Config.php文件

最主要配置一下四项:

const APPID = '';
const MCHID = '';
const KEY = '';
const APPSECRET = '';

APPID 和 APPSECRET都可以在微信后台中找到。

MCHID 在申请微信支付后发来的邮件中可以找到,KEY 则根据邮件提示

去商户平台配置即可。

3. 访问起始 index.php 

首先访问 index.php 你可以看到界面

我们首先需要的是 JSAPI支付。但是看代码 index.php 最下面的链接。他默认是个demo的链接,改为我们自定义的即可

<ul>
    <li style="background-color:#FF7F24"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/jsapi.php';?>">JSAPI支付</a></li>
    <li style="background-color:#698B22"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/micropay.php';?>">刷卡支付</a></li>
    <li style="background-color:#8B6914"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/native.php';?>">扫码支付</a></li>
    <li style="background-color:#CDCD00"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/orderquery.php';?>">订单查询</a></li>
    <li style="background-color:#CD3278"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/refund.php';?>">订单退款</a></li>
    <li style="background-color:#848484"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/refundquery.php';?>">退款查询</a></li>
    <li style="background-color:#8EE5EE"><a href="<?php echo 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].'example/download.php';?>">下载订单</a></li>
</ul>

当然你也可以直接写死为自己的访问链接。

4. JSAPI 支付

必要代码解析:

$logHandler= new CLogFileHandler("../logs/".date('Y-m-d').'.log');
$log = Log::Init($logHandler, 15);

调用日志类 可以通过 $log->DEBUG('test'); 打印调试信息。其实也可以直接使用 $Log::DEBUG('test'); 来调试

$tools = new JsApiPay();
$openId = $tools->GetOpenid();

主要是为了获取 openid 其中GetOpenid() 函数定义在 文件 WxPay.JsApiPay.php 文件中

public function GetOpenid()
	{
		//通过code获得openid
		if (!isset($_GET['code'])){
			//触发微信返回code码
			$baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$_SERVER['QUERY_STRING']);
			$url = $this->__CreateOauthUrlForCode($baseUrl);
			Header("Location: $url");
			exit();
		} else {
			//获取code码,以获取openid
		    $code = $_GET['code'];
			$openid = $this->getOpenidFromMp($code);
			return $openid;
		}
	}

$baseUrl 其实就是为了在跳转回来这个页面。  可以继续跟踪函数__CreateOauthUrlForCode()  其实就是通过微信的Auth2.0 来获取Openid

参考链接:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html

这就需要你把微信的 网页授权接口也设置好。

获取到 Openid 就可以调用微信支付的统一下单接口了。回到 文件 jsapi.php 如下代码

$input = new WxPayUnifiedOrder();
$input->SetBody("test");
$input->SetAttach("test");
$input->SetOut_trade_no(WxPayConfig::MCHID.date("YmdHis"));
$input->SetTotal_fee("1");
$input->SetTime_start(date("YmdHis"));
$input->SetTime_expire(date("YmdHis", time() + 600));
$input->SetGoods_tag("test");
$input->SetNotify_url("http://paysdk.weixin.qq.com/example/notify.php");
$input->SetTrade_type("JSAPI");
$input->SetOpenid($openId);
$order = WxPayApi::unifiedOrder($input);
echo '<font color="#f00"><b>统一下单支付单信息</b></font><br/>';
printf_info($order);
$jsApiParameters = $tools->GetJsApiParameters($order);

这里面的代码:

$input->SetAttach("test");

如果 把值改为 $input->SetAttach("test this is attach");就会存在bug 后面再说,其实这个参数不是必须的干脆可以去掉。

代码:

$input->SetNotify_url("http://paysdk.weixin.qq.com/example/notify.php");

是设置接收支付结果通知的Url 这里是默认的demo 链接我们可以设置成我们的:

$input->SetNotify_url(dirname('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']).'/notify.php');

当然你也可以选择直接写死。

其中的函数 unifiedOrder($input) 可以到WxPay.Api.php 中文件跟踪,其实就是调用统一下单接口。

在 WxPay.Api.php 中需要更改的一处代码是:

//异步通知url未设置,则使用配置文件中的url
        if(!$inputObj->IsNotify_urlSet()){
            $inputObj->SetNotify_url(WxPayConfig::NOTIFY_URL);//异步通知url
        }

就是当没设置 notifyUrl 的时候回去配置文件中找,但是配置文件中根本没有设置。

所以你可以选择在 配置文件WxPay.Config.php 中加上这个配置,也可以直接写一个默认的notify链接。

函数 GetJsApiParameters() 是获取jsApi支付的参数给变量 $jsApiParameters 方便在下面的Js中调用

jsapi.php 中js的代码:

function jsApiCall()
	{
		WeixinJSBridge.invoke(
			'getBrandWCPayRequest',
			<?php echo $jsApiParameters; ?>,
			function(res){
				WeixinJSBridge.log(res.err_msg);
				alert(res.err_code+res.err_desc+res.err_msg);
			}
		);
	}

	function callpay()
	{
		if (typeof WeixinJSBridge == "undefined"){
		    if( document.addEventListener ){
		        document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
		    }else if (document.attachEvent){
		        document.attachEvent('WeixinJSBridgeReady', jsApiCall); 
		        document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
		    }
		}else{
		    jsApiCall();
		}
	}

其中点击立即支付按钮调用的就是 callpay() 函数,他有会调用jsApiCall() 函数打开支付程序。

此后输入密码完成支付。

在完成支付页面点击完成会回到这个支付页面,并弹出 支付成功的提示框

这个其实就是 js函数 jsApiCall 里面的alter 弹出的对话框

其中 res.err_msg 为get_brand_wcpay_request:ok 表明前端判断的支付成功,我们可以根据这个将支付跳转到成功页面。

但是这个并不可信。确认是否支付成功还是应当 通过notify.php 处理业务逻辑。

5. 支付结果通知 notify.php

其实这个页面最主要的代码就两行

$notify = new PayNotifyCallBack();
$notify->Handle(false);

其中大部分逻辑在 Handle 函数中处理 文件 WxPay.Notify.php

final public function Handle($needSign = true)
	{
		$msg = "OK";
		//当返回false的时候,表示notify中调用NotifyCallBack回调失败获取签名校验失败,此时直接回复失败
		$result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg);
		if($result == false){
			$this->SetReturn_code("FAIL");
			$this->SetReturn_msg($msg);
			$this->ReplyNotify(false);
			return;
		} else {
			//该分支在成功回调到NotifyCallBack方法,处理完成之后流程
			$this->SetReturn_code("SUCCESS");
			$this->SetReturn_msg("OK");
		}
		$this->ReplyNotify($needSign);
	}

主要代码:

$result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg);

跟踪函数 notify 文件WxPay.Api.php

public static function notify($callback, &$msg)
	{
		//获取通知的数据
		$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
		//如果返回成功则验证签名
		try {
			$result = WxPayResults::Init($xml);
		} catch (WxPayException $e){
			$msg = $e->errorMessage();
			return false;
		}
		
		return call_user_func($callback, $result);
	}

通过 $GLOBALS['HTTP_RAW_POST_DATA']; 获取同志数据 然后 Init 函数验证签名等。验签成功运行代码

return call_user_func($callback, $result);

即调用了一个回调函数,NotifyCallBack() 函数并传递参数 $result 在NotifyCallBack函数中会调用我们重写的NotifyProcess()函数(此函数在notify.php 中被重写)

NotifyProcess() 判断也没有问题就会 设置返回 success的xml信息

$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");

并最终调用函数 $this->ReplyNotify($needSign);  echo success的结果

函数ReplyNotify 需要修改一处代码:

final private function ReplyNotify($needSign = true)
	{
		//如果需要签名
		if($needSign == true && 
			$this->GetReturn_code($return_code) == "SUCCESS")
		{
			$this->SetSign();
		}
		WxpayApi::replyNotify($this->ToXml());
	}

$this->GetReturn_code($return_code) == "SUCCESS")

改为

$this->GetReturn_code() == "SUCCESS")

即可。

这样整个流程就结束了。上面提到了 传递订单参数

$input->SetAttach("test");

如果我设置 值为 test this is attach (其实只要有空格就会存在bug)

如图 传递的订单信息

可以看到 attach 信息正常,当然支付也是正常的没有任何问题。

但是发现总是会收到notify 通知,即意味着没有返回给微信服务器正确的结果通知。

打印服务器发来的通知数据

可以看到 attach 是 test+this+is+attach 即空格被转化为加号

打印接收到的签名和程序算出来的签名发现 签名不同,即认为接收结果异常。

所以我们要是想使用attach 这个值就不能有空格,要么干脆不使用这个参数

(等待微信修复这个bug, 也可能是我这边有哪个地方不会? - -#)

这样 微信支付的 JsApi支付就大致分析完成了。

补充:

1. 程序还有几处拼写错误,例如WxPay.JsApiPay.php的 GetOpenidFromMp 函数

其中设置超时代码

curl_setopt($ch, CURLOP_TIMEOUT, $this->curl_timeout);

修改为

curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);

而且 变量 $this->curl_timeout 也没有赋值,初始化。 你可以根据实际自己在类的声明下面初始化并赋值。

2. 不允许跨号支付 提示

是因为你在一个订阅号中打开了这个支付链接,而在微信中订阅号是没有支付权限的。但是在一个有支付权限的服务号或者将链接直接发给朋友

或者发到朋友圈却是可以支付的。

有的时候你先通过订阅号访问支付链接提示不允许跨号支付,然后再发给朋友这个时候再打开仍有不允许跨号支付的提示出现。(目前是这样,应该算是个bug)

如果你要避免这些问题,其实好的解决办法应该是使用微信的扫码支付了。(扫码支付模式二)下一节会解释下微信扫码支付。

当然具体使用哪种支付方式要依据实际情况来定。

此文章通过 python 爬虫创建,原文是自己的csdn 地址: 微信支付 —— 公众号支付代码详解

微信支付—— 扫码支付

个人认为扫码支付比Jsapi支付从开发和使用上要顺心的多。扫码支付不用担心是PC端还是移动浏览器还是微信客户端访问的问题,生成一个二维码,扫描支付即可。

一些配置和代码SDK以及SDK存在的错误可以参考上一篇
微信支付
的文章

http://blog.csdn.net/m0sh1/article/details/45199711

 

友情提示以下内容实在简陋 - -# 如果你跑通了 Jsapi支付,那么扫码支付其实没什么太特别的。

扫码支付发起支付的文件在 example SDK 中的 native.php 文件中

扫码支付有两种支付方式,在使用扫码支付之前必须配置 支付回调URL具体配置 参考

微信开发者文档 http://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_3

两种支付方式的介绍

模式1:http://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4

模式2:http://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5

 

其实模式2 不需要设置回调URL 但是一旦你修改了支付配置,而且要使用扫码支付就必须勾选Native原生支付,此时回调URL也就是必填项了

但是我只想用模式2 ,模式2 用不到回调URL,只好胡乱写了个以后使用模式1可能使用的URL了。

代码分析:

扫码支付模式1

$notify = new NativePay();
$url1 = $notify->GetPrePayUrl("123456789");

主要看一下 扫码支付模式2

$input = new WxPayUnifiedOrder();
$input->SetBody("test");
$input->SetAttach("test");
$input->SetOut_trade_no(WxPayConfig::MCHID.date("YmdHis"));
$input->SetTotal_fee("1");
$input->SetTime_start(date("YmdHis"));
$input->SetTime_expire(date("YmdHis", time() + 600));
$input->SetGoods_tag("test");
$input->SetNotify_url("http://paysdk.weixin.qq.com/example/notify.php");
$input->SetTrade_type("NATIVE");
$input->SetProduct_id("123456789");
$result = $notify->GetPayUrl($input);
$url2 = $result["code_url"];

二维码:

<img alt="模式二扫码支付" src="http://paysdk.weixin.qq.com/example/qrcode.php?data=<?php echo urlencode($url2);?>" style="width:150px;height:150px;"/>

首先实例化类WxPayUnifiedOrder,然后设置一些支付所需的参数,并将所需参数传给函数 GetPayUrl()

函数定义在 example/Wxpay.NativePay.php 文件中

public function GetPayUrl($input)
	{
		if($input->GetTrade_type() == "NATIVE")
		{
			$result = WxPayApi::unifiedOrder($input);
			return $result;
		}
	}

 

$result = WxPayApi::unifiedOrder($input);

此处代码即为 调用统一下单接口 ,代码位于 文件 lib/WxPay.Api.php 其中部分代码

if($inputObj->GetTrade_type() == "JSAPI" && !$inputObj->IsOpenidSet()){
			throw new WxPayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
		}
		if($inputObj->GetTrade_type() == "NATIVE" && !$inputObj->IsProduct_idSet()){
			throw new WxPayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
		}

判断支付方式,如果是 JsApi 方式需要Openid

Native 方式必须需要product_id , 顺便再吐槽一下,判断是否是Native 支付方式,缺少product_id 提示的 后面竟然是 JSAPI 必填product_id ,

唉,真是不能再马虎了,SDK都可以这么马虎的写写也没sei了

在之后就是调用统一下单接口流程了。

 

函数执行完毕会返回一个 weixin:// 开头的链接,再调用phpqrcode 程序生成二维码即可。

支付结果处理页面仍使用notify.php 文件中的处理逻辑就可以了。

此文章通过 python 爬虫创建,原文是自己的csdn 地址: 微信支付—— 扫码支付

 

Magento 微信支付

Magento 微信支付

根据前面几节介绍的微信支付分析这里大致介绍一下magento中微信支付功能的编写思路,用户可以选择微信支付(JsApi)也可以选择微信扫码支付(Native).
首先在magento 的app/code/local 下创建模块 并在 module设置active 为true
大致目录结构如图
这里写图片描述
转载请注明出处:http://blog.csdn.net/m0sh1/article/details/45449679
编写 etc/system.xml 为后台配置模块
可以将微信支付所需要的 appid appsecret mchid 和 key等信息配置到这里。这样在后台启用这个模块就会在前台支付过程中显示这个支付方式了。
当我们点击确认支付的时候 其实是调用了相关支付方式Model中的
getOrderPlaceRedirectUrl() 函数。我们可以在这个函数中设定下一步要走的流程。
例如:

public function getOrderPlaceRedirectUrl()
    {
        return Mage::getUrl('wxpay/wxselect');
    }

即调用 WxselectController.php 中的indexAction()方法,一般其它支付是直接跳转到相应的PaymentController.php 中的redirectAction() 方法,这里这么做的目的是展示给用户一个选择支付方式的页面。因为如果用户在PC端打开或者在移动浏览器中打开是不能使用微信JsApi支付的,(即使在微信中还可能存在跨号支付的问题)所以同时展示出来微信扫码支付的支付方式,当然你可以在代码上通过判断智能的实现支付方式的选择。
在选择页面用户选择了支付方式后,跳转到 PaymentController.php中的indexAction() 方法中。
核心代码如下:

$order = $this->getOrder();
if (!$order->getId())
{
    $this->norouteAction();
    return;
}

$order->addStatusToHistory(
    $order->getStatus(),
    Mage::helper('customer')->__('微信支付')
);

$order->save();

$this->getResponse()
    ->setBody($this->getLayout()
        ->createBlock('wxpay/redirect')
        ->setOrder($order)
        ->toHtml());

程序实例化 Block 展示微信支付页面。在Block 中获取order

$order = $this->getOrder();

JsApi 所需要的参数 通过Model 获取

$jsApiParameters = $model->createPackageInfo($order,$currentUrl);

如果是Native 方式则代码为:

$urlParam = $model->createPackageInfoNative($order);

因为微信支付需要设定支付发起的目录(具体可参见前面的微信支付相关文章)并且只有在这个目录下支付才能生效。
而magento(很多mvc 框架都是)是通过router形式运行的。我大致测试了一下设定支付目录就是 controller下的一个方法,支付没有成功,也可能我编写的有不对的地方,总之没有继续深究,而是干脆把支付放到了magento 根目录下的自定义目录下,例如是wxp目录。
这时候要做的操作就是微信支付所需要的参数 以get形式传递给相应的支付发起文件就可以了。(get形式就可以了,看了应该下没有什么需要保密的内容,有建议欢迎指出。)
例如 jsapi方式那么直接创建文件 jsapi.php
部分代码:

<?php
$jsApiParameters=$_GET['jsApiParameters']
?>

然后将参数传给 js 就可以了
同理 native.php
最后就是 编写 notify 函数,我设定的notify都是通知给 PaymentController.php中的notifyAction()方法
部分代码:

$notify = new PayNotifyCallBack();
$resCheck = $notify->Handle(false);
if($resCheck){
// 验证签名等成功
$xmlBackData = $GLOBALS['HTTP_RAW_POST_DATA'];
$array_data = json_decode(json_encode(simplexml_load_string($xmlBackData, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
$out_trade_no = $array_data['out_trade_no'];
$model = Mage::getModel('wxpay/payment');
$order = Mage::getModel('sales/order')->loadByIncrementId($out_trade_no);
// 获取到了order 那么此时就可以对这个order 做状态更改了

大致思路就是这样,这是第一次使用CSDN 的MarkDown 编辑器编写的使用起来还不错 ╭(′▽`)╯
转载请注明出处:http://blog.csdn.net/m0sh1/article/details/45449679

 

此文章通过 python 爬虫创建,原文是自己的csdn 地址: Magento 微信支付

 

Laravel 图片处理扩展包intervention/image的基本使用

首先是安装:

composer require intervention/image

安装他需要你的 php版本大于等于 5.4
还需要有 fileinfo 扩展,你可以通过 phpinfo() 查看扩展是否安装,也可以通过命令:

php --ri fileinfo 查看

我使用的是 lnmp 1.4 的一键安装,没有 fileinfo扩展
那么首先安装这个扩展:

1.首先确认你的系统中有 fileinfo 这个扩展源码,可以使用find 查找

find / -name fileinfo

2.如果没有,那么你可以再去php官网下载一份和你的php版本对应的源安装包
例如我的是 php 5.6.31

wget http://cn2.php.net/get/php-5.6.31.tar.gz/from/this/mirror
mv mirror php-5.6.31.tar.gz
tar -zxvf php-5.6.31.tar.gz
cd php-5.6.31/ext/fileinfo

此时就在 fileinfo 目录了。
3.你可以在这里直接执行命令

phpize

就会看到 configure文件
4.再执行如下命令:

./configure --with-php-config=/usr/local/php/bin/php-config
make && make install

此时完成了 fileinfo 扩展的编译工作
5.添加 fileinfo 到到 php.ini
修改 php.ini 添加 `extension=fileinfo.so`
6.重启 php-fpm 和 nginx 完成 fileinfo 扩展的安装

此时再执行

composer require intervention/image

就不会报错了
接下来laravel 添加这个服务
修改 config/app.php 在 providers 添加

Intervention\Image\ImageServiceProvider::class,

在 aliases 中添加

'Image' => Intervention\Image\Facades\Image::class,

这样就可以在 controller 里使用了

use Image;
// ...
// 代码
// ...
$img = Image::make('upload/a.png')->resize(50,100);  //修改尺寸
if(file_exists(public_path('new_image/'))){
}
else{
mkdir ( public_path('new_image/'), 0777);
}
$img->save('new_image/a_new.png',75); // 保存到别的路径,第二个参数设置压缩质量

上面的 make 传参是图片路径,这个路径是public 下的路径
在save 的时候首先要判断目录存在,如果不存在需要先创建,否则报错。如果是好几层的目录需要递归创建
参考函数:

public static function mkMutiDir($dir){
if(!is_dir($dir)){
if(!self::mkMutiDir(dirname($dir))){
return false;
}
if(!mkdir($dir,0777)){
return false;
}
}
return true;
}

Image::make 接收的参数不局限于图片文件 具体参考:
http://image.intervention.io/api/make
所以在 Laravel 中上传的文件也可以直接 make

$file = $request->file('img');
$img = Image::make($file);
//...

接下来你可以参考api 文档根据实际使用啦!

 

laravel 错误处理,接口错误返回json

Laravel 默认已经为我们配置好了错误和异常处理,我们在 App\Exceptions\Handler 类中触发异常并将响应返回给用户。
所有异常都由类App\Exceptions\Handler处理,该类包含两个方法:report和render
report方法只是将异常传递给异常被记录的基类,我们这里主要是针对 render 的修改:

先看代码:

public function render($request, Exception $exception)
{
    if($request->is('api/*')){
        $response = [];
        $error = $this->convertExceptionToResponse($exception);
        $response['status'] = $error->getStatusCode();
        $response['msg'] = 'something error';
        if(config('app.debug')) {
            $response['msg'] = empty($exception->getMessage()) ? 'something error' : $exception->getMessage();
            if($error->getStatusCode() >= 500) {
                if(config('app.debug')) {
                    $response['trace'] = $exception->getTraceAsString();
                    $response['code'] = $exception->getCode();
                }
            }
        }
        $response['data'] = [];
        return response()->json($response, $error->getStatusCode());
    }else{
        return parent::render($request, $exception);
    }
}

$request->is('api/*') 用于判断是否是 接口请求

$response 数组你可以依据你的接口返回格式统一一下。