hardywen / wxpay

WeiXin Payment
11 stars 7 forks source link

Please add Laravel 5 support #1

Open unizhu opened 9 years ago

unizhu commented 9 years ago

thanks

denghongcai commented 9 years ago

take a look at my fork Repo

hardywen commented 9 years ago

Thanks, @denghongcai

unizhu commented 9 years ago

@denghongcai 你的 repo我装了,然而没能理解如何使用...并且..你的 repo 里面没法 open issue. 所以只能写在这里. 首先有个小 bug 是在laravel 5.1 下:config/app.php: 需要写成:'Wxpay' => 'HongcaiDeng\Wxpay\Facades\Wxpay', 用::class 是会报错的.使用的时候用 \Wxpay

另外重要的是:

  1. 我始终没能实现在订单页面使用 \Wxpay::instance('jsApi')->setConfig($config)->pay(); 它只会跳转到我配置的 js_api_call_url ,但是没法使用,因为这个 url 只会拿到code. 然而我并不知道应该跳转到哪个订单. 照理应该跳转到 wxpay::pay 这个 view 上.我试图在执行 Wxpay::instance('jsApi')->setConfig($config)->pay(); 后 return 到 wxpay::pay .然而并没什么用,执行完后是直接返回了.
  2. 然后我进行了第二种尝试, 直接在我订单页面使用 \Wxpay::instance('jsApi')->setConfig($config)->getParameters(); 把 parameters 传到 jssdk 的 invoke 方法里,然而, parameters 里面 prepay_id=空. 微信支付显示 failed : appId;

以上尝试方法的配置是基于我原先拿微信的官方 SDK 改得,自己改得那个可以使用,但是没整合过 laravel. 所以基本上,固定的配置参数是对的,除了那个 js_api_call_url .

所以我还是不会用你的 vendor .. 或许你可以把使用方法写的更详细一点?

denghongcai commented 9 years ago

更新了下readme

理解下这个过程,js_api_call_url是可以在使用时改变的,config里的只是个参考

$wxpay = Wxpay::instance('jsApi');

$config = [
    'body' => $order->activity->content,
    'total_fee' => 1,
    'out_trade_no' => $order->oid,
    'sub_mch_id' => '',
    'device_info' => '',
    'attach' => '', // 附加数据
    'time_start' => '',
    'time_expire' => '',
    'goods_tag' => '',
    'product_id' => '',
    'js_api_call_url' => action('OrderController@pay', ['id' => $order->id])
];
list($prepay_id, $view) = $wxpay->setConfig($config)->pay();

然后你拿到了返回的$view,如果在控制器里就直接return $view就好了

看看代码就知道了,还有些API我实现了但是没写在readme里

hardywen commented 9 years ago

@unidotnet 你问题的第1点:它只会跳转到我配置的 js_api_call_url ,但是没法使用,因为这个 url 只会拿到code. 然而我并不知道应该跳转到哪个订单. 关键就是这个js_api_call_url要设置成你订单的页面。比如 /orders/pay/1 这样子就知道要支付 id =1的订单了。(由于微信的特殊要求,必须得设置成 /xxx/xxx/{id} 这种形式。) 组件的流程基本集中在 pay() 这个方法里了,流程为:获取code -> 通过code获取openid -> 组装参数 (这里会调用一次统一支付接口,拿到prepay_id)-> 把参数传给views -> views里的js 发起支付。希望能帮到你。

unizhu commented 9 years ago

@denghongcai @hardywen 谢谢. 目前我是这样写的: $this->values['timeStamp'] = (string)$value; 发起支付页面controller里:

$config = array(
            'body'=>'xxx',
            'attach'=>'xxx',
            'goods_tag'=>'xxx',
            'total_fee' =>'1',
            'out_trade_no'=>$order_id,
            'time_start'=>date("YmdHis"),
            'time_expire'=>date("YmdHis", time() + 600),
            'js_api_call_url' => action('OrderController@wechatPayRedirect', ['order_id' => $order_id])
         );
        list($prepay_id, $view) = \Wxpay::instance('jsApi')->setConfig($config)->pay();
        Log::debug('$prepay_id :'.$prepay_id);
        Log::debug('$view :'.$view);
        return $view;

并没有执行,而是到了 OrderController@wechatPayRedirect . 这里返回了一个微信 js 的标准页面,也就是 invoke 的方法.然而.. 微信返回: 缺少必要参数 $key0$ 按照以前的经验,貌似是要把 timeStamp 强制改成 string , 但是我看了下你的代码, 已经 $jsApiObj["timeStamp"] = "$timeStamp";

还是没法出现支付,请赐教.

hardywen commented 9 years ago

能调用到js的接口了,说明你的流程能跑通了,看错误是缺少参数。 那你你试试在views里, 这句<?php echo $jsApiParameters ?>, 调试一下 看看输出的参数对不对,有没有少什么。 还有就是其实你可以 进vender里直接改源码,做些调试,Log 一下 微信接口的返回值之类的,甚至是 dd() 跳出来调试。

unizhu commented 9 years ago

@denghongcai @hardywen

在wechatPayRedirect里,就是跳转回来的那个 controller 方法里:

$parameters = \Wxpay::instance('jsApi')->getParameters();
print_r($parameters);
return view('wechat_redirect',['$jsApiParameters',$parameters]);

出来的 prepay_id=空. 这个prepay_id应该在第一次 oauth 请求的时候就应该生成了吧? 然后再传回Wxpay. 我猜想是因为Wxpay类不能持久化,所以成员变量都没拉? 怎么样才能让prepay_id正确的出现在getParameters()里?

另外: @denghongcai 我每次运行

[root@centserver test]# php artisan vendor:publish --force --provider="HongcaiDeng\Wxpay\WxpayServiceProvider"
Copied File [/vendor/hongcaideng/wxpay/config/wxpay.php] To [/config/wxpay.php]
Copied Directory [/vendor/hongcaideng/wxpay/resources/views] To [/resources/views/vendor/wxpay]
Publishing complete for tag []!
[root@centserver test]# vim ./vendor/hongcaideng/wxpay/src/HongcaiDeng/Wxpay/JsApi/JsApi.php
[root@centserver test]# php artisan vendor:publish
Copied Directory [/vendor/hongcaideng/wxpay/resources/views] To [/resources/views/vendor/wxpay]
Publishing complete for tag []!
[root@centserver test]# php artisan vendor:publish
Copied Directory [/vendor/hongcaideng/wxpay/resources/views] To [/resources/views/vendor/wxpay]
Publishing complete for tag []!

都会重新复制一遍 view?

unizhu commented 9 years ago

另外

$prepay_id = $this->getUnifiedOrderPrepayId();
        Log::debug('prepay id : '.$prepay_id);
        $this->setPrepayId($prepay_id);

log 里并没任何反应..

denghongcai commented 9 years ago

唉,你没有理解这个的流程……

vendor:publish只需要执行一次……你要明白这个命令是用来干嘛的……

首先,用户在微信里点击链接或者自定义菜单跳转到你的页面,然后点击支付,这个时候进入的是

action('OrderController@pay', ['id' => 1])

这个url,然后这个控制器的代码是

 public function pay($orderId)
    {
        $order = Order::find($orderId);
        if(is_null($order)) {
            return response(Response::HTTP_NOT_FOUND);
        }
        $wxpay = Wxpay::instance('jsApi');
        $config = [
            'body' => $order->activity->content,
            'total_fee' => 1,
            'out_trade_no' => $order->id,
            'sub_mch_id' => '',
            'device_info' => '',
            'attach' => '', // 附加数据
            'time_start' => '',
            'time_expire' => '',
            'goods_tag' => '',
            'product_id' => '',
            'js_api_call_url' => action('OrderController@pay', ['id' => $order->id])
        ];
        list($prepay_id, $view) = $wxpay->setConfig($config)->pay();
        return $view;
    }

这时在调用pay()这个方法的时候,由于还没拿到微信网页授权的code,取不到openid,也就没法发起支付。在pay()里的代码会直接调用redirect()跳转到授权URL进行snapi_base形式的回调授权,回调的返回地址就是

action('OrderController@pay', ['id' => $order->id])

从微信返回之后还是回到这个控制器里来了,但是回调过来的URL里有CODE这个GET参数,走到pay()这里之后我们的包会帮你获取用户的openid并且将参数准备好填入到view里,这时才会走到

return $view

这里,将包里带的最简单的view渲染出来,唤起微信支付的界面,接下来的事情就是在支付完成后处理js的回调并且等待服务器推送的确认 确认方法的示例如下

public function payNotify(Request $req)
    {
        $wxpay = Wxpay::instance('jsApi');
        list($notify, $data) = $wxpay->verifyNotify();
        if($notify) {
            Log::info($data);
            $order = Order::find($data['out_trade_no'])->first();
            $order->status = config('constants.ORDER_STATUS_PAID');
            $wxpay->setReturnParameter('return_code', 'SUCCESS');
            $wxpay->returnXML();
        }
        else {
            Log::info($data);
            $wxpay->setReturnParameter("return_code","FAIL");//返回状态码
            $wxpay->setReturnParameter("return_msg","签名失败");//返回信息
            $wxpay->returnXml();
        }
    }

代码没太好的格式化,凑合看吧

这样说还是没明白那我就……

unizhu commented 9 years ago

@denghongcai 谢谢. 我终于明白了原来是同一个 action... 不好意思请原谅我才用 laravel 2周时间. 改成 'js_api_call_url' => action('OrderController@wechatPayMobile', ['order_id' => $order_id]) 就可以了进行支付了.

接着是微信支付的验证,也就是 notify 我设置的 url 是 /Payment/wechatpay/callback 但是一直是验证失败.... 再次请教.

denghongcai commented 9 years ago

callback这个控制器参考我的示例就好……

如果不对就把数据都打印到log里慢慢找

unizhu commented 9 years ago

@denghongcai @hardywen 2位目前想换个工作吗?我正在招人。。

kevineves commented 9 years ago

@denghongcai 在你的repo中 notify.php line14 checkSign方法生成本地签名时需要将$data中原有的sign值unset掉

@unidotnet 遇到验证失败的问题估计就是这个bug导致的

denghongcai commented 9 years ago

@kevineves 确实是的,我的问题,我本地的代码里unset了sign,但是发布库里没改

unizhu commented 9 years ago

@denghongcai @kevineves 谢谢,今天试了下可以了. 另外再问个问题,后台验证成功后,设置哪个 url 可以跳转回我的网站页面?现在是个空白页面...

denghongcai commented 9 years ago

跳转需要改view里的js的

gamelife1314 commented 9 years ago

@denghongcai 那再laravel框架下授权目录应该设置成什么?

denghongcai commented 9 years ago

我在这里回复你吧 @gamelife1314

1.我在获取code的时候,返回以下错误信息,您是否能帮我解释一下:
array:2 [▼ "errcode" => 40029 "errmsg" => "req id: dB1j50900ns39, invalid code" ]

code会在网页授权之后回调redirect_uri返回,只能使用一次,你看你是不是多次调用了获取access_token的接口

确保参数都对,没什么大问题

gamelife1314 commented 9 years ago

@denghongcai 好的,谢谢,我去再尝试尝试

gamelife1314 commented 9 years ago

@hardywen @denghongcai @unidotnet @kevineves 真的很着急, :sob: 请问授权目录该怎么样设置?出现了access_denied错误; 我发起支付的路由是http://hostname/order/wxpay/{id},按照这样,应该怎么样设置才好?

gamelife1314 commented 9 years ago

谢谢各位的帮助,我自己debug出来了!