reactjs +golang 微信支付坑坑坑

本贴最后更新于 2396 天前,其中的信息可能已经东海扬尘

P81023161420jpg

最近在做支付充值这一块,用的微信支付,发现坑点真不少。由于无 golang 的 sdk 于是拿着 github 上的别人写好的改了改,最终也还是圆满完成了支付这一块。

主要场景:微信支付-H5-微信内发起支付

主要内容:微信网页开发 >jssdk> 微信商户

开发语言:reactjs+golang

大概的主体思路如下每一步的操作都很重要
微信网页开发配置:
1、在微信后台配置域名(目的:获取 openid)
2、jssdk 域名的配置(目的:前端请求的配置)
3、商户后台的域名配置(目的:支付的配置)

主要配置就以上这些(应该没遗漏)
主要文档:网页授权的文档,jssdk wxconfig wxpay 的文档,微信商户支付文档

坑 1、微信授权 redrect_uri 错误(此处偷懒可以用别人的):自己写则仔细检查各种参数以及微信后台的配置即可,总会成功的
示例授权代码:

package controllers import ( "net/url" "charge/models" "github.com/astaxie/beego" mpoauth2 "gopkg.in/chanxuehong/wechat.v2/mp/oauth2" oauth "gopkg.in/chanxuehong/wechat.v2/oauth2" "gopkg.in/chanxuehong/wechat.v2/open/oauth2" ) type Common struct { beego.Controller UserID uint } type WebController struct { Common } func (this *WebController) Prepare() { if !UserInterceptor(&this.Common) { WechatLogin(&this.Common) } } var oauth2Endpoint oauth.Endpoint = mpoauth2.NewEndpoint(Appid, AppSecret) func WechatLogin(this *Common) { code := this.GetString("code") //第一次请求进来时候 if code == "" { this.GoAuth() this.StopRun() return } client := oauth.Client{Endpoint: oauth2Endpoint} if token, err := client.ExchangeToken(code); err != nil { beego.Warning("exchange token error:", err) this.GoAuth() this.StopRun() return } else { if info, err := oauth2.GetUserInfo(token.AccessToken, token.OpenId, "", nil); err != nil { beego.Warning("get userinfo error:", err) this.GoAuth() this.StopRun() return } else { //保存用户信息到数据库 var user models.User if models.DB.Where("openid =?", info.OpenId).First(&user).RecordNotFound() { user.Openid = info.OpenId user.Nickname = info.Nickname if err := models.DB.Create(&user).Error; err != nil { beego.Error("update user error:", err) this.StopRun() return } this.UserID = user.ID this.SetSession("userinfo", user) } else { if err := models.DB.Model(&user).Updates(map[string]interface{}{"Nickname": info.Nickname,}).Error; err != nil { beego.Error("user update error:", err) this.StopRun() return } this.UserID = user.ID this.SetSession("userinfo", user) } } } } func (this *Common) GoAuth() { //此处values uri, _ := url.Parse(beego.AppConfig.String("domain") + this.Ctx.Input.URI()) values := uri.Query() values.Del("code") path := mpoauth2.AuthCodeURL(Appid, beego.AppConfig.String("domain")+ this.Ctx.Input.URL()+ "?"+ values.Encode(), "snsapi_userinfo", this.GetString("token")) this.Redirect(path, 302) this.StopRun() return } func UserInterceptor(ctr *Common) bool { if userinfo := ctr.GetSession("userinfo"); userinfo != nil { user := userinfo.(models.User) if err := models.DB.Where("id=? ", user.ID).First(&user).Error; err != nil { beego.Warning("user read error:", err) return false } ctr.UserID = user.ID return true } return false } //@router /* [*] func (this *WebController) Index() { this.TplName = "react/index.html" } //@router /MP_verify_zpcrQi7YFTe5pSaV.txt [*] func (this *WebController) MP() { this.TplName = "MP_verify_zpcrQi7YFTe5pSaV.html" }

1、请求 ------> 服务器---302-----> 微信服务器(用户同意)

2、微信服务器--code--> 服务器

3、服务器----code-> 微信服务器----(用户信息和 token)------服务器

坑 2、jssdk 配置 wx.config 和 wx.chooseWXPay
reactjs 的微信 jssdk weixin-js-sdk

config 配置:react 获取当前路径我是使用 window 然后 split 域名,去掉默认的#号是使用 browserHistory 而不是用 hash 路由 我的每次 wx.chooseWXPay 请求之前都是调用了 wx.config config
关于调试:需线上配置好的环境在微信开发者工具中调试
关于签名失败:仔细检查参数

[!wx.config 基于微信后台域名的配置]

前端代码:

_getInitialState() { let url = window.location.href; let urlArr = url.split("xxxx"); let that = this; let pathname = urlArr[1]; window.$http.post('/api/jssdk', qs.stringify({path: pathname})).then(res => { if (res.status === 10000) { that.setState({ openid: res.openid }); wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: res.appid, // 必填,公众号的唯一标识 timestamp: res.timestamp, // 必填,生成签名的时间戳 nonceStr: res.noncestr, // 必填,生成签名的随机串 signature: res.signature,// 必填,签名 jsApiList: ["chooseWXPay", "onMenuShareTimeline", "addCard"], // 必填,需要使用的JS接口列表 success: function (res) { } }); } }).catch(err => { }); }

后端 jssdk 接口

//@router /api/jssdk [*] func (this *WebController) JSSDK() { path := this.GetString("path") noncestr := string(utils.Krand(16, 3)) timestamp := time.Now().Unix() var user models.User //由于已经微信授权将openid写入数据库了 if err := models.DB.Where("id = ?", this.UserID).First(&user); err != nil { beego.Warning(err) } //"gopkg.in/chanxuehong/wechat.v2/mp/jssdk" signature := jssdk.WXConfigSign(JSTICKET, noncestr, strconv.FormatInt(timestamp, 10), 你的域名+path) data := make(map[string]interface{}) data["appid"] = Appid data["noncestr"] = noncestr data["timestamp"] = timestamp data["signature"] = signature data["status"] = 10000 data["openid"] = user.Openid this.Data["json"] = data this.ServeJSON() return }

然后预下单
将 openid 商品信息提交到服务端,服务端通过预下单接口下单(此处其实是模拟商户向微信请求此时商户的的参数包括)

//向商户下单[github.com/objcoding/wxpay] func PostPreOrder(openid string, tradeNo string, amount int64, ip string, category int) (prepayId string, paySign string, isOk bool, err error) { account := wxpay.NewAccount(Appid, Mchid, ApiKey, false) client := wxpay.NewClient(account) bodyString := "" if category == 1 { bodyString = "callpay" } else { bodyString = "fuelpay" } // 设置http请求超时时间 client.SetHttpConnectTimeoutMs(2000) // 设置http读取信息流超时时间 client.SetHttpReadTimeoutMs(1000) client.SetSignType("MD5") params := make(wxpay.Params) fmt.Println("body:", bodyString) fmt.Println("appid:", Appid) fmt.Println("out_trade_no:", tradeNo) params.SetString("body", bodyString). SetString("appid", Appid). SetString("out_trade_no", tradeNo). SetInt64("total_fee", amount). SetString("spbill_create_ip", ip). SetString("notify_url", notifyUrl). SetString("trade_type", tradeType). SetString("openid", openid) /* beego.Error("-----------request param------------") for key, value := range params { beego.Error("key:", key, " value:", value) } */ returnParams, err := client.UnifiedOrder(params) if err != nil { log.Println(err) return "", "", false, err } /* beego.Error("-----------response param------------") for key, value := range returnParams { beego.Error("key:", key, " value:", value) } */ returnCode, ok := returnParams["return_code"] resultCode, ok := returnParams["result_code"] if returnCode == "SUCCESS" && resultCode == "SUCCESS" && ok { paySign, _ = returnParams["sign"] prepayId, _ := returnParams["prepay_id"] return prepayId, paySign, true, err } return "", "", false, err }

上面返回了预下单的 preid 和 paysign(这是错误的签名) 后面我拿出来重新改写了

func (this *OrderController) test(){ noncestr := utils.Str2Md5(time.Now().Format("20060102150405")) timestamp := time.Now().Unix() //来自上面PostPreOrder()返回的 paySign = Sign(prepayId, noncestr, strconv.FormatInt(timestamp, 10)) /* rst["package"] = "prepay_id=" + prepayId rst["paySign"] = paySign rst["nonceStr"] = noncestr rst["timeStamp"] = strconv.FormatInt(timestamp, 10) rst["appId"] = Appid rst["status"] = 10000 rst["tradeNo"] = tradeNo this.Data["json"] = rst this.ServeJSON() } /支付的签名函数 (包内的签名函数的改写) func Sign(prepayId string, noncestr string, timestamp string) string { params := make(wxpay.Params) params.SetString("package", "prepay_id="+prepayId). SetString("nonceStr", noncestr). SetString("timeStamp", timestamp). SetString("appId", Appid). SetString("signType", "MD5") var keys = make([]string, 0, len(params)) for k := range params { if k != "sign" { // 排除sign字段 keys = append(keys, k) } } sort.Strings(keys) //创建字符缓冲 var buf bytes.Buffer for _, k := range keys { if len(params.GetString(k)) > 0 { buf.WriteString(k) buf.WriteString(`=`) buf.WriteString(params.GetString(k)) buf.WriteString(`&`) } } // 加入apiKey作加密密钥 buf.WriteString(`key=`) buf.WriteString(ApiKey) var ( dataMd5 [16]byte str string ) dataMd5 = md5.Sum(buf.Bytes()) str = hex.EncodeToString(dataMd5[:]) return strings.ToUpper(str) }

返回给前端 6 个重要参数 其中 5 个是调用 wx.chooseWXPay 的(这是后端给前端的,也有一些写法是前端把参数准备好然后再请求)
此处注意参数 key 和 value 的准确性(比如大小写,值的类型等)

在以上基础上再调用 wx.chooseWXPay

_preOrder = () => { let that = this; let itemId = that.state.itemId; let mobile = that.state.mobile; let openid = that.state.openid; let category = that.state.category; mobile = mobile.replace(/\s/ig, ''); //预下单 window.$http.post('/api/order/pre', qs.stringify({ mobile: mobile, id: itemId, category: category, openid: openid })).then(res => { //回调获得支付参数(也有是在前端将参数准备好的) let appId = res.appId; let timeStamp = res.timeStamp; let nonceStr = res.nonceStr; let packages = res.package; let paySign = res.paySign; that.setState({ tradeNo: res.tradeNo }); //发起支付[此处注意这个包帮你封装了一层 timestamp 实际微信文档为timeStamp] wx.chooseWXPay({ appId: appId, timestamp: timeStamp.toString(), nonceStr: nonceStr, package: packages, signType: 'MD5', paySign: paySign, success: function (res) { //完成后修改订单状态 that._paySuccess(); }, }).catch(err => { console.log("error") }); if (res.status === 10001) { console.log("下单失败") } }).catch(err => { }); };

总结:整个逻辑很简单但是一个人前后端都写的话,对于微信这一块不熟的话写起来就会犯迷糊,忘记了参数,类型等等等。相比来说支付宝的就简单很多,加油

  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    500 引用 • 1396 回帖 • 243 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    246 引用 • 1338 回帖 • 2 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
xhaoxiong
站在巨人的肩膀上学习与创新

推荐标签 标签

  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 406 关注
  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    198 引用 • 543 回帖 • 1 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 3 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    15 引用 • 7 回帖 • 1 关注
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 506 关注
  • PWL

    组织简介

    用爱发电 (Programming With Love) 是一个以开源精神为核心的民间开源爱好者技术组织,“用爱发电”象征开源与贡献精神,加入组织,代表你将遵守组织的“个人开源爱好者”的各项条款。申请加入:用爱发电组织邀请帖
    用爱发电组织官网:https://programmingwithlove.stackoverflow.wiki/

    用爱发电组织的核心驱动力:

    • 遵守开源守则,体现开源&贡献精神:以分享为目的,拒绝非法牟利。
    • 自我保护:使用适当的 License 保护自己的原创作品。
    • 尊重他人:不以各种理由、各种漏洞进行未经允许的抄袭、散播、洩露;以礼相待,尊重所有对社区做出贡献的开发者;通过他人的分享习得知识,要留下足迹,表示感谢。
    • 热爱编程、热爱学习:加入组织,热爱编程是首当其要的。我们欢迎热爱讨论、分享、提问的朋友,也同样欢迎默默成就的朋友。
    • 倾听:正确并恳切对待、处理问题与建议,及时修复开源项目的 Bug ,及时与反馈者沟通。不抬杠、不无视、不辱骂。
    • 平视:不诋毁、轻视、嘲讽其他开发者,主动提出建议、施以帮助,以和谐为本。只要他人肯努力,你也可能会被昔日小看的人所超越,所以请保持谦虚。
    • 乐观且活跃:你的努力决定了你的高度。不要放弃,多年后回头俯瞰,才会发现自己已经成就往日所仰望的水平。积极地将项目开源,帮助他人学习、改进,自己也会获得相应的提升、成就与成就感。
    1 引用 • 487 回帖 • 2 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 1 关注
  • 叶归
    12 引用 • 56 回帖 • 20 关注
  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    15 引用 • 136 回帖
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1742 回帖
  • WebComponents

    Web Components 是 W3C 定义的标准,它给了前端开发者扩展浏览器标签的能力,可以方便地定制可复用组件,更好的进行模块化开发,解放了前端开发者的生产力。

    1 引用 • 11 关注
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    413 引用 • 3590 回帖
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 798 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 79 关注
  • GitHub

    GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。

    209 引用 • 2040 回帖
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖 • 5 关注
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 554 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 677 关注
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    98 引用 • 367 回帖
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖 • 2 关注
  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    36 引用 • 200 回帖 • 33 关注
  • 反馈

    Communication channel for makers and users.

    120 引用 • 906 回帖 • 279 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 74 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 99 关注
  • RemNote
    2 引用 • 16 回帖 • 25 关注