Go语言银联8583报文解析库,支持联小额免密付和银商聚合支付

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: Go语言银联8583报文解析库,支持联小额免密付和银商聚合支付

很早之前就整过一个Go语言版的银联8583报文解析库,当时仅是8583报文的解析。


最近整合了进了银联小额双免交易和银商的聚合支付交易通道,这可以是网上最简单的8583报文解析库了。


银联双免支付通道支持银行卡免密和云闪付二维码交易,而银商的聚合支付交易通道支持支付宝付款码,微信付款码和云闪付二维码。


如果配置了正确的秘钥参数,可直接用来交易。


https://github.com/yangyongzhen/go8583.git


附部分代码实现和使用demo:


/**
银联8583报文,
包含签到,云闪付二维码交易,银联卡小额免密交易
Author:yangyongzhen
QQ:534117529
*/
package up8583
import (
  "errors"
  "fmt"
  "go8583/easy8583"
  "go8583/utils"
  "strconv"
  "strings"
)
type Up8583 struct {
  Ea         *easy8583.Easy8583
  ManNum     string //商户号
  PosNum     string //终端号
  MainKey    string //主密钥
  TPDU       string
  CommSn     int    //通信流水
  RecSn      int    //售卡方系统跟踪号
  PiciNum    []byte //批次号
  LicenceNum []byte
  MacKey string //MAC key
  UpBinNum  string //银行卡卡号
  CardSnNum string //持卡序号
  CardDate  string //卡有效期
  Fd35Data  string //二磁道数据
}
func memcpy(dst, src []byte, size int) {
  for i := 0; i < size; i++ {
    dst[i] = src[i]
  }
  return
}
func equals(src1 []byte, src2 []byte) bool {
  if src1 == nil || src2 == nil {
    return false
  }
  le1 := len(src1)
  le2 := len(src2)
  if le1 != le2 {
    return false
  }
  for i := 0; i < le1; i++ {
    if src1[i] != src2[i] {
      return false
    }
  }
  return true
}
/*
银联8583签到组包
*/
func (up *Up8583) Frame8583QD() {
  s := up.Ea
  field := up.Ea.Field_S
  s.Init8583Fields(field)
  //消息类型
  s.Msgtype[0] = 0x08
  s.Msgtype[1] = 0x00
  //11域,受卡方系统跟踪号BCD 通讯流水
  field[10].Ihave = true
  field[10].Len = 3
  sn := fmt.Sprintf("%06d", up.CommSn)
  field[10].Data = utils.HexStringToBytes(sn)
  //41域,终端号
  field[40].Ihave = true
  field[40].Len = 8
  field[40].Data = []byte(up.PosNum)
  //42域,商户号
  field[41].Ihave = true
  field[41].Len = 15
  field[41].Data = []byte(up.ManNum)
  //60域
  field[59].Ihave = true
  field[59].Len = 0x11
  field[59].Data = make([]byte, 6)
  field[59].Data[0] = 0x00
  memcpy(field[59].Data[1:], up.PiciNum, 3)
  field[59].Data[4] = 0x00
  field[59].Data[5] = 0x30
  //62域
  field[61].Ihave = true
  field[61].Len = 0x25
  field[61].Data = make([]byte, 25)
  str := "Sequence No12"
  memcpy(field[61].Data, []byte(str), 13)
  memcpy(field[61].Data[13:], up.LicenceNum, 4)
  memcpy(field[61].Data[17:], []byte(up.PosNum), 8)
  //63域
  field[62].Ihave = true
  field[62].Len = 0x03
  field[62].Data = make([]byte, 3)
  field[62].Data[0] = 0x30
  field[62].Data[1] = 0x30
  field[62].Data[2] = 0x31
  /*报文组帧,自动组织这些域到Pack的TxBuffer中*/
  s.Pack8583Fields()
  up.CommSn++ //通讯流水每次加一
  //s.PrintFields(up.Ea.Field_S)
}
func (up *Up8583) Ans8583QD(rxbuf []byte, rxlen int) error {
  r := up.Ea
  fields := up.Ea.Field_S
  fieldr := up.Ea.Field_R
  ret := r.Ans8583Fields(rxbuf, rxlen)
  if ret == 0 {
    fmt.Println("解析成功")
    r.PrintFields(fieldr)
  } else {
    fmt.Println("解析失败")
    return errors.New("error,failed to ans..")
  }
  //消息类型判断
  if (r.Msgtype[0] != 0x08) || (r.Msgtype[1] != 0x10) {
    //Log.d(TAG,"消息类型错!");
    return errors.New("error,wrong Msgtype ")
  }
  //应答码判断
  if (fieldr[38].Data[0] != 0x30) || (fieldr[38].Data[1] != 0x30) {
    //Log.d(TAG,"应答码不正确!");
    return errors.New("error,wrong resp code:" + fmt.Sprintf("%02x%02x", fieldr[38].Data[0], fieldr[38].Data[1]))
  }
  //跟踪号比较
  //memcmp
  if !equals(fields[10].Data, fieldr[10].Data) {
    return errors.New("error,wrong comm no ")
  }
  //终端号比较
  if !equals(fields[40].Data, fieldr[40].Data) {
    return errors.New("error,posnum not equal ")
  }
  //商户号比较
  if !equals(fields[41].Data, fieldr[41].Data) {
    return errors.New("error,mannum not equal ")
  }
  //3DES解密PIN KEY
  data := make([]byte, 16)
  memcpy(data, fieldr[61].Data, 16)
  pinkey, err := utils.Des3Decrypt(data, utils.HexStringToBytes(up.MainKey))
  if err != nil {
    return errors.New("1" + err.Error())
  }
  //解密后的结果对8Byte全0做3DES加密运算
  tmp := make([]byte, 8)
  out, err := utils.Des3Encrypt(tmp, pinkey)
  if err != nil {
    return errors.New("2" + err.Error())
  }
  check := make([]byte, 4)
  pincheck := make([]byte, 4)
  memcpy(check, out, 4)
  memcpy(pincheck, fieldr[61].Data[16:], 4)
  if !equals(check, pincheck) {
    return errors.New("error,Er PIK")
  }
  //3DES解密MAC KEY
  memcpy(data, fieldr[61].Data[20:], 16)
  mackey, err := utils.Des3Decrypt(data, utils.HexStringToBytes(up.MainKey))
  if err != nil {
    return errors.New("3" + err.Error())
  }
  out, err = utils.DesEncrypt(tmp, mackey[0:8])
  if err != nil {
    return errors.New("4" + err.Error())
  }
  maccheck := make([]byte, 4)
  memcpy(check, out, 4)
  memcpy(maccheck, fieldr[61].Data[36:], 4)
  if !equals(check, maccheck) {
    return errors.New("error,Er MAC")
  }
  memcpy(up.PiciNum, fieldr[59].Data[1:], 3)
  up.MacKey = utils.BytesToHexString(mackey[0:8])
  fmt.Printf("mackey:%s\n", up.MacKey)
  up.Ea.SetMacKey(up.MacKey)
  return nil
}
func (up *Up8583) Ans8583Qrcode(rxbuf []byte, rxlen int) error {
  r := up.Ea
  fields := up.Ea.Field_S
  fieldr := up.Ea.Field_R
  ret := r.Ans8583Fields(rxbuf, rxlen)
  if ret == 0 {
    fmt.Println("解析成功")
    r.PrintFields(fieldr)
  } else {
    fmt.Println("解析失败")
    return errors.New("error,failed to ans..")
  }
  //消息类型判断
  if (r.Msgtype[0] != 0x02) || (r.Msgtype[1] != 0x10) {
    //Log.d(TAG,"消息类型错!");
    return errors.New("error,wrong Msgtype ")
  }
  //应答码判断
  if (fieldr[38].Data[0] != 0x30) || (fieldr[38].Data[1] != 0x30) {
    //Log.d(TAG,"应答码不正确!");
    return errors.New("error,wrong resp code:" + fmt.Sprintf("%02x%02x", fieldr[38].Data[0], fieldr[38].Data[1]))
  }
  //跟踪号比较
  //memcmp
  if !equals(fields[10].Data, fieldr[10].Data) {
    return errors.New("error,wrong comm no ")
  }
  //终端号比较
  if !equals(fields[40].Data, fieldr[40].Data) {
    return errors.New("error,posnum not equal ")
  }
  //商户号比较
  if !equals(fields[41].Data, fieldr[41].Data) {
    return errors.New("error,mannum not equal ")
  }
  return nil
}
/*
银联8583 二维码交易组包
qrcode:二维码内容
money:交易金额
recSn:交易流水
*/
func (up *Up8583) Frame8583Qrcode(qrcode string, money int, recSn int) {
  s := up.Ea
  field := up.Ea.Field_S
  s.Init8583Fields(field)
  //消息类型
  s.Msgtype[0] = 0x02
  s.Msgtype[1] = 0x00
  //3域 交易处理码
  field[2].Ihave = true
  field[2].Len = 3
  field[2].Data = make([]byte, 3)
  //4域 交易金额
  field[3].Ihave = true
  field[3].Len = 6
  field[3].Data = utils.HexStringToBytes(fmt.Sprintf("%012d", money))
  //11域,受卡方系统跟踪号BCD 通讯流水
  field[10].Ihave = true
  field[10].Len = 3
  sn := fmt.Sprintf("%06d", recSn)
  field[10].Data = utils.HexStringToBytes(sn)
  //22域
  field[21].Ihave = true
  field[21].Len = 2
  field[21].Data = []byte{0x03, 0x20}
  //25域
  field[24].Ihave = true
  field[24].Len = 1
  field[24].Data = make([]byte, 1)
  //41域,终端号
  field[40].Ihave = true
  field[40].Len = 8
  field[40].Data = []byte(up.PosNum)
  //42域,商户号
  field[41].Ihave = true
  field[41].Len = 15
  field[41].Data = []byte(up.ManNum)
  //49域 交易货币代码
  field[48].Ihave = true
  field[48].Len = 3
  field[48].Data = []byte{0x31, 0x35, 0x36}
  //59域,扫码的数据
  field[58].Ihave = true
  field[58].Len = 0x24
  field[58].Data = make([]byte, 24)
  field[58].Data[0] = 'A' //TAG+Len(019)
  field[58].Data[1] = '3'
  field[58].Data[2] = '0'
  field[58].Data[3] = '1'
  field[58].Data[4] = '9'
  memcpy(field[58].Data[5:], []byte(qrcode), 19)
  //60域
  field[59].Ihave = true
  field[59].Len = 0x13
  field[59].Data = make([]byte, 7)
  field[59].Data[0] = 0x22
  memcpy(field[59].Data[1:], up.PiciNum, 3)
  field[59].Data[4] = 0x00
  field[59].Data[5] = 0x06
  field[59].Data[6] = 0x00
  //MAC,64域
  field[63].Ihave = true
  field[63].Len = 0x08
  field[63].Data = make([]byte, 8)
  //这个域要求填MAC,只需按这样填,MAC的计算在pack8583Fields自动完成了
  /*报文组帧,自动组织这些域到Pack的TxBuffer中*/
  s.Pack8583Fields()
  //CommSn++ //通讯流水每次加一
  //s.PrintFields(up.Ea.Field_S)
}
func NewUp8583() *Up8583 {
  var up = new(Up8583)
  up.Ea = easy8583.New8583()
  up.TPDU = "6000000001"
  up.ManNum = "000000000000000"
  up.PosNum = "00000000"
  up.MainKey = "00000000000000000000000000000000"
  up.CommSn = 1
  up.RecSn = 1 //终端交易流水,连续,且不能重复
  up.PiciNum = make([]byte, 3)
  up.LicenceNum = []byte{0x33, 0x30, 0x36, 0x30}
  up.MacKey = "0000000000000000"
  up.Ea.Tpdu = utils.HexStringToBytes(up.TPDU)
  return up
}
/**
Setup
初始化参数配置
manNum:商户号
posNum:终端号
mainKey:主密钥
*/
func (up *Up8583) Setup(manNum, posNum, mainKey, tpdu string) {
  up.TPDU = tpdu
  up.ManNum = manNum
  up.PosNum = posNum
  up.MainKey = mainKey
  up.Ea.Tpdu = utils.HexStringToBytes(up.TPDU)
}
/**
使用demo
*/
func main() {
  fmt.Println("test...")
  up := NewUp8583()
  up.Setup("888888888888888", "12345678", "11111111111111111111111111111111", "6005010000")
  //up.Frame8583QD()
  //recvstr := "007960000001386131003111080810003800010AC0001450021122130107200800085500323231333031343931333239303039393939393930363030313433303137303131393939390011000005190030004046F161A743497B32EAC760DF5EA57DF5900ECCE3977731A7EA402DDF0000000000000000CFF1592A"
  //recv := utils.HexStringToBytes(recvstr)
  //ret := up.Ea.Ans8583Fields(recv, len(recv))
  //if ret == 0 {
  //  fmt.Println("解析成功")
  //  up.Ea.PrintFields(up.Ea.Field_R)
  // } else {
  //  fmt.Println("解析失败")
  // }
  up.Frame8583QD()
  up.Ea.PrintFields(up.Ea.Field_S)
  //
  //fmt.Println(utils.BytesToHexSrxbuf, err := utils.UpHttpsPost(Url, up.Ea.Txbuf)
  // err = up.Ans8583QD(rxbuf, rxlen)
  // if err == nil {
  //  log.Println("签到成功")
  // }tring(up.Ea.Txbuf))
  up.Frame8583Qrcode("6220485073630469936", 1, 1)
  up.Ea.PrintFields(up.Ea.Field_S)
}


使用demo:云闪付二维码交易:


/**
使用demo,银联二维码交易
*/
func QrcodeProc(qrcode string, money int, recSn int) error {
  if Url == "" {
    return errors.New("error: Url must not null")
  }
  //up8583.RecSn++ //交易流水加加
  up.Frame8583Qrcode(qrcode, money, recSn)
  up.Ea.PrintFields(up.Ea.Field_S)
  log.Printf("connect:server=%s\n", Url)
  rxbuf, err := utils.UpHttpsPost(Url, up.Ea.Txbuf)
  rxlen := len(rxbuf)
  if err == nil {
    log.Printf("recv ok!len=%d\n", rxlen)
    err = up.Ans8583Qrcode(rxbuf, rxlen)
    if err == nil {
      log.Println("交易成功")
    } else {
      log.Println("交易失败")
    }
  }
  return err
}
func main() {
  fmt.Println("test...")
  up := NewUp8583()
    //配置进去商户号,终端号,主密钥参数
  up.Setup("888888888888888", "12345678", "11111111111111111111111111111111", "6005010000")
  //up.Frame8583QD()
  //recvstr := "007960000001386131003111080810003800010AC0001450021122130107200800085500323231333031343931333239303039393939393930363030313433303137303131393939390011000005190030004046F161A743497B32EAC760DF5EA57DF5900ECCE3977731A7EA402DDF0000000000000000CFF1592A"
  //recv := utils.HexStringToBytes(recvstr)
  //ret := up.Ea.Ans8583Fields(recv, len(recv))
  //if ret == 0 {
  //  fmt.Println("解析成功")
  //  up.Ea.PrintFields(up.Ea.Field_R)
  // } else {
  //  fmt.Println("解析失败")
  // }
  up.Frame8583QD()
  up.Ea.PrintFields(up.Ea.Field_S)
  //
  //fmt.Println(utils.BytesToHexSrxbuf, err := utils.UpHttpsPost(Url, up.Ea.Txbuf)
  // err = up.Ans8583QD(rxbuf, rxlen)
  // if err == nil {
  //  log.Println("签到成功")
  // }tring(up.Ea.Txbuf))
  //up.Frame8583Qrcode("6220485073630469936", 1, 1)
  //up.Ea.PrintFields(up.Ea.Field_S)
    err = QrcodeProc()
    if err == nil {
       log.Println("交易成功")
    }
}


相关文章
|
9天前
|
存储 Go 索引
go语言使用for循环遍历
go语言使用for循环遍历
24 7
|
12天前
|
存储 Go
go语言 遍历映射(map)
go语言 遍历映射(map)
25 2
|
13天前
|
Go 调度 开发者
Go语言中的并发编程:深入理解goroutines和channels####
本文旨在探讨Go语言中并发编程的核心概念——goroutines和channels。通过分析它们的工作原理、使用场景以及最佳实践,帮助开发者更好地理解和运用这两种强大的工具来构建高效、可扩展的应用程序。文章还将涵盖一些常见的陷阱和解决方案,以确保在实际应用中能够避免潜在的问题。 ####
|
13天前
|
测试技术 Go 索引
go语言使用 range 关键字遍历
go语言使用 range 关键字遍历
17 3
|
13天前
|
测试技术 Go 索引
go语言通过 for 循环遍历
go语言通过 for 循环遍历
23 3
|
25天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
38 2
|
JSON Prometheus Cloud Native
go解析Prometheus的数据
访问一个api, 返回如下数据: {"status":"success","data":{"resultType":"matrix","result":[{"metric":{},"values":[[1473820558.
2582 0
|
23天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
26 2
|
23天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
33 2
|
9天前
|
开发框架 Go 计算机视觉
纯Go语言开发人脸检测、瞳孔/眼睛定位与面部特征检测插件-助力GoFly快速开发框架
开发纯go插件的原因是因为目前 Go 生态系统中几乎所有现有的人脸检测解决方案都是纯粹绑定到一些 C/C++ 库,如 OpenCV 或 dlib,但通过 cgo 调用 C 程序会引入巨大的延迟,并在性能方面产生显著的权衡。此外,在许多情况下,在各种平台上安装 OpenCV 是很麻烦的。使用纯Go开发的插件不仅在开发时方便,在项目部署和项目维护也能省很多时间精力。

推荐镜像

更多