大会办完以后就去补工作了...总结都没来及做 - -
这两天在做微信支付的集成,基于最新的 V3 协议,于是做了这个 gem https://github.com/jasl/wx_pay ,目前只实现了统一支付接口。
使用方式看 ReadMe 吧
PS1:微信支付的文档和演示代码里也有不少错误,浪费了非常多的时间在这上边。等不忙了再写个演示项目出来。
PS2:其实已经有很多同类的 Gem 了,比如 https://github.com/RaymondChou/Rwepay 、 https://github.com/jasl/jasl_tenpay ,这些都是基于老版协议的,也仍然可用
#2 楼 @HungYuHei 关键文档坑,就光说签名算法,他给了个运算步骤,好了,我用它的样本做测试用例吧,结果那个文档里计算结果是错的...
另外一方面 JSAPI 在微信浏览器下几乎无法调试,解决浏览器端各种交互的时候有一种回到北大青鸟的感觉 - -
#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 不一样。我还试过自己生成一个新的。但都是报 商户签名失败
。
#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+"¬ify_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
, 小写就不行。
#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)
你好,请问一下,WxPay 里的 trade_type 难道不支持 APP,看你的介绍只支持 JSAPI、NATIVE。但是官方文档里面都支持的啊
#25 楼 @qicaisheng 理论是支持的,不过因为我目前没有环境去测试 APP 下的情况,所以避免误导没有写在文档里,如果你可以帮忙测试,通过后请告知我,更新文档、
#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 这样的大小写
#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 ,只是参照了里面的方法。 我晚上再研究一下,再更新啊
@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);
}
);
}
#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('未知错误');
}
}
);
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 没有),一个要多注意检查配置。
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 是什么原因?
用了你的 Gem,很好用,授权和支付都 ok 了,现在做企业付款到零钱的业务遇到问题,一直返回
{"xml"=>{"return_code"=>"SUCCESS", "return_msg"=>"参数错误:输入的商户订单号有误", "result_code"=>"FAIL", "err_code"=>"PARAM_ERROR", "err_code_des"=>"参数错误:输入的商户订单号有误"}}
订单号是根据时间戳生成的一个,应该不会有问题,不知道有没有企业付款到零钱的示例代码可以参考一下
API 的封装得看大伙努力了,我现在不做微信支付的开发了,这个你看下微信支付的文档看下是不是调用的参数有问题吧,看样子是个小问题,是某个参数不对。
另外可以提一个 issue 在 Github 上,看看有没人能帮忙
@jasl 已经解决了,问题出在调用 to_xml 方法把参数 hash 转化成 xml 的时候,它把下划线转化成短横线了,加上 to_xml(dasherize: false) 参数就好了
response = RestClient::Request.execute(
method: :post,
url: url,
payload: payload.to_xml(dasherize: false),
ssl_client_cert: WxPay.apiclient_cert,
ssl_client_key: WxPay.apiclient_key
)
再次感谢你的 gem,节省不少时间,么么哒