前言

可能看过我博客的朋友知道我主要是做的支付这一块的测试工作。而我们都知道现在比较流行的支付方式就是微信支付和支付宝支付,当然最近在使用低手续费大力推广的京东金融(已改名为京东数科)以后也可能站到第一队列,但是要在中国市场走到和财付通、蚂蚁金服一个层级就任重而道远了。

废话不多说,我们一起来看看微信支付签名的官方文档。搜索微信支付--点击支付开发文档--接口规则--安全规范。

我们会看的以下的内容:

签名生成的方法文档已经说的很清晰,下面我们一起来看看怎么使用golang来实现它,以及怎么使用一些更高级的特性来优化。

初始方式

最开始的方式比较直接,能实现这个需求就行:

func GetSign(sourceMap map[string]string, bizKey string) string {
orderedString := orderParam(sourceMap, bizKey)
md5Ctx := md5.New()
md5Ctx.Write([]byte(orderedString))
signString := md5Ctx.Sum(nil)
//fmt.Print(hex.EncodeToString(cipherStr))
return hex.EncodeToString(signString)
} func orderParam(source map[string]string, bizKey string) string {
var tempArr []string
i := 0
for k, v := range source {
tempArr = append(tempArr, k+"="+v)
i++
}
sort.Strings(tempArr)
temString := ""
for n, v := range tempArr {
if n+1 < len(tempArr) {
temString = temString + v + "&"
} else {
temString = temString + v + bizKey
}
}
fmt.Println(temString)
return temString
}

代码说明

  • orderParam主要用来把传递的参数转化为键值对的格式(即key1=value1&key2=value2…)并在最后拼接上key
  • GetSign 获取orderParam拼接之后字符串进行md5加密

后来发现这样的方式有很多的弊端,比如无法处理可能某个参数是数字的情况,无法处理某个参数的value值是map或数组的情况,所以就进行了兼容性和性能上的优化。

优化

这一次的优化主要就是添加了格式的兼容,将传入的参数变成了可以存储任何类型数据的interface{},另外就是优化了拼接字符串的操作。优化后的代码如下

func betterOne(srcmap map[string]interface{}, bizkey string) string {
md5ctx := md5.New()
keys := make([]string, 0, len(srcmap)) for k := range srcmap {
if k == "sign" {
continue
}
keys = append(keys, k)
}
sort.Strings(keys)
var buf bytes.Buffer
for _, k := range keys {
vs := srcmap[k]
if vs == "" {
continue
}
if buf.Len() > 0 {
buf.WriteByte('&')
} buf.WriteString(k)
buf.WriteByte('=')
switch vv := vs.(type) {
case string:
buf.WriteString(vv)
case int:
buf.WriteString(strconv.FormatInt(int64(vv), 10))
default:
panic("params type not supported")
}
}
buf.WriteString(bizkey)
md5ctx.Write([]byte(buf.String()))
return hex.EncodeToString(md5ctx.Sum(nil))
}

buf.WriteString使用buffer来替代循环的字符串操作,来自于基础库http库中的url.encode方法,在此前【Golang 接口自动化01】使用标准库net/http发送Get请求提到过这个方法。理论上来说性能会有一个比较大的提升,实际测试结果如下:

其中ns/opB/opallocs/op分别代表每个操作的耗时、分配内存、分配对象次数。可以看的三者都有较大的提升。

当时在知道这个差距之后,我放下了手上的已经构建得七七八八的自动化代码,专心的研究了一段时间开源项目的源码,所以有了下面这个兼容性更好和性能更平衡的版本。

最终方法

这个版本主要考虑的是不同数据兼容性,兼容了直接传递struct时进行签名的计算(后面会学习到直接把struct当作json发送的方法),并把map[string]stringmap[string]interface{}的数据都进行了对应处理。

性能对比

下面是三者对比的性能测试结果

可以看到最终版比优化版损耗的时间还要略短。

参考代码

// Getsign get the sign info
func Getsign(srcdata interface{}, bizkey string) string {
md5ctx := md5.New() switch v := reflect.ValueOf(srcdata); v.Kind() {
case reflect.String:
md5ctx.Write([]byte(v.String() + bizkey))
return hex.EncodeToString(md5ctx.Sum(nil))
case reflect.Map:
orderStr := orderParam(v.Interface(), bizkey)
md5ctx.Write([]byte(orderStr))
return hex.EncodeToString(md5ctx.Sum(nil))
case reflect.Struct:
orderStr := Struct2map(v.Interface(), bizkey)
md5ctx.Write([]byte(orderStr))
return hex.EncodeToString(md5ctx.Sum(nil))
default:
return ""
}
} func orderParam(source interface{}, bizKey string) (returnStr string) {
switch v := source.(type) {
case map[string]string:
keys := make([]string, 0, len(v)) for k := range v {
if k == "sign" {
continue
}
keys = append(keys, k)
}
sort.Strings(keys)
var buf bytes.Buffer
for _, k := range keys {
if v[k] == "" {
continue
}
if buf.Len() > 0 {
buf.WriteByte('&')
} buf.WriteString(k)
buf.WriteByte('=')
buf.WriteString(v[k])
}
buf.WriteString(bizKey)
returnStr = buf.String()
case map[string]interface{}:
keys := make([]string, 0, len(v)) for k := range v {
if k == "sign" {
continue
}
keys = append(keys, k)
}
sort.Strings(keys)
var buf bytes.Buffer
for _, k := range keys {
if v[k] == "" {
continue
}
if buf.Len() > 0 {
buf.WriteByte('&')
} buf.WriteString(k)
buf.WriteByte('=')
// buf.WriteString(srcmap[k])
switch vv := v[k].(type) {
case string:
buf.WriteString(vv)
case int:
buf.WriteString(strconv.FormatInt(int64(vv), 10))
default:
panic("params type not supported")
}
}
buf.WriteString(bizKey)
returnStr = buf.String()
}
// fmt.Println(returnStr)
return
} func Struct2map(content interface{}, bizKey string) string {
var tempArr []string
temString := ""
var val map[string]string
if marshalContent, err := json.Marshal(content); err != nil {
fmt.Println(err)
} else {
d := json.NewDecoder(bytes.NewBuffer(marshalContent))
d.UseNumber()
if err := d.Decode(&val); err != nil {
fmt.Println(err)
} else {
for k, v := range val {
val[k] = v
}
}
}
i := 0
for k, v := range val {
// 去除冗余未赋值struct
if v == "" {
continue
}
i++
tempArr = append(tempArr, k+"="+v)
}
sort.Strings(tempArr)
for n, v := range tempArr {
if n+1 < len(tempArr) {
temString = temString + v + "&"
} else {
temString = temString + v + bizKey
}
}
return temString
}

总结

  • 微信签名
  • buffer
  • 简单性能测试

【Golang 接口自动化06】微信支付md5签名计算及其优化的更多相关文章

  1. 微信支付MD5签名算法C#版,ASCII码字典序排序0,A,B,a,b

    /// <summary> /// 微信支付MD5签名算法,ASCII码字典序排序0,A,B,a,b /// </summary> /// <param name=&qu ...

  2. 【Golang 接口自动化00】为什么要用Golang做自动化?

    为什么使用Golang做自动化 顺应公司的趋势学习了Golang之后,因为没有开发那么多的时间和项目来实践,怕步此前学习Java缺少练习遗忘殆尽的后尘,决定利用工作之余的时间把此前用Python的写的 ...

  3. (实用篇)php官方微信接口大全(微信支付、微信红包、微信摇一摇、微信小店)

    微信入口绑定,微信事件处理,微信API全部操作包含在这些文件中.内容有:微信摇一摇接口/微信多客服接口/微信支付接口/微信红包接口/微信卡券接口/微信小店接口/JSAPI <?php class ...

  4. 微信支付没有结果通知,notify_url参数的接口没有收到微信支付结果通知

    在微信支付统一下单的时候需要填一个notify_url参数用于处理微信支付结果通知 但是,有时候我们发现我们设置的这个接口收不到微信请求.原因有一下几个,大家一一对照,也欢迎补充. 1. url是否可 ...

  5. 【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试

    前言 Mock是一个做自动化测试永远绕不过去的话题.本文主要介绍使用标准库net/http/httptest完成HTTP请求的Mock的测试方法. 可能有的小伙伴不太了解mock在实际自动化测试过程中 ...

  6. 【Golang 接口自动化03】 解析接口返回XML

    上一篇我们学习了怎么发送各种数据类型的http请求,这一篇我们来介绍怎么来解析接口返回的XML的数据. 解析接口返回数据 定义结构体 假设我们现在有一个接口返回的数据resp如下: <?xml ...

  7. 【Golang 接口自动化02】使用标准库net/http发送Post请求

    写在前面 上一篇我们介绍了使用 net/http 发送get请求,因为考虑到篇幅问题,把Post单独拎了出来,我们在这一篇一起从源码来了解一下Golang的Post请求. 发送Post请求 net/h ...

  8. 【Golang 接口自动化05】使用yml管理自动化用例

    我们在前面几篇文章中学习怎么发送数据请求,怎么处理解析接口返回的结果,接下来我们一起来学习怎么进行测试用例管理,今天我们介绍的是使用yml文件进行用例管理,所以首先我们一起来了解一下YAML和它的简单 ...

  9. 【Golang 接口自动化04】 解析接口返回JSON串

    前言 上一次我们一起学习了如何解析接口返回的XML数据,这一次我们一起来学习JSON的解析方法. JSON(Javascript Object Notation)是一种轻量级的数据交换语言,以文字为基 ...

随机推荐

  1. !!【通达信】求教:如何对A股的所有股票按照某个选股指标的某个参数排序? - 理想论坛 中国人气最旺的股票论坛

    http://www.55188.com/thread-7152852-1-1.html .401进入指标排序,然后占右键把指标更改为MACD即可.(注意401前投资面有一个点!)

  2. 定时器事件QtimerEvent 随机数 qrand Qtimer定时器

    QTimerEvent类:定时器事件.QObject的子类都可使用  int QObject::startTimer(int interval)[参数:毫秒][返回值:定时器整型编号]来开启一个定时器 ...

  3. ARM中的汇编指令

    Arm指令,32位的指令集,一共有16条的基本指令,每条指令都可以按条件执行, 指令都是32bit的,高四位是条件码[31:28], Thumb指令,16位的指令集,执行效率比arm指令集要低,但是节 ...

  4. MFC六大核心机制之五、六:消息映射和命令传递

    作为C++程序员,我们总是希望自己程序的所有代码都是自己写出来的,如果使用了其他的一些库,也总是千方百计想弄清楚其中的类和函数的原理,否则就会感觉不踏实.所以,我们对于在进行MFC视窗程序设计时经常要 ...

  5. Python 让PIP源使用国内镜像,提升下载速度和安装成功率

    对于Python开发用户来讲,PIP安装软件包是家常便饭.但国外的源下载速度实在太慢,浪费时间.而且经常出现下载后安装出错问题.所以把PIP安装源替换成国内镜像,可以大幅提升下载速度,还可以提高安装成 ...

  6. Linux系统下C语言程序的构建过程

    本文转载自:http://www.ruanyifeng.com/blog/2014/11/compiler.html 源码要运行,必须先转成二进制的机器码.这是编译器的任务. 比如,下面这段源码(假定 ...

  7. tomcat和jetty区别

    参见:https://www.cnblogs.com/fengli9998/p/7247559.html 1. Jetty更轻量级.这是相对Tomcat而言的. 由于Tomcat除了遵循Java Se ...

  8. Linux服务器---apache支持SSL

    Apache支持ssl 1.检测是否安装ssl模块,如果没有就安装 [root@localhost cgi-bin]# rpm -qa | grep mod_ssl           //查看是否安 ...

  9. [转载]onclientclick和onclick区别

    OnClientClick是客户端脚本,一般使用javascript,在客户端,也就是IE中运行,点击后马上执行OnClick是服务器端事件处理函数,使用C#或者vb.net,在服务器端,也就是IIS ...

  10. SNMP学习笔记之Centos7配置SNMP服务

    0x00 安装yum源安装SNMP软件包 1.yum源安装SNMP服务: yum -y install net-snmp net-snmp-utils 2.查看SNMP版本号: snmpd -v 0x ...