前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言银联8583报文解析库,支持联小额免密付和银商聚合支付

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

作者头像
杨永贞
发布2021-03-05 11:01:34
1.5K0
发布2021-03-05 11:01:34
举报

很早之前就整过一个Go语言版的银联8583报文解析库,不过是用来测试的。

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

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

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

附代码实现和使用demo:

代码语言:javascript
复制
/**
银联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:云闪付二维码交易:

代码语言:javascript
复制
/**
使用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("交易成功")
    }

}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-03-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档