@@ -0,0 +1,28 @@ | |||
package boc | |||
const Version = "2.0" | |||
const SignType = "RSA2" | |||
//service类型 | |||
const CreateBocCashier = "CreateBocCashier" //【中銀收銀台】創建支付交易 | |||
const CreateAASAppTrade = "CreateAASAppTrade" //【中銀收银台】聚合APP支付統一下單 | |||
const CardBinding = "CardBinding " //【商戶收銀台】银行卡綁定 | |||
const CardBindingQueryList = "CardBindingQueryList " //【商戶收銀台】查询绑定银行卡列表 | |||
const RemoveBankCard = "removeBankCard " //【商戶收銀台】移除綁定銀行卡 | |||
const CardBindingNotify = "CardBindingNotify " //【商戶收銀台】银行卡绑定结果后台通知 | |||
const CreateEMVTrade = "CreateEMVTrade " //【商戶收銀台】創建EMV二维码支付交易 | |||
const CreateCardTrade = "CreateCardTrade " //【商戶收銀台】創建银行卡交易 | |||
const CreateQRTrade = "CreateQRTrade " //【商戶收銀台】創建聚合碼支付交易 | |||
const CreateWeChatTrade = "CreateWeChatTrade" //【商户收银台】微信公众号/小程序支付统一下单 | |||
const CreateBocPayAppTrade = "CreateBocPayAppTrade" //【商户收银台】手機銀行APP支付統一下單 | |||
const CreateWeChatAppTrade = "CreateWeChatAppTrade" //【商户收银台】微信APP支付統一下單 | |||
const CreateAlipayAppTrade = "CreateAlipayAppTrade" //【商户收银台】支付寶APP支付統一下單 | |||
const OrderRefund = "OrderRefund " //【基礎服務】退款 | |||
const OrderQuery = "OrderQuery " //【基礎服務】訂單查詢 | |||
const OfflineResult = "OfflineResult " //【基礎服務】支付結果後台通知 | |||
const PageResult = "PageResult " //【基礎服務】支付結果頁面通知 | |||
const Statement = "Statement " //【基礎服務】對賬文件下載 | |||
const Settlement = "Settlement " //【基礎服務】結算單下載 | |||
const OrderRelationQuery = "OrderRelationQuery " //【基礎服務】訂單关联查詢 |
@@ -1,12 +1,26 @@ | |||
package boc | |||
import ( | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||
"fmt" | |||
"github.com/syyongx/php2go" | |||
"time" | |||
) | |||
func Send(prd bool, appId, privateKeyStr string, param map[string]string) (string, error) { | |||
data := zhios_pay_utils.JoinStringsInASCII(param, "", "", false, false) | |||
const ( | |||
MerchantId = "888007050019400" | |||
TerminalNo = "98036140" | |||
PrivateKeyStr = "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCSJk259Gd9IuvG\nRxYOma5vjo0+x2CTyP7m3uJ/epf+mwp/4XySbjVharBFmKk0XoSnZz/X/cYOgQXH\nPWPnBd0Y4qTl3xVf+DdZHF035KNG0LJPqvzqOSCuD5w32BHN0o9wFaR75PbWfsUl\nn2XqLVRFdv3IaKJYVWb8fM7VLT0aA+Rk81GXGfAbmXzNqX3lbOkqlTcBdzMHdE94\nqpAxtppVJ8JQqRJsWwzqJHo2koN+/kb3vL9IxeABJGUgU4Fy53MuC7JcvdYUoE7V\n92eD6A9g6ctSHP5y7olx9T7uMAaHk/TDv3+QNk6rI54cif7V7C5ykhtH5ZfHQ27C\nx929l7LZAgMBAAECggEAVZfVQajfyyvehbswMdDTlkYYdsHxz9uRXcj/QcWcquJG\nXerDOEcPQERpg/kKb79DVX9kpqzbh7cMmUlc7vpCKMbj6DSkohYO5EN2XLus3FZx\nd7MeeFaIyygdR59lnfNJSk2CjYTlA24vK/+UgneVuTEdJHgTTESIEitOPw9Fj6v+\n/+vRwuT6PVDm1FDUxFcYmJYzdMwJZoH0wDymofiEdn6beaHkqDXakrdY307cHOlq\ncrVQJKSybLwcBsbk9pgGrh6lCQLtQ8pHNtf6rE5FkEesO/VmdRFDGAK/9cuXEJ7u\nICdSESdfvnKl9BeN3MzSaeDSnh2IPoz13VVxXuJF+QKBgQDfHBVA/e1XFrLDM5IW\nO6nIxZeeZSufITDKHi7qRoS2mlOwjpMoIVAHCxlpnWd89YQ7NPdScEi6Z7l2sEET\n0vKfWsJ3ltowj5QUXS0XuJeAhkmK66lBNrICkatoAGeWxDrraCgkqLPNe40ydQ6h\nT2c3nR8tf++U2TzCpCXxzj6pgwKBgQCnsdTIWKwhRIltTgEzXTh3+JMQ36MH3sVt\nP14bxRN1hlR59yQvx0IEToXOHASSRU0fWxj3H8UewobYmE+2Ro+qNHFY7Do7Ypum\nIM7In00Tx4CKOVOkMZtNtR9hPk2/JUKpPSQBl7dSysBDngAwGhBhpwutWDpEiIfL\n+P4pAFuvcwKBgDrPUT7ncDdjkU1o+5oGshNtGjEzY9M3UYAFgsJYQkVL2sGQY3PR\njCG/KDyxtAS5RtjAdmzxkgooqzeA8DaEhVXB+77AMdVZE2RmxysYij7jpuEKi3vY\nKgYJVdA9QxD3urwwoVxAZ9zDM43YUznsDz4WmZBJx+JJfP2lfScHAXd3AoGAMpRR\noKgWKuaYKM7JT+vDF3RzLzi03K/VjnD5epjDmMQOgMcJNBv+jnv8ocETo+ahL17z\ntb3wX6m+sF+oU0sPFABDW9XMbpL8bWwh0qMxxVB8NbS6xKBtvk9uCo+XNZsQcylM\nzeor0h3zRbHHTrrdzAZtYDhnQrqHcv9D8av4+38CgYEAgyn1h+EompwBF4hKo6Dx\nVL/ZZYcE1E+XMzmHvP8C1sOVBSPZuCAnCx43cZiJZ94/5v8V3+4nKuWbeN1ar9BK\nXXSAVkk21qM8cv2IoDwi/DdBLXD302KMReAOWfJnQ+sbU1v2B2VPJoCwd4lSqauj\nVE4PCFhhrsou1VM1XbP8XsM=\n-----END PRIVATE KEY-----" | |||
) | |||
func Send(prd bool, privateKeyStr string, param map[string]string) (string, error) { | |||
for key, value := range param { | |||
if value == "" { | |||
delete(param, key) | |||
} | |||
} | |||
data := zhios_pay_utils.JoinStringsInASCII(param, "", "", true, false) | |||
sign, err := zhios_pay_utils.GetSign(privateKeyStr, data) | |||
if err != nil { | |||
return "", err | |||
@@ -19,12 +33,68 @@ func Send(prd bool, appId, privateKeyStr string, param map[string]string) (strin | |||
"Content-Type": "application/json", | |||
} | |||
param["merchantSign"] = sign | |||
fmt.Println(zhios_pay_utils.SerializeStr(param)) | |||
fmt.Println(url) | |||
res, err := zhios_pay_utils.CurlPost(url, param, headers) | |||
fmt.Println("参数:", zhios_pay_utils.SerializeStr(param)) | |||
zhios_pay_utils.CurlDebug = true | |||
res, err := zhios_pay_utils.CurlPost(url, zhios_pay_utils.SerializeStr(param), headers) | |||
if err != nil { | |||
return "", err | |||
} | |||
fmt.Println(string(res)) | |||
return string(res), nil | |||
} | |||
func CreateBocPayAppTradeReq(prd bool, privateKeyStr, merchantId, terminalNo string, createBocPayAppTradeReq *md.CreateBocPayAppTradeReq) (string, error) { | |||
//基础信息,必填 | |||
createBocPayAppTradeReq.Version = Version | |||
createBocPayAppTradeReq.Service = CreateBocPayAppTrade | |||
createBocPayAppTradeReq.SignType = SignType | |||
createBocPayAppTradeReq.MerchantId = merchantId | |||
createBocPayAppTradeReq.TerminalNo = terminalNo | |||
createBocPayAppTradeReq.RequestId = createBocPayAppTradeReq.MercOrderNo + zhios_pay_utils.AnyToString(time.Now().Unix()) | |||
//按照接口类型选填信息 | |||
//createBocPayAppTradeReq.Amount = "100" | |||
//createBocPayAppTradeReq.OriginalAmount = "100" | |||
//createBocPayAppTradeReq.MercOrderNo = "2022101401" | |||
//createBocPayAppTradeReq.OrderDate = "20221013" | |||
//createBocPayAppTradeReq.OrderTime = "175600" | |||
//createBocPayAppTradeReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||
//createBocPayAppTradeReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||
// "mid":"123456", | |||
//}) | |||
param := make(map[string]string) | |||
zhios_pay_utils.Unserialize(zhios_pay_utils.Serialize(createBocPayAppTradeReq), ¶m) | |||
send, err := Send(prd, privateKeyStr, param) | |||
if err != nil { | |||
return "", err | |||
} | |||
resp, _ := php2go.URLDecode(send) | |||
return resp, nil | |||
} | |||
func CreateWeChatTradeReq(prd bool, privateKeyStr, merchantId, terminalNo string, createWeChatTradeReq *md.CreateWeChatTradeReq) (string, error) { | |||
//基础信息,必填 | |||
createWeChatTradeReq.Version = Version | |||
createWeChatTradeReq.Service = CreateWeChatTrade | |||
createWeChatTradeReq.SignType = SignType | |||
createWeChatTradeReq.MerchantId = merchantId | |||
createWeChatTradeReq.TerminalNo = terminalNo | |||
createWeChatTradeReq.RequestId = createWeChatTradeReq.MercOrderNo + zhios_pay_utils.AnyToString(time.Now().Unix()) | |||
//按照接口类型选填信息 | |||
//createBocPayAppTradeReq.Amount = "100" | |||
//createBocPayAppTradeReq.OriginalAmount = "100" | |||
//createBocPayAppTradeReq.MercOrderNo = "2022101401" | |||
//createBocPayAppTradeReq.OrderDate = "20221013" | |||
//createBocPayAppTradeReq.OrderTime = "175600" | |||
//createBocPayAppTradeReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||
//createBocPayAppTradeReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||
// "mid":"123456", | |||
//}) | |||
param := make(map[string]string) | |||
zhios_pay_utils.Unserialize(zhios_pay_utils.Serialize(createWeChatTradeReq), ¶m) | |||
send, err := Send(prd, privateKeyStr, param) | |||
if err != nil { | |||
return "", err | |||
} | |||
resp, _ := php2go.URLDecode(send) | |||
return resp, nil | |||
} |
@@ -44,3 +44,24 @@ type CreateBocPayAppTradeReq struct { | |||
Reserved2 string `json:"reserved2" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | |||
Reserved3 string `json:"reserved3" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | |||
} | |||
type CreateWeChatTradeReq struct { | |||
MacaoBOCPublicParameter | |||
Amount string `json:"amount" name:"订单金額" label:"以分爲單位,如1元表示爲100"` | |||
OriginalAmount string `json:"originalAmount" name:"原订单金额" label:"以分爲單位,如1元表示爲100若无优惠请跟订单金额【amount】一致"` | |||
MerchantPreferentialCnName string `json:"merchantPreferentialCnName" name:"商户系统优惠活动中文名称" label:"非必传"` | |||
MerchantPreferentialEnName string `json:"merchantPreferentialEnName" name:"商户系统优惠活动英文名称" label:"非必传"` | |||
Subject string `json:"subject" name:"訂單標題" label:"非必传,用戶支付完成後顯示在手機上的訂單名稱"` | |||
ProductDesc string `json:"productDesc" name:"商品描述" label:"非必传,對一筆交易的具體描述信息,如果是多種商品,請將商品描述字符串累加傳給body。特殊字符不支持"` | |||
MercOrderNo string `json:"mercOrderNo" name:"商戶系統消費訂單號" label:"商戶系統必須確保該訂單號在商戶系統是唯一的不能包含單引號“’”、尖括號“<”“>”和逗號“,”"` | |||
OrderDate string `json:"orderDate" name:"訂單創建日期" label:"格式:yyyyMMdd"` | |||
OrderTime string `json:"orderTime" name:"訂單創建時間" label:"格式:HHmmss"` | |||
ValidNumber string `json:"validNumber" name:"有效期" label:"單位:秒 默認值1200"` | |||
NotifyUrl string `json:"notifyUrl" name:"支付結果後台通知地址" label:"支付通知地址(后缀),商户在商戶服務管理後台配置好通知地址(域名)后,聚合平台会将域名和后缀拼接组成完整的通知地址,并在订单支付成后向此通知地址发送支付结果通知。"` | |||
SubAppId string `json:"subAppId" name:"子商戶AppId" label:"微信分配的子商户公众账号或小程序Id。"` | |||
SubOpenId string `json:"subOpenId" name:"子商戶用戶唯一Id" label:"用戶在子商戶appId下的唯一標識。下單前商戶需要調用微信【網頁授權獲取用戶信息】接口獲取到用戶的OpenId。詳情查看附錄5.5.1章節"` | |||
TransWay string `json:"transWay" name:"交易方式" label:"B3-公众号支付B4-小程序支付"` | |||
Reserved1 string `json:"reserved1" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | |||
Reserved2 string `json:"reserved2" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | |||
Reserved3 string `json:"reserved3" name:"商戶備用字段" label:"交易成功后原樣返回給商戶"` | |||
} |
@@ -157,3 +157,23 @@ func TestCardBindlist(t *testing.T) { | |||
} | |||
t.Log(zhios_pay_utils.SerializeStr(data)) | |||
} | |||
func TestPaymentQueryList(t *testing.T) { | |||
Adapay, err := adapay.GetAdapayByDBSysConfig("jiuhuicang", "app_a794b158-359b-4e5a-8464-5bd8f171bb6e", "api_live_419ec107-9854-45e8-af12-070efcef6ae9", "api_test_d6485653-c09e-405f-b386-8ef2936dfb97", "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwN6xgd6Ad8v2hIIsQVnbt8a3JituR8o4Tc3B5WlcFR55bz4OMqrG/356Ur3cPbc2Fe8ArNd/0gZbC9q56Eb16JTkVNA/fye4SXznWxdyBPR7+guuJZHc/VW2fKH2lfZ2P3Tt0QkKZZoawYOGSMdIvO+WqK44updyax0ikK6JlNQIDAQAB", | |||
"MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMxVAwWM79+2Lx1TYA4muHy40AbwcBwXmvgNJGp2gq7bcDaNd5cmvXndwS6exV2hIGo8er0Qu3myGrwD/4BIoqCDSuqYfJkrlKzKM30+WAfwRulrFW+SOr6qCvGFF2bMdL884NMgd5PCpayASONq/blefQ3/Ic+c0gtZQxJzYzxLAgMBAAECgYEAyS+rGfv68zpeiXFeUpCtdaBCs5Jp0D8txq6p/GlTovdNNWl1Js5uIV/GpjCOA/JyCCgkcaPX86r5x8Xn/FeIRt/U1kkGs2zvKcxQntkOhmzMdDe7/FuwbRUUeM6LWGIc5AeQcBWYZeS8HAFMnlWho+knMqqgHC/fgUyon6YgMAECQQDyjJYSp+hdhTnvo88GPisQEPOoZRbrUnVFbwjD/OrQT7s6uSlbTAJRXGEYfdeaWsh2CoFl986pOqc/84E/Lr3LAkEA16nd1J3stk9XtC4i+iiXY0codiK24kU+pMcEm8NNyMV1nQT5iTcDA1ZeJCgamJZFeqcfyusimBNbCXdBKk2rgQJBAIymodAntkOlIjepEkBYhLhIXENme6fypTaicL7WR4SM99HR1f2vUhjELTn6n7BOvLhW1zq+PQU9kgcvud9dx4kCQFSMXtBFHZEXp/2WfNXv5fHg6sbtsx8gIH//GhpqxerpJsPpOF8H9yFu0beBFXQurYx5SqiF6GkQZYdffme0TYECQQDgOdQxysI1FEM/l8BDnQgmnO7sJqUtP9HsVcKElGdkJMZGqb8B5rt12y4dxpyVfyaTMZZZjqIx8zgQBhn+l0NJ") | |||
if err != nil { | |||
t.Error(err) | |||
} | |||
createPaymentParams := map[string]interface{}{ | |||
"app_id": Adapay.AppId, | |||
"page_index": "1", | |||
"page_size": "10", | |||
} | |||
data, apiError, err := Adapay.Payment().QueryList(createPaymentParams) | |||
if err != nil || apiError != nil { // 网络或本应用异常 | |||
t.Error(err) | |||
t.Error(apiError) | |||
return | |||
} | |||
t.Log(zhios_pay_utils.SerializeStr(data)) | |||
} |
@@ -1,9 +1,13 @@ | |||
package test | |||
import ( | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/lib/boc" | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||
"fmt" | |||
"github.com/syyongx/php2go" | |||
"testing" | |||
"time" | |||
) | |||
func TestJoinStringsInASCII(t *testing.T) { | |||
@@ -11,3 +15,73 @@ func TestJoinStringsInASCII(t *testing.T) { | |||
"", "", false, false) | |||
fmt.Println(ascii) | |||
} | |||
func TestBocSend(t *testing.T) { | |||
var createBocPayAppTradeReq md.CreateBocPayAppTradeReq | |||
//基础信息,必填 | |||
createBocPayAppTradeReq.Version = boc.Version | |||
createBocPayAppTradeReq.Service = boc.CreateBocPayAppTrade | |||
createBocPayAppTradeReq.SignType = boc.SignType | |||
createBocPayAppTradeReq.MerchantId = boc.MerchantId | |||
createBocPayAppTradeReq.TerminalNo = boc.TerminalNo | |||
createBocPayAppTradeReq.RequestId = "2022101401" + zhios_pay_utils.AnyToString(time.Now().Unix()) | |||
//按照接口类型选填信息 | |||
createBocPayAppTradeReq.Amount = "100" | |||
createBocPayAppTradeReq.OriginalAmount = "100" | |||
createBocPayAppTradeReq.MercOrderNo = "2022101401" | |||
createBocPayAppTradeReq.OrderDate = "20221013" | |||
createBocPayAppTradeReq.OrderTime = "175600" | |||
createBocPayAppTradeReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||
createBocPayAppTradeReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||
"mid": "123456", | |||
}) | |||
param := make(map[string]string) | |||
zhios_pay_utils.Unserialize(zhios_pay_utils.Serialize(createBocPayAppTradeReq), ¶m) | |||
send, err := boc.Send(false, boc.PrivateKeyStr, param) | |||
if err != nil { | |||
t.Error(err) | |||
} | |||
t.Logf("回调信息1:%s", send) | |||
resp, _ := php2go.URLDecode(send) | |||
t.Logf("回调信息:%s", resp) | |||
} | |||
func TestCreateBocPayAppTradeReq(t *testing.T) { | |||
var createBocPayAppTradeReq md.CreateBocPayAppTradeReq | |||
//按照接口类型选填信息 | |||
createBocPayAppTradeReq.Amount = "100" | |||
createBocPayAppTradeReq.OriginalAmount = "100" | |||
createBocPayAppTradeReq.MercOrderNo = "2022101402" | |||
createBocPayAppTradeReq.OrderDate = "20221013" | |||
createBocPayAppTradeReq.OrderTime = "175600" | |||
createBocPayAppTradeReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||
createBocPayAppTradeReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||
"mid": "123456", | |||
}) | |||
send, err := boc.CreateBocPayAppTradeReq(false, boc.PrivateKeyStr, boc.MerchantId, boc.TerminalNo, &createBocPayAppTradeReq) | |||
if err != nil { | |||
t.Error(err) | |||
} | |||
t.Logf("回调信息:%s", send) | |||
} | |||
func TestCreateWeChatTradeReq(t *testing.T) { | |||
var CreateWeChatTradeReqReq md.CreateWeChatTradeReq | |||
//按照接口类型选填信息 | |||
CreateWeChatTradeReqReq.Amount = "100" | |||
CreateWeChatTradeReqReq.OriginalAmount = "100" | |||
CreateWeChatTradeReqReq.MercOrderNo = "2022101403" | |||
CreateWeChatTradeReqReq.OrderDate = "20221013" | |||
CreateWeChatTradeReqReq.OrderTime = "175600" | |||
CreateWeChatTradeReqReq.NotifyUrl = "/api/v1/mall/pay/ada/sub_callback" | |||
CreateWeChatTradeReqReq.SubAppId = "wx987580a9437354d1" | |||
CreateWeChatTradeReqReq.SubOpenId = "oEk7V5hNLxQXoTpOcvzRdGJQKnjM" | |||
CreateWeChatTradeReqReq.TransWay = "B3" | |||
CreateWeChatTradeReqReq.Reserved1 = zhios_pay_utils.SerializeStr(map[string]string{ | |||
"mid": "123456", | |||
}) | |||
send, err := boc.CreateWeChatTradeReq(false, boc.PrivateKeyStr, boc.MerchantId, boc.TerminalNo, &CreateWeChatTradeReqReq) | |||
if err != nil { | |||
t.Error(err) | |||
} | |||
t.Logf("回调信息:%s", send) | |||
} |
@@ -0,0 +1,341 @@ | |||
package zhios_pay_utils | |||
import ( | |||
"encoding/json" | |||
"fmt" | |||
"reflect" | |||
"strconv" | |||
"strings" | |||
) | |||
func Map2Struct(vals map[string]interface{}, dst interface{}) (err error) { | |||
return Map2StructByTag(vals, dst, "json") | |||
} | |||
func Map2StructByTag(vals map[string]interface{}, dst interface{}, structTag string) (err error) { | |||
defer func() { | |||
e := recover() | |||
if e != nil { | |||
if v, ok := e.(error); ok { | |||
err = fmt.Errorf("Panic: %v", v.Error()) | |||
} else { | |||
err = fmt.Errorf("Panic: %v", e) | |||
} | |||
} | |||
}() | |||
pt := reflect.TypeOf(dst) | |||
pv := reflect.ValueOf(dst) | |||
if pv.Kind() != reflect.Ptr || pv.Elem().Kind() != reflect.Struct { | |||
return fmt.Errorf("not a pointer of struct") | |||
} | |||
var f reflect.StructField | |||
var ft reflect.Type | |||
var fv reflect.Value | |||
for i := 0; i < pt.Elem().NumField(); i++ { | |||
f = pt.Elem().Field(i) | |||
fv = pv.Elem().Field(i) | |||
ft = f.Type | |||
if f.Anonymous || !fv.CanSet() { | |||
continue | |||
} | |||
tag := f.Tag.Get(structTag) | |||
name, option := parseTag(tag) | |||
if name == "-" { | |||
continue | |||
} | |||
if name == "" { | |||
name = strings.ToLower(f.Name) | |||
} | |||
val, ok := vals[name] | |||
if !ok { | |||
if option == "required" { | |||
return fmt.Errorf("'%v' not found", name) | |||
} | |||
if len(option) != 0 { | |||
val = option // default value | |||
} else { | |||
//fv.Set(reflect.Zero(ft)) // TODO set zero value or just ignore it? | |||
continue | |||
} | |||
} | |||
// convert or set value to field | |||
vv := reflect.ValueOf(val) | |||
vt := reflect.TypeOf(val) | |||
if vt.Kind() != reflect.String { | |||
// try to assign and convert | |||
if vt.AssignableTo(ft) { | |||
fv.Set(vv) | |||
continue | |||
} | |||
if vt.ConvertibleTo(ft) { | |||
fv.Set(vv.Convert(ft)) | |||
continue | |||
} | |||
return fmt.Errorf("value type not match: field=%v(%v) value=%v(%v)", f.Name, ft.Kind(), val, vt.Kind()) | |||
} | |||
s := strings.TrimSpace(vv.String()) | |||
if len(s) == 0 && option == "required" { | |||
return fmt.Errorf("value of required argument can't not be empty") | |||
} | |||
fk := ft.Kind() | |||
// convert string to value | |||
if fk == reflect.Ptr && ft.Elem().Kind() == reflect.String { | |||
fv.Set(reflect.ValueOf(&s)) | |||
continue | |||
} | |||
if fk == reflect.Ptr || fk == reflect.Struct { | |||
err = convertJsonValue(s, name, fv) | |||
} else if fk == reflect.Slice { | |||
err = convertSlice(s, f.Name, ft, fv) | |||
} else { | |||
err = convertValue(fk, s, f.Name, fv) | |||
} | |||
if err != nil { | |||
return err | |||
} | |||
continue | |||
} | |||
return nil | |||
} | |||
func Struct2Map(s interface{}) map[string]interface{} { | |||
return Struct2MapByTag(s, "json") | |||
} | |||
func Struct2MapByTag(s interface{}, tagName string) map[string]interface{} { | |||
t := reflect.TypeOf(s) | |||
v := reflect.ValueOf(s) | |||
if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { | |||
t = t.Elem() | |||
v = v.Elem() | |||
} | |||
if v.Kind() != reflect.Struct { | |||
return nil | |||
} | |||
m := make(map[string]interface{}) | |||
for i := 0; i < t.NumField(); i++ { | |||
fv := v.Field(i) | |||
ft := t.Field(i) | |||
if !fv.CanInterface() { | |||
continue | |||
} | |||
if ft.PkgPath != "" { // unexported | |||
continue | |||
} | |||
var name string | |||
var option string | |||
tag := ft.Tag.Get(tagName) | |||
if tag != "" { | |||
ts := strings.Split(tag, ",") | |||
if len(ts) == 1 { | |||
name = ts[0] | |||
} else if len(ts) > 1 { | |||
name = ts[0] | |||
option = ts[1] | |||
} | |||
if name == "-" { | |||
continue // skip this field | |||
} | |||
if name == "" { | |||
name = strings.ToLower(ft.Name) | |||
} | |||
if option == "omitempty" { | |||
if isEmpty(&fv) { | |||
continue // skip empty field | |||
} | |||
} | |||
} else { | |||
name = strings.ToLower(ft.Name) | |||
} | |||
if ft.Anonymous && fv.Kind() == reflect.Ptr && fv.IsNil() { | |||
continue | |||
} | |||
if (ft.Anonymous && fv.Kind() == reflect.Struct) || | |||
(ft.Anonymous && fv.Kind() == reflect.Ptr && fv.Elem().Kind() == reflect.Struct) { | |||
// embedded struct | |||
embedded := Struct2MapByTag(fv.Interface(), tagName) | |||
for embName, embValue := range embedded { | |||
m[embName] = embValue | |||
} | |||
} else if option == "string" { | |||
kind := fv.Kind() | |||
if kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64 { | |||
m[name] = strconv.FormatInt(fv.Int(), 10) | |||
} else if kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64 { | |||
m[name] = strconv.FormatUint(fv.Uint(), 10) | |||
} else if kind == reflect.Float32 || kind == reflect.Float64 { | |||
m[name] = strconv.FormatFloat(fv.Float(), 'f', 2, 64) | |||
} else { | |||
m[name] = fv.Interface() | |||
} | |||
} else { | |||
m[name] = fv.Interface() | |||
} | |||
} | |||
return m | |||
} | |||
func isEmpty(v *reflect.Value) bool { | |||
k := v.Kind() | |||
if k == reflect.Bool { | |||
return v.Bool() == false | |||
} else if reflect.Int < k && k < reflect.Int64 { | |||
return v.Int() == 0 | |||
} else if reflect.Uint < k && k < reflect.Uintptr { | |||
return v.Uint() == 0 | |||
} else if k == reflect.Float32 || k == reflect.Float64 { | |||
return v.Float() == 0 | |||
} else if k == reflect.Array || k == reflect.Map || k == reflect.Slice || k == reflect.String { | |||
return v.Len() == 0 | |||
} else if k == reflect.Interface || k == reflect.Ptr { | |||
return v.IsNil() | |||
} | |||
return false | |||
} | |||
func convertSlice(s string, name string, ft reflect.Type, fv reflect.Value) error { | |||
var err error | |||
et := ft.Elem() | |||
if et.Kind() == reflect.Ptr || et.Kind() == reflect.Struct { | |||
return convertJsonValue(s, name, fv) | |||
} | |||
ss := strings.Split(s, ",") | |||
if len(s) == 0 || len(ss) == 0 { | |||
return nil | |||
} | |||
fs := reflect.MakeSlice(ft, 0, len(ss)) | |||
for _, si := range ss { | |||
ev := reflect.New(et).Elem() | |||
err = convertValue(et.Kind(), si, name, ev) | |||
if err != nil { | |||
return err | |||
} | |||
fs = reflect.Append(fs, ev) | |||
} | |||
fv.Set(fs) | |||
return nil | |||
} | |||
func convertJsonValue(s string, name string, fv reflect.Value) error { | |||
var err error | |||
d := StringToSlice(s) | |||
if fv.Kind() == reflect.Ptr { | |||
if fv.IsNil() { | |||
fv.Set(reflect.New(fv.Type().Elem())) | |||
} | |||
} else { | |||
fv = fv.Addr() | |||
} | |||
err = json.Unmarshal(d, fv.Interface()) | |||
if err != nil { | |||
return fmt.Errorf("invalid json '%v': %v, %v", name, err.Error(), s) | |||
} | |||
return nil | |||
} | |||
func convertValue(kind reflect.Kind, s string, name string, fv reflect.Value) error { | |||
if !fv.CanAddr() { | |||
return fmt.Errorf("can not addr: %v", name) | |||
} | |||
if kind == reflect.String { | |||
fv.SetString(s) | |||
return nil | |||
} | |||
if kind == reflect.Bool { | |||
switch s { | |||
case "true": | |||
fv.SetBool(true) | |||
case "false": | |||
fv.SetBool(false) | |||
case "1": | |||
fv.SetBool(true) | |||
case "0": | |||
fv.SetBool(false) | |||
default: | |||
return fmt.Errorf("invalid bool: %v value=%v", name, s) | |||
} | |||
return nil | |||
} | |||
if reflect.Int <= kind && kind <= reflect.Int64 { | |||
i, err := strconv.ParseInt(s, 10, 64) | |||
if err != nil { | |||
return fmt.Errorf("invalid int: %v value=%v", name, s) | |||
} | |||
fv.SetInt(i) | |||
} else if reflect.Uint <= kind && kind <= reflect.Uint64 { | |||
i, err := strconv.ParseUint(s, 10, 64) | |||
if err != nil { | |||
return fmt.Errorf("invalid int: %v value=%v", name, s) | |||
} | |||
fv.SetUint(i) | |||
} else if reflect.Float32 == kind || kind == reflect.Float64 { | |||
i, err := strconv.ParseFloat(s, 64) | |||
if err != nil { | |||
return fmt.Errorf("invalid float: %v value=%v", name, s) | |||
} | |||
fv.SetFloat(i) | |||
} else { | |||
// not support or just ignore it? | |||
// return fmt.Errorf("type not support: field=%v(%v) value=%v(%v)", name, ft.Kind(), val, vt.Kind()) | |||
} | |||
return nil | |||
} | |||
func parseTag(tag string) (string, string) { | |||
tags := strings.Split(tag, ",") | |||
if len(tags) <= 0 { | |||
return "", "" | |||
} | |||
if len(tags) == 1 { | |||
return tags[0], "" | |||
} | |||
return tags[0], tags[1] | |||
} |
@@ -0,0 +1,47 @@ | |||
package zhios_pay_utils | |||
import ( | |||
"fmt" | |||
"reflect" | |||
"strings" | |||
"unsafe" | |||
) | |||
// string与slice互转,零copy省内存 | |||
// zero copy to change slice to string | |||
func Slice2String(b []byte) (s string) { | |||
pBytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) | |||
pString := (*reflect.StringHeader)(unsafe.Pointer(&s)) | |||
pString.Data = pBytes.Data | |||
pString.Len = pBytes.Len | |||
return | |||
} | |||
// no copy to change string to slice | |||
func StringToSlice(s string) (b []byte) { | |||
pBytes := (*reflect.SliceHeader)(unsafe.Pointer(&b)) | |||
pString := (*reflect.StringHeader)(unsafe.Pointer(&s)) | |||
pBytes.Data = pString.Data | |||
pBytes.Len = pString.Len | |||
pBytes.Cap = pString.Len | |||
return | |||
} | |||
// 任意slice合并 | |||
func SliceJoin(sep string, elems ...interface{}) string { | |||
l := len(elems) | |||
if l == 0 { | |||
return "" | |||
} | |||
if l == 1 { | |||
s := fmt.Sprint(elems[0]) | |||
sLen := len(s) - 1 | |||
if s[0] == '[' && s[sLen] == ']' { | |||
return strings.Replace(s[1:sLen], " ", sep, -1) | |||
} | |||
return s | |||
} | |||
sep = strings.Replace(fmt.Sprint(elems), " ", sep, -1) | |||
return sep[1 : len(sep)-1] | |||
} |