Gem 微信支付 Gem

jasl · 2014年11月11日 · 最后由 hungyuhei 回复于 2015年12月25日 · 14233 次阅读

大会办完以后就去补工作了...总结都没来及做 - -

这两天在做微信支付的集成,基于最新的V3协议,于是做了这个gem https://github.com/jasl/wx_pay ,目前只实现了统一支付接口。

使用方式看ReadMe吧

PS1:微信支付的文档和演示代码里也有不少错误,浪费了非常多的时间在这上边。等不忙了再写个演示项目出来。

PS2:其实已经有很多同类的Gem了,比如 https://github.com/RaymondChou/Rwepayhttps://github.com/jasl/jasl_tenpay ,这些都是基于老版协议的,也仍然可用

共收到 55 条回复

支持, 刚好 最近也要用 微信和支付宝支付

我也写了个 https://github.com/HungYuHei/wechat_pay 说起来微信支付的文档真是看得蛋疼

:plus1:

#2楼 @HungYuHei 关键文档坑,就光说签名算法,他给了个运算步骤,好了,我用它的样本做测试用例吧,结果那个文档里计算结果是错的...

另外一方面JSAPI在微信浏览器下几乎无法调试,解决浏览器端各种交互的时候有一种回到北大青鸟的感觉 - -

#5楼 @jasl 这个gem可以在产品环境用吗?

#7楼 @flowerwrong KnewOne 已经在生产环境使用了两个多月了,我仅仅实现了API中的支付功能因为目前我们只需要这一个 只有KO需要其他API的时候,我才会去实现,并应用到我们的生产环境中。

#8楼 @jasl 我遇到了一个问题,不知道你有没有遇到过。我测试支付的时候显示 商户签名失败 , 网上的大多是没有拿到 prepay_id ,但是我这边是拿到了的。问题出在js那里:

function onBridgeReady(){
   WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {
           "appId" : "wx2421b1c4370ec43b",     //公众号名称,由商户传入     
           "timeStamp":" 1395712654",         //时间戳,自1970年以来的秒数     
           "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串     
           "package" : "prepay_id=u802345jgfjsdfgsdg888",     
           "signType" : "MD5",         //微信签名方式:     
           "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
       },
       function(res){     
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 
       }
   ); 
}
if (typeof WeixinJSBridge == "undefined"){
   if( document.addEventListener ){
       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
   }else if (document.attachEvent){
       document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 
       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
   }
}else{
   onBridgeReady();
} 

paySign 我不知道应该是填写预下单的时候发送的 sign ,还是返回的数据中的 sign ,两个sign不一样。我还试过自己生成一个新的。但都是报 商户签名失败

@flowerwrong 看文档还需要拼接 package 的内容

#9楼 @flowerwrong paySign 填写传给js里的参数的签名,就是

{
           "appId" : "wx2421b1c4370ec43b",     //公众号名称,由商户传入     
           "timeStamp":" 1395712654",         //时间戳,自1970年以来的秒数     
           "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串     
           "package" : "prepay_id=u802345jgfjsdfgsdg888",     
           "signType" : "MD5"         //微信签名方式:   
}

这些的签名

@jasl paySign 就是 invoke_unifiedorder 成功之后,微信返回的参数里面的 sign 吧? 然后微信的 JSAPI 里面对 js 的 package 有这些说明:

//然后进行最后一步,这里按照key=value除了sign外进行字典序排序后组成下列的字符串,最后再串接sign=value
var completeString = "bank_type="+banktype+"&body="+body+"&fee_type="+fee_type+"&input_charset="+input_charset+"&notify_url="+notify_url+"&out_trade_no="+out_trade_no+"&partner="+partner+"&spbill_create_ip="+spbill_create_ip+"&total_fee="+total_fee;
                completeString = completeString + "&sign="+md5SignValue;

需要重新拼接 package 字符串,然后放到 js button 里面吧?否则确实一直出现签名错误

#10楼 @robot_zhang package拼了,是签名时大小写不对。比如 appId , 小写就不行。

#11楼 @jasl :plus1: 谢谢,大小写都必须一样

#11楼 @jasl 谢谢你的wx_pay gem,让我省了不少时间。代码也很漂亮。

#12楼 @robot_zhang 不是的,是需要利用11楼的hash再加上key,然后sort~>md5

@js_noncestr = SecureRandom.uuid.tr('-', '')
@js_timestamp = Time.now.getutc.to_i.to_s
@app_id = app_id
@package = "prepay_id=#{@ra[:r]['prepay_id']}"

params_pre_pay_js = {
    appId: @app_id,
    nonceStr: @js_noncestr,
    package: @package,
    timeStamp: @js_timestamp,
    signType: 'MD5'
}
@js_pay_sign =     WxPay::Sign.generate(params_pre_pay_js)

#12楼 @robot_zhang 楼上的额做法是对滴~

@flowerwrong 对的,package 只需要 prepay 就行了 感谢 @jasl

#15楼 @flowerwrong 我也是这么生成签名的,但还是报 fail_invalid signature

#18楼 @nancy 用我库里带的算法吧,或者按照这个来实现,KO生产环境验证有半年了,最近看了下微信支付的最新文档,3.3.7 这块API没有变动过 如果有问题90%可能是配置有问题

#19楼 @jasl 用的你的gem, 很好用,谢谢啦,我再看看配置

微信回调时,出现Can't verify CSRF token authenticity 这个问题,大家怎么处理的啊?直接去掉么?

#21楼 @beyondyuqifeng 去掉就可以了

不知 是否可以使用SHA 加密呢?

你好,请问一下,WxPay里的trade_type难道不支持APP,看你的介绍只支持JSAPI、NATIVE。但是官方文档里面都支持的啊

#25楼 @qicaisheng 理论是支持的,不过因为我目前没有环境去测试APP下的情况,所以避免误导没有写在文档里,如果你可以帮忙测试,通过后请告知我,更新文档、

#24楼 @pathbox 貌似微信支付不支持SHA签名吧?

#27楼 @jasl 刚做了app的微信支付 统一下单、查询订单。生成prepay_id签名方法一样。但是APP环境下,有2点要注意。 1.要将appid, noncestr(第一次签名用的随机字符串),package(设置为'Sign=WXPay'),partnerid (商户ID),timestamp,prepayid(第一次签名生成的)再按之前的签名规则生成签名sign,然后随同prepay_id,一块返回给APP客户端。 2.二次签名传参数时候,appid、noncestr、package、partnerid、timestamp、prepayid 这些参数名 全是小写,不是appID、nonceStr 这样的大小写

#27楼 @jasl 还请教一下,我按照你这样填下notify_url,然后在route.rb里配置 post "notify" => "orders#notify",并在对于controller对于action里执行订单处理信息。 但是发现没有执行到对应的action,这可能是什么原因啊?

#29楼 @qicaisheng 关于APP方面配置的情况,麻烦帮忙更新一下 wxpay的文档吧~ 感谢!

这个问题要看下日志了,可能是你路由的配置问题,文档里我的只是为了示意 实际上我们的系统里是这样配置的路由

resources :orders do
  member do
    post 'wxpay_notify'
  end
end
# will generate route like /orders/:id/wxpay_notify

还是CSRF 的问题,加上skip_before_filter :verify_authenticity_token 就好了。 我研究ruby 和 rails 才2个多月,还没写过gem ,也没用你wx_pay这个gem ,只是参照了里面的方法。 我晚上再研究一下,再更新啊

#31楼 @qicaisheng 辛苦~ CSRF这个是Rails的防重放的机制,但是对于API是没有必要的,看来还是写到文档上比较好~

#32楼 @jasl 提交新分支了

#33楼 @qicaisheng 看到了,昨天忙,我稍微重构下合并,感谢!

#20楼 @nancy 请教一个,你的问题解决了吗。请问是怎么解决的? 谢谢!

@jasl 调用统一订单接口,一直提示签名失败,还没找到原因,帮忙给个定位思路?可能是xml编码问题吗? *错误结果 Key: return_code Value: FAIL Key: return_msg Value: 签名错误

*代码

@params = {
      body: 'pay one cent',
      out_trade_no: 'test003', #"test_order_#{Time.now.to_i.to_s}",
      total_fee: 1,
      spbill_create_ip: '127.0.0.1',
      notify_url: 'http://origin.xxx.com.cn/blogs/show/2',
      trade_type: 'JSAPI', # could be "JSAPI", "NATIVE" or "APP",
      openid: @blog.title # required when trade_type is `JSAPI`
    }

    @params = WxPay::Service.invoke_unifiedorder @params

gem算出的签名值和微信签名调试工具计算出来的值是一样的。http://mch.weixin.qq.com/wiki/tools/signverify/

#36楼 @caiqinghua 找到原因了,key配错了,配置为appsecret了。@jasl 到处都是坑啊 此处要填商户key,而不是appsecret

看到成功的response,哈皮

Key: return_code Value: SUCCESS

Key: return_msg Value: OK

#38楼 @jasl 最后调用javascript时,需要把jsApiParameters传到javascript,请问如何优雅的实现? https://ruby-china.org/topics/9844 这里的方法不好。

//调用微信JS api 支付
        function jsApiCall()
        {
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest',
                <?php echo $jsApiParameters; ?>,  #这行在rails中如何实现?
                function(res){
                    WeixinJSBridge.log(res.err_msg);
                    //alert(res.err_code+res.err_desc+res.err_msg);
                }
            );
        }

@caiqinghua 可以用 <%= xx %> 来实现, 也可以用 ajax 来取. xx 可以用对应的 controller 中取.

#39楼 @caiqinghua 你发的代码erb本身就是可以这么用地~ 不过我是在控制器直接渲染好json 然后这样写

WeixinJSBridge.invoke(
        'getBrandWCPayRequest',
        <%= raw @params %>,
        function (res) {
          if (res.err_msg == "get_brand_wcpay_request:ok") {
            window.location.href = '<%= wxpay_callback_order_path(@order) %>';
          } else if (res.err_msg == "get_brand_wcpay_request:cancel") {
            window.location.href = '<%= order_path(@order) %>';
          } else if (res.err_msg == "get_brand_wcpay_request:fail") {
            alert('系统错误');
          } else {
            alert('未知错误');
          }
        }
);

#41楼 @jasl 控制器端是怎么写的? 这有问题吗?

def wechat_pay_js_params
    @params = {
        appid: 'wx7d3xxxxxxxxc',
        prepay_id: 'wx201506150843452f821f7b070705485969'
      }
    _prepay_id = @params[:prepay_id]
    @params = {
        appId: 'WxPay.appid',
        timeStamp: Time.now.to_i.to_s,
        nonceStr: SecureRandom.uuid.tr('-', ''),
        package: "prepay_id=#{_prepay_id}",
        signType: "MD5"
      }
    render json: @params
  end

我用ajax死活不成功,关键没有定位思路

#42楼 @caiqinghua @jasl ajax获取变量也成功了,最后调用getBrandWCPayRequest,返回invalid appid错误,猜测是支付授权目录没有设置正确。rails这支付授权目录要怎么设置?php非MVC的比较简单。

#43楼 @caiqinghua 也不难吧 比如你设置支付授权目录 https://knewone.com/orders/

那发起支付使用 https://knewone.com/orders/wxpay 是ok的 那么route定义

resources :orders do
  collection do
    get 'wxpay'
  end
end

然后实现wxpay action就可以了,只要在这个url里调用微信js支付即可 回调因为是在参数里指定,所以用restful风格就是rails的标准实践来搞没有任何问题

AJAX不成功恐怕要靠你自己分析了,这个自己的代码别人不好帮

另外根据我的经验,微信给的错误信息不能全信,比如你支付授权目录设置的有问题,给你的报错不靠蒙这辈子也想不通是支付授权目录设置的问题,解决方法就是,一个尽可能的多alert信息(微信支付只能在微信浏览器里调试,console.log没有),一个要多注意检查配置。

#44楼 @jasl 昨天晚上已经调通了,是授权目录设置问题,之前的猜测是正确的,还要注意授权目录设置后10分生效,请耐心等待

#22楼 @jasl skip_before_action :verify_authenticity_token 还是提示WARN -- : Can't verify CSRF token authenticity

#46楼 @caiqinghua 这样估计是你的代码问题啦,CSRF和Gem无关了

get_brand_wcpay_request:fail 怎么解

 function onBridgeReady(){
        WeixinJSBridge.invoke(
                'getBrandWCPayRequest',{
                   "appId": "<%= @r['appid']%>",
                     "timeStamp": "<%= Time.now.to_i.to_s %>",
                     "nonceStr": "<%= @r['nonce_str']%>",
                     "package": "prepay_id=<%= @r['prepay_id']%>",
                     "signType": "MD5",
                     "paySign": "<%= @r['sign'] %>"
                 },
                 function(res){
alert(res.err_msg);
                    if(res.err_msg == "get_brand_wcpay_request:ok"){
                            alert('ok');
                        }else if(res.err_msg == "get_brand_wcpay_request:cancel") {
                        }else if(res.err_msg == "get_brand_wcpay_request:fail"){
                            alert('fail');
                        }else{
                            } 
                 }
            );   
     }
     if (typeof WeixinJSBride == "undefined"){
         if (document.addEventListener){
             document.addEventListener('WeixinJSBridgeReady',onBridgeReady,false); 
            } else if (document.attachEvent){
             document.attachEvent('WeixinJSBridgeReady',onBridgeReady);
             document.attachEvent('onWeixinJSBridegReady',onBridgeReady);
            }
        }else{
          onBridgeReady();  
        }

请问fail是什么原因?

#49楼 @shindouhiro 微信支付文档里有...手头文档删了,这个查下就了解了

51楼 已删除

Wxpay可以支持扫描支付吗?统一下单支付能支持PC端的使用吗?我算出paySign了发现不知道怎么继续下去?请指导

#52楼 @liwen_zhang 支持的,你只要按照微信支付文档的要求调整统一支付接口的参数即可

#2楼 @hungyuhei 我准备调用你写的gem 结果浪费了我半天的时间,最后果断放弃了。

#54楼 @yuan 我那个 gem 是基于微信支付 v2.x API 的,如果你是 v3.X 就应该用楼主那个。如果你是 v2.x 在使用过程中有什么问题,欢迎在 github 上提 issue,确认后我会尽快修复

56楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册