w7corp / easywechat

📦 一个 PHP 微信 SDK
https://easywechat.com
MIT License
10.27k stars 2.4k forks source link

微信支付相关文档细化 #138

Closed yangweijie closed 8 years ago

yangweijie commented 8 years ago

在用你的微信类做微信支付功能, 微信配置商户号和key 总算要到了,然后微信unifiedOrder 返回配置总算 看懂要用

    $payment = new Payment($unifiedOrder);
    $payconfig = $payment->getConfig();

去获取,问题是进入下一步

wxjs发起支付,总是提示 get_brand_wxpay_request::fail_invalid signature

网上搜了很多,有的说是支付授权目录不对,有的说是参数大小写顺序之类的。第二个应该可以排除,因为能下单。

第一个。我的项目是分前后端,前端3demo.sinaapp.com/wxbuy 第二部是 /wxorder 出现按钮,点击支付调回调。 而后端是 3api.sinaapp.com/weixin/wxpayNotify post方式。 授权目录 设为 3api.sinaapp.com/ 或者3api.sinaapp.com/weixin/ 都没用, 以为是发起支付前的页面地址 3demo.sinaapp.com/ 或者 3demo.sinaapp.com/wxorder/ 都不对。

不知道如何调试这个签名错误呢?楼主有没有调试微信开发的思路 ,或者放一个测试demo。我发现官方的那个php的demo 就可以访问,也就是里面的商户id 是任意使用的,希望楼主能出个 简单的该支付框架 使用微信支付的demo 给大家填填坑

overtrue commented 8 years ago

@0i @tianyong90 帮忙看看这个问题呗

0i commented 8 years ago

金额参数需要是字符串,可以贴一下你的代码吗?

update152 commented 8 years ago

公众账号支付授权目录,再检查下,我之前遇到的就是这个问题 结果是授权目录的问题

yangweijie commented 8 years ago

后端 http://3api.sinaapp.com/weixin/unifiedOrder 这是url。 对应文件是 http://3api.sinaapp.com/app/Http/Controller/WeixinController.php

//创建微信支付订单
    public function unifiedOrder(){
        $fee = Request::input('fee'); //TODO 根据订单读取出来。
        $openid = Request::input('openid');
        slog('调试 下单的微信 openid:');
        slog($openid);
        $order_num = Request::input('order_num');
        $orderInfo = DB::table('order')->where('order_num', $order_num)->first();
        if(!$orderInfo){
            warn("订单号为:{$order_num}的订单不存在");
            error('订单不存在');
        }
        //debug
        warn("正在尝试发起订单{$order_num}的微信支付");
        $config = config('wechat');
        slog($config);
        /**
         * 第 1 步:定义商户
         */
        $business = new Business(
            $config['app_id'],
            $config['secret'],
            $config['mch_id'],
            $config['mch_key']
        );

        slog('business');
        slog($business);
        /**
         * 第 2 步:定义订单
         */
        $order = new Order();
        $luckymoneyTypeInfo = DB::table('luckymoney_type')->select('event_id', 'name')->where('id', $orderInfo->type_id)->first();
        if(!$luckymoneyTypeInfo){
            error('获取红包类型信息失败,请刷新重试。');
        }
        $eventInfo = DB::table('event')->select('id', 'name', 'start_time', 'end_time')->where('id', $luckymoneyTypeInfo->event_id)->first();

        $order->body = $eventInfo->name;
        $order->out_trade_no = $order_num.'_t'.date("YmdHis");
        $order->total_fee = $orderInfo->pay_money * 100;    // 单位为 “分”, 字符串类型
        //debug
        $order->total_fee = '1';
        $order->openid = $openid;
        $order->notify_url = 'http://3api.sinaapp.com/weixin/wxpayNotify';

        /**
         * 第 3 步:统一下单
         */
        $unifiedOrder = new UnifiedOrder($business, $order);

        slog(objectToArray($unifiedOrder));
        /**
         * 第 4 步:生成支付配置文件
         */
        $payment = new Payment($unifiedOrder);
        $payconfig = $payment->getConfig();
        // echo $payconfig;
        return success(['order'=>['body'=>$eventInfo->name, 'out_trade_no'=>$order_num, 'total_fee'=>$order->total_fee,'openid'=>$openid, 'notify_url'=>'http://3api.sinaapp.com/weixin/wxpayNotify'], 'config'=>$payconfig]);
    }

    //微信支付结果回调
    public function wxpayNotify(){
        lesslog("begin notify");

        $config = config('wechat');

        $notify = new Notify(
            $config['app_id'],
            $config['secret'],
            $config['mch_id'],
            $config['mch_key']
        );

        $transaction = $notify->verify();

        if (!$transaction) {
            $notify->reply('FAIL', 'verify transaction error');
        }
        slog($transaction);
        lesslog($transaction, '微信支付后的通知');

        // var_dump($transaction);

        echo $notify->reply();
    }

前端的http://3demo.sinaapp.com/app/pay/controller.js

(function(){
    'use strict';
    angular.module('3demo')
    .controller('wxBuyCtrl',function($scope, $stateParams,jconfig,$state,check_in_weixin,$timeout, $cookies){
        var vm = this;
        vm.buy = function(good){
            $state.go('wxorder', {title:good.title, price:good.price*100});
        }
        vm.weixin_openid = '';
        var openid = $cookies.get('weixin_openid');
        //debug openid = '';
        if(openid){
            vm.weixin_openid = openid;
        }
    })
    .controller('wxOrderCtrl',function($scope, $zh,$state,$stateParams,$cookies){
        var vm = this;
        var params = {
            title: $stateParams.title,
            fee: $stateParams.price,
            openid: $cookies.get('weixin_openid')
        }
        vm.loading = true;
        vm.title = params.title;
        vm.price = params.price == 0? 0.01: params.price/100;
        //正式流程是先创建系统订单后调用此接口 会多传order_id
        params.order_num = '320151025150318';
        $zh.post('weixin/unifiedOrder', params).success(function(data){
            vm.loading = false;
            vm.order = angular.fromJson(data.order);
            vm.jsApiCall = function(){
                WeixinJSBridge.invoke(
                    'getBrandWCPayRequest',
                    angular.fromJson(data.config),
                    function(res){
                        if(res.err_msg == "get_brand_wcpay_request:ok"){
                            $state.go('wxorder_result');
                        }else{
                            vm.errmsg = res.err_msg;
                        }
                    }
                );
            }

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

    })
    ;
})();

模板

<div class="container">
    <div class="row" id="order" ng-hide="errmsg" ng-if="!wxOrder.loading">
        <font color="#f00"><b>统一下单支付单信息</b></font><br/>
        <p ng-repeat="(key,value) in wxOrder.order"><font color='#00ff55;'>{{key}}</font> : {{value}}</p>
        <font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">{{wxOrder.order.total_fee/100}}元</span>钱</b></font><br/><br/>
        <div align="center">
            <a ng-click="wxOrder.callpay()" class="btn btn-primary">立即支付</a>
        </div>
    </div>
    <div class="row" ng-show="wxOrder.errmsg">
        发起微信支付 预处理出错, <br>
        出错信息:<pre>{{wxOrder.errmsg}}</pre>
    </div>
</div>

刚试过把 金额改成字符串,还是没用。

支付授权目录 http://www.zhanhuigou.com/mobile/ http://www.zhanhuigou.com/weixin/weixinpay/ http://www.zhanhuigou.com/weixin/weixinpay/demo 测试授权目录http://3api.sinaapp.com/weixin/

yangweijie commented 8 years ago

路由是

//微信支付相关接口
$app->post('/weixin/unifiedOrder','WeixinController@unifiedOrder');
$app->post('/weixin/wxpayNotify','WeixinController@wxpayNotify');
tianyong90 commented 8 years ago

没看出代码里有啥毛病。建议用官方SDK里的签名工具传相同的参数生成签名,然后和overtrue/wecaht生成的签名比对下。这样可以确定是签名本身出错了还是因为配置的授权目录不对。

update152 commented 8 years ago

授权目录写详细点,发起支付url是什么

yangweijie commented 8 years ago

image 发起支付的URL是什么意思啊, 是指调用点立即支付按钮所在页面的地址吗?那个是http://3demo.sinaapp.com/wxorder啊 大家可以 先浏览器打开 http://3demo.sinaapp.com/wxbuy 用二维码插件 微信浏览到啊 @tianyong90 官方sdk 签名工具地址在哪, 然后overtrue/wechat 里哪个方法的生成签名去对比,是unifiedOrder 还是wxpayNotify 里面生成的签名呢,如果 验证成功,前面返回的数据格式的杨莉是什么样字的呢?

我的获取订单返回json是这样的

{
    "code": 0,
    "message": "",
    "data": {
        "order": {
            "body": "\u91cd\u5e86\u56fd\u9645\u535a\u89c8\u4e2d\u5fc3\uff08\u91cd\u5e86 \u60a6\u6765\uff09",
            "out_trade_no": "320151025150318",
            "total_fee": "1",
            "openid": "owca5twvhoHw2W7jkp8b1y2-DYFk",
            "notify_url": "http:\/\/3api.sinaapp.com\/weixin\/wxpayNotify"
        },
        "config": "{\"appId\":\"wx6f390c0b020f9e1e\",\"timeStamp\":\"1445846549\",\"nonceStr\":\"Kna7dSufNLRJEOal\",\"package\":\"prepay_id=wx20151026160229a8b89aba410747207536\",\"signType\":\"MD5\",\"paySign\":\"17C6486C20A8EDA18AC7D0F35C26DAC7\"}"
    }
}
tianyong90 commented 8 years ago

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1 这里可以下载官方的SDK,里面有签名工具。 支付授权目录就是实际发现支付的地址,您这上面有好几个域名,我看晕了。 估计还是授权目录闹的……

yangweijie commented 8 years ago

终于好了,其实问题很曲折,开始做的时候,订单号加了长了超过32字节了。 然后是支付金额 测试写了个1 习惯了 没带‘’ 这个建议sdk传的时候检查一下。因为php是弱类型,经常会拿来运算。 最后一个问题, 签名错误 实际上还是与授权目录有关,因为 实在弄不懂

根据图中栏目顺序进入修改栏目,勾选JSAPI网页支付开通该权限,并配置好支付授权目录,该目录必须是发起支付的页面的精确目录,子目录下无法正常调用支付。 精准目录是什么意思, 有的说是回调地址里 方法前面那个 比如 我回调是 weixin/wxpayNotify 域名是 http://3api.sinaapp.com 那么就写http://3api.sinaapp.com/weixin/ 这是错误的,后来证明,是发起页面所在域名, 比方我有个页面回去掉wxPay,那个方法, 支付目录应该是 域名/pay.html。 而由于我的是angularjs ,http://3demo.sinaapp.com/wxorder 这页面发起的,所以支付目录是去掉wxorder 就是根域名。而且开始我以为签名还与证书有关,那个大家看看文档应该是退款用的。

dhb52 commented 8 years ago

只有操作商户金额的才需要证书。其他业务不需要