From 1a5a3ffc29b5a3c3090d3790349eac7368fbc1b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E5=AE=97=E6=A5=A0?= Date: Sun, 27 Apr 2025 10:17:40 +0800 Subject: [PATCH] 1 --- platformapi/dadaapi/dadaapi_test.go | 57 ++++ platformapi/ebaiapi/shop_sku_test.go | 2 +- platformapi/fnpsapi/fn_test.go | 4 +- platformapi/mtwmapi/mtwmapi_test.go | 6 +- platformapi/mtwmapi/order_test.go | 2 +- platformapi/mtwmapi/poi_test.go | 29 +- platformapi/tao_vegetable/order_test.go | 14 +- platformapi/tonglianpayapi/callback.go | 54 +++- platformapi/tonglianpayapi/tonglian_model.go | 282 ++++++++++++++++++ .../tonglianpayapi/tonglian_online_pay.go | 194 ++++++++++++ .../tonglian_online_pay_test.go | 101 +++++++ platformapi/tonglianpayapi/tonglianpayapi.go | 134 --------- .../tonglianpayapi/tonglianpayapi_test.go | 48 +++ platformapi/tonglianpayapi/yun_mis_pay.go | 149 +++++++++ 14 files changed, 920 insertions(+), 156 deletions(-) create mode 100644 platformapi/tonglianpayapi/tonglian_model.go create mode 100644 platformapi/tonglianpayapi/tonglian_online_pay.go create mode 100644 platformapi/tonglianpayapi/tonglian_online_pay_test.go create mode 100644 platformapi/tonglianpayapi/yun_mis_pay.go diff --git a/platformapi/dadaapi/dadaapi_test.go b/platformapi/dadaapi/dadaapi_test.go index 3c56d70e..9ad12334 100644 --- a/platformapi/dadaapi/dadaapi_test.go +++ b/platformapi/dadaapi/dadaapi_test.go @@ -3,8 +3,10 @@ package dadaapi import ( "fmt" "git.rosy.net.cn/baseapi" + "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/model" "go.uber.org/zap" + "strings" "testing" ) @@ -194,3 +196,58 @@ func Test222222(t *testing.T) { // } // } //} + +func Test111(t *testing.T) { + SplitUniversalOrderID("313943552673") +} + +func SplitUniversalOrderID(universalOrderID string) (orderID string, vendorID int) { + index := strings.Index(universalOrderID, "|") + if index != -1 { + orderID = universalOrderID[:index] + vendorID = int(utils.Str2Int64(universalOrderID[index+1:])) + } else { + if vendorID = GetPossibleVendorIDFromVendorOrderID(universalOrderID); vendorID == -1 { + panic(fmt.Sprintf("unkown order type, orderID:%s", universalOrderID)) + } + orderID = universalOrderID + } + return orderID, vendorID +} + +func GetPossibleVendorIDFromVendorOrderID(vendorOrderID string) (vendorID int) { + vendorID = -1 + //if vendorOrderIDInt64 := utils.Str2Int64WithDefault(vendorOrderID, 0); vendorOrderIDInt64 > 0 { + orderIDLen := len(vendorOrderID) + // 5287873015048 13 wsc + // 15380342248732 14 old ebai order + // 800402581000221 15,16 jd order + // 33437032333978492 17 mtwm order + // 3022716176275221584 19 elm order, new ebai order + + // 京东到家从2020年开始订单号的长度都会在现有基础上加一位,订单号的前两位取的是当年的最后两位数(如:2020取的20),以适应业务的发展。 + // 改造点: + // 1、订单号位数变化,由原有15位数增加1位数调整为16位数,对接商家需检查是否有对订单号位数做长度校验。 + // 2、第一位数字发生变化,由原来9开头调整为当年年份后两位数如:2020年订单开头为20; + if orderIDLen == len("925265130002541") || orderIDLen == len("1925265130002541") { + vendorID = 0 + } else if orderIDLen == len("3022716176275221584") { + // vendorID = model.VendorIDELM + vendorID = 3 // 饿百零售开放平台订单接口中订单ID“order_id”字段长度将调整为19位,和饿了么订单ID“eleme_order_id”字段格式保持一致。 + } else if orderIDLen == len("15380342248732") { + if vendorOrderID[:2] == "88" { + vendorID = 9 + } else { + vendorID = 3 + } + } else if orderIDLen == len("33437032333978492") || orderIDLen == len("116379390766579767") { + vendorID = 1 + } else if orderIDLen == len("5287873015048") { + vendorID = 11 + } else if orderIDLen == len("1000004390") { + vendorID = 9 + } else if orderIDLen == len("18100216009800000001") || orderIDLen == len("191075245758000000039") { + vendorID = 5 + } + return vendorID +} diff --git a/platformapi/ebaiapi/shop_sku_test.go b/platformapi/ebaiapi/shop_sku_test.go index 640b51d5..ecf8e362 100644 --- a/platformapi/ebaiapi/shop_sku_test.go +++ b/platformapi/ebaiapi/shop_sku_test.go @@ -328,7 +328,7 @@ func TestDeleteStoreSku(t *testing.T) { } func TestDeleteSku(t *testing.T) { - shopId := "100829" + shopId := "804627" param1 := &SkuListParams{ Page: 1, PageSize: 100, diff --git a/platformapi/fnpsapi/fn_test.go b/platformapi/fnpsapi/fn_test.go index 24576c03..75e8e8bc 100644 --- a/platformapi/fnpsapi/fn_test.go +++ b/platformapi/fnpsapi/fn_test.go @@ -29,8 +29,8 @@ func init() { func TestQueryOneStore(t *testing.T) { api = New("6705486294797503379", "c1e6c280-e618-4103-9d0a-673bc54fb22e", "51658", "") //token, err := api.GetAccessToken() - api.accessToken = "8cffab79-4dab-4a9a-a609-c1ff6b14109b" - data, err := api.GetStore("20002") + api.accessToken = "eb855784-ca08-460b-be73-5681ceb28222" + data, err := api.GetStore("5000009") fmt.Println(data) fmt.Println(err) } diff --git a/platformapi/mtwmapi/mtwmapi_test.go b/platformapi/mtwmapi/mtwmapi_test.go index 5f58ff7b..bb0b73ff 100644 --- a/platformapi/mtwmapi/mtwmapi_test.go +++ b/platformapi/mtwmapi/mtwmapi_test.go @@ -23,10 +23,10 @@ func init() { //api = New("589", "a81eb3df418d83d6a1a4b7c572156d2f", "", "") // 果园 - api = New("4123", "df2c88338b85f830cebce2a9eab56628", "", "") + //api = New("4123", "df2c88338b85f830cebce2a9eab56628", "", "") //商超 - //api = New("5873", "41c479790a76f86326f89e8048964739", "", "token_hGnQOvCSkfrCOXFLHA1NMQ") + api = New("5873", "41c479790a76f86326f89e8048964739", "", "token_n49KrTnbe-Qatd-80sFOnQ") //cookieStr := ` // acctId=57396785; token=0bWbK5VbK50E2BmIhIH2zHB-am_y7mB37yXHm6RLZWx4*; wmPoiId=-1; //` @@ -53,7 +53,7 @@ func TestGetOAuthCode(t *testing.T) { } func TestPreparationMealComplete(t *testing.T) { - api.PreparationMealComplete(900658760944919842) + api.PreparationMealComplete(3901564910320888932) } func TestGetAccessToken(t *testing.T) { diff --git a/platformapi/mtwmapi/order_test.go b/platformapi/mtwmapi/order_test.go index a7d275a6..2bb7ae3e 100644 --- a/platformapi/mtwmapi/order_test.go +++ b/platformapi/mtwmapi/order_test.go @@ -31,7 +31,7 @@ func getTimeFromTimestamp(timeStamp int64) time.Time { } func TestOrderGetOrderDetail(t *testing.T) { - result, err := api.OrderGetOrderDetail(2001521861299113073, false) + result, err := api.OrderGetOrderDetail(5401564660946475322, false) if err != nil { t.Fatal(err) } diff --git a/platformapi/mtwmapi/poi_test.go b/platformapi/mtwmapi/poi_test.go index 73dd2c6a..3fc27d4a 100644 --- a/platformapi/mtwmapi/poi_test.go +++ b/platformapi/mtwmapi/poi_test.go @@ -6,6 +6,7 @@ import ( "git.rosy.net.cn/baseapi/utils" "strings" "testing" + "time" ) const ( @@ -24,16 +25,30 @@ func TestPoiGetIDs(t *testing.T) { } func Test1111(t *testing.T) { - result, err := api.PoiMGet([]string{"17821732"}) - if err != nil { - fmt.Println(err) + storeIds := []int64{ + 12524795, } - for _, v := range result { - if strings.Contains(v.Name, "京西") { - data := fmt.Sprintf("%s %s ", v.AppPoiCode, v.Name) + fmt.Sprintf("
", v.PicURL) - fmt.Println(data) + + for k, v := range storeIds { + result, err := api.PoiMGet([]string{utils.Int64ToStr(v)}) + if err != nil { + fmt.Println(k) + continue } + if result == nil || len(result) == 0 { + fmt.Println(k) + continue + } + for _, v2 := range result { + if strings.Contains(v2.Name, "京西") { + fmt.Println(fmt.Sprintf("
", v2.PicURL)) + } else { + fmt.Println(k) + } + } + time.Sleep(200 * time.Microsecond) } + } func TestPoiMGet(t *testing.T) { result, err := api.PoiMGet([]string{"17821732"}) diff --git a/platformapi/tao_vegetable/order_test.go b/platformapi/tao_vegetable/order_test.go index 244294b8..f46ab3c6 100644 --- a/platformapi/tao_vegetable/order_test.go +++ b/platformapi/tao_vegetable/order_test.go @@ -22,8 +22,8 @@ func TestName(t *testing.T) { } func TestGetOrderDetail(t *testing.T) { requestParam := &request591.AlibabaAelophyOrderGetRequest{OrderGetRequest: &domain591.AlibabaAelophyOrderGetOrderGetRequest{ - StoreId: utils.String2Pointer("JX667128"), - BizOrderId: utils.Int64ToPointer(8000211756001500978), + StoreId: utils.String2Pointer("JX100236"), + BizOrderId: utils.Int64ToPointer(8000245939558000686), }} data, err := apiTao.QueryOrderDetail(requestParam) @@ -74,13 +74,13 @@ func Test222(t *testing.T) { // 接单 func TestDeliveryFinishACCEPTED(t *testing.T) { - orderID := 8000101702997900468 - storeID := "JX102364" + orderID := 8000253695335600623 + storeID := "JX666900" param := &request591.AlibabaAelophyOrderWorkCallbackRequest{} param.WorkCallbackRequest = &domain591.AlibabaAelophyOrderWorkCallbackWorkCallbackRequest{ - StoreId: utils.String2Pointer("JX102364"), + StoreId: utils.String2Pointer(storeID), BizOrderId: utils.Int64ToPointer(int64(orderID)), - Status: utils.String2Pointer(OrderStatusSuccess), + Status: utils.String2Pointer(OrderStatusPickedUp), StatusRemark: nil, //DelivererName: utils.String2Pointer("张廷"), //DelivererPhone: utils.String2Pointer("SIGN"), @@ -107,7 +107,7 @@ func TestDeliveryFinishACCEPTED(t *testing.T) { workCallbackSubOrderInfoList = append(workCallbackSubOrderInfoList, workCallbackSubOrderInfo) } param.WorkCallbackRequest.WorkCallbackSubOrderInfoList = &workCallbackSubOrderInfoList - apiTao.SetToken("50000C00432zMSClqLiSDjBr2NyiazjtFmsgTOdbBfti16a73f18k0XDfkUQywSmcjfC") + apiTao.SetToken("50000C00e13l3SYxqadamvelw15656b52A0xeXfaS8nyVCgitlzIq1nDwFzYdcQpIsVz") //apiTao.SetToken("50002C01524csKWniqfypk8dbshKRTeFIOIy1a46c0cfgsvodWjsseMsXqpvWvECMj1") err = apiTao.DeliveryFinish(param) fmt.Println(err) diff --git a/platformapi/tonglianpayapi/callback.go b/platformapi/tonglianpayapi/callback.go index 7f19b46b..bd6eb2d9 100644 --- a/platformapi/tonglianpayapi/callback.go +++ b/platformapi/tonglianpayapi/callback.go @@ -14,7 +14,12 @@ const ( MsgTypePayZFB = "VSP511" //支付宝 MsgTypeRefundZFB = "VSP513" - TrxStatusSuccess = "0000" + TrxStatusSuccess = "0000" // 交易成功 + TrxStatusTransaction = "2000" // 支付中 + TrxStatusTransaction2 = "2008" // 支付中 + OnlinePayStatusSuccess = "SUCCESS" + OnlinePayStatusFail = "FAIL" + MisStatusSuccess = "Y0000" // 调用成功 ) type CallBackResult struct { @@ -57,3 +62,50 @@ func (a *API) GetCallbackMsg(request *http.Request) (call *CallBackResult, err e utils.Map2StructByJson(mapData, &call, false) return call, err } + +// CallBackResultOnlinePay 扫码支付回调 +type CallBackResultOnlinePay struct { + AppID string `json:"appID"` + OuttrxID string `json:"outtrxID"` + TrxCode string `json:"trxCode"` + TrxID string `json:"trxID"` + TrxAmt string `json:"trxAmt"` + TrxDate string `json:"trxDate"` + PayTime string `json:"payTime"` + ChnlTrxID string `json:"chnlTrxID"` + TrxStatus string `json:"trxStatus"` + CusID string `json:"cusID"` + TermNo string `json:"termNo"` + TermBatchid string `json:"termBatchid"` + TermTraceno string `json:"termTraceno"` + TermAuthno string `json:"termAuthno"` + TermRefnum string `json:"termRefnum"` + TrxReserved string `json:"trxReserved"` + SrcTrxid string `json:"srcTrxid"` + CusorderID string `json:"cusorderID"` + Acct string `json:"acct"` + Fee string `json:"fee"` + SignType string `json:"signType"` + Cmid string `json:"cmid"` + Chnlid string `json:"chnlid"` + Sign string `json:"sign"` + Initamt string `json:"initamt"` + Chnldata string `json:"chnldata"` + Accttype string `json:"accttype"` + Bankcode string `json:"bankcode"` + Logonid string `json:"logonid"` +} + +func (a *API) GetCallbackOnlinePayMsg(request *http.Request) (call *CallBackResultOnlinePay, err error) { + data, err := ioutil.ReadAll(request.Body) + if err != nil { + return nil, err + } + values, err := utils.HTTPBody2Values(data, false) + if err != nil { + return nil, err + } + mapData := utils.URLValues2Map(values) + utils.Map2StructByJson(mapData, &call, false) + return call, err +} diff --git a/platformapi/tonglianpayapi/tonglian_model.go b/platformapi/tonglianpayapi/tonglian_model.go new file mode 100644 index 00000000..879aa8ce --- /dev/null +++ b/platformapi/tonglianpayapi/tonglian_model.go @@ -0,0 +1,282 @@ +package tonglianpayapi + +import ( + "crypto/md5" + "fmt" + "git.rosy.net.cn/baseapi/platformapi" + "net/http" + "sort" + "strings" +) + +const ( + prodURL = "https://vsp.allinpay.com/apiweb" + prodURL2 = "https://syb.allinpay.com/apiweb" + sepcAction = "unitorder/pay" + sepcAction2 = "h5unionpay/unionorder" + + sigKey = "sign" + PayTypeWxXcx = "W06" + PayTypeWxCode = "W01" + PayTypeZfbApp = "A03" + PayTypeZfbQrcode = "A01" + PayTypeZfbJS = "A02" + PayTypeH5 = "H5" + + ResponseCodeSuccess = "SUCCESS" + ResponseCodeFail = "FAIL" +) + +type API struct { + appID string + cusID string + appKey string + + client *http.Client + config *platformapi.APIConfig +} + +func New(appID, appKey, cusID string, config ...*platformapi.APIConfig) *API { + curConfig := platformapi.DefAPIConfig + if len(config) > 0 { + curConfig = *config[0] + } + return &API{ + appID: appID, + appKey: appKey, + cusID: cusID, + client: &http.Client{Timeout: curConfig.ClientTimeout}, + config: &curConfig, + } +} + +func (a *API) signParam(params map[string]interface{}) (sig string) { + var valueList []string + for k, v := range params { + if k != sigKey { + if str := fmt.Sprint(v); str != "" { + valueList = append(valueList, fmt.Sprintf("%s=%s", k, str)) + } + } + } + valueList = append(valueList, fmt.Sprintf("key=%s", a.appKey)) + sort.Sort(sort.StringSlice(valueList)) + sig = strings.Join(valueList, "&") + binSig := md5.Sum([]byte(sig)) + sig = fmt.Sprintf("%X", binSig) + return sig +} + +type CreateUnitorderOrderParam struct { + CusID string `json:"cusid"` + AppID string `json:"appid"` + Trxamt int `json:"trxamt"` //交易金额 单位为分 + Reqsn string `json:"reqsn"` //商户交易单号 + NotifyUrl string `json:"notifyUrl"` //接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 + Acct string `json:"acct"` + PayType string `json:"paytype"` + SubAppID string `json:"subAppID"` +} + +type CreateUnitOrderOrderResult struct { + RetCode string `json:"retCode"` + RetMsg string `json:"retMsg"` + CusID string `json:"cusID"` + AppID string `json:"appID"` + TrxID string `json:"trxID"` + ChnltrxID string `json:"chnltrxID"` + Reqsn string `json:"reqsn"` + RandomStr string `json:"randomStr"` + TrxStatus string `json:"trxStatus"` + FinTime string `json:"finTime"` + ErrMsg string `json:"errMsg"` + PayInfo string `json:"payInfo"` + Sign string `json:"sign"` +} + +type PayInfo struct { + AppID string `json:"appID"` + TimeStamp string `json:"timeStamp"` + NonceStr string `json:"nonceStr"` + Package string `json:"package"` + SignType string `json:"signType"` + PaySign string `json:"paySign"` +} + +type PayRefundParam struct { + CusID string `json:"cusid"` + AppID string `json:"appid"` + Trxamt int `json:"trxamt"` //交易金额 单位为分 + Reqsn string `json:"reqsn"` //商户交易单号 + OldReqsn string `json:"oldReqsn"` + Remark string `json:"remark"` + RandomStr string `json:"randomStr"` + Sign string `json:"sign"` + TrxID string `json:"trxID"` + OldTrxID string `json:"oldTrxID"` +} + +type PayRefundResult struct { + RetCode string `json:"retCode"` + RetMsg string `json:"retMsg"` + CusID string `json:"cusID"` + AppID string `json:"appID"` + TrxID string `json:"trxID"` + Reqsn string `json:"reqsn"` + TrxStatus string `json:"trxStatus"` + FinTime string `json:"finTime"` + ErrMsg string `json:"errMsg"` + RandomStr string `json:"randomStr"` + Sign string `json:"sign"` + Fee string `json:"fee"` + TrxCode string `json:"trxCode"` +} + +type CreateH5UnitorderOrderParam struct { + Trxamt int `json:"trxamt"` //交易金额 单位为分 + Reqsn string `json:"reqsn"` //商户交易单号 + NotifyUrl string `json:"notifyUrl"` //接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 + Charset string `json:"charset"` + Returl string `json:"returl"` + Body string `json:"body"` +} + +// OnlinePayParam 扫码枪扫码支付参数 +type OnlinePayParam struct { + //参数 参数名称 可空 备注 + Orgid string `json:"orgid"` // 集团/代理商商户号 是 共享集团号/代理商参数时必填 + Cusid string `json:"cusid"` // 商户号实际交易的商户号 否 + Appid string `json:"appid"` // 应用ID平台分配的APPID 否 + Version string `json:"version"` // 版本号接口版本号 可 + Randomstr string `json:"randomstr"` // 随机字符串 否 商户自行生成的随机字符串 + Trxamt string `json:"trxamt"` // 交易金额单位为分 否 + Reqsn string `json:"reqsn"` // 商户交易单号 否 保证商户平台唯一 + Body string `json:"body"` // 订单商品名称,为空则以商户名作为商品名称 是 + Remark string `json:"remark"` // 备注 是 禁止出现+,空格,/,?,%,#,&, = 这几类特殊符号 + Authcode string `json:"authcode"` // 支付授权码如微信, 支付宝, 银联的付款二维码 否 + LimitPay string `json:"limit_pay"` // 支付限制 no_credit--指定不能使用信用卡支付 是 32 暂时只对微信支付和支付宝, 支付宝支付有效, 仅支持no_credit + GoodsTag string `json:"goods_tag"` // 订单优惠标记 订单优惠标记,用于区分订单是否可以享受优惠,字段内容在微信后台配置券时进行设置,说明详见代金券或立减优惠 是 只对微信支付有效 + Benefitdetail string `json:"benefitdetail"` // 优惠信息Benefitdetail的json字符串, 注意是String 填写格式详见附录5.8 是 + SubAppid string `json:"sub_appid"` // 微信子appid 微信小程序/微信公众号/APP的appid 是 只对微信支付有效 + Chnlstoreid string `json:"chnlstoreid"` // 渠道门店编号 商户在支付渠道端的门店编号 对于支付宝支付,支付宝门店编号 对于微信支付,微信门店编号 + Subbranch string `json:"subbranch"` // 门店号 是 4 + Idno string `json:"idno"` // 证件号实名交易必填.填了此字段就会验证证件号和姓名 是 暂只支持支付宝 + Extendparams string `json:"extendparams"` // 拓展参数 json字符串,注意是String一般用于渠道的活动参数填写 是 参考5.9拓展参数附录说明 + Truename string `json:"truename"` // 付款人真实姓名实名交易必填.填了此字段就会验证证件号和姓名 是 暂只支持支付宝 + Asinfo string `json:"asinfo"` // 分账信息 格式:cusid:type:amount cusid:type:amount… 其中 cusid:接收分账的通联商户号 type分账类型(01:按金额 02:按比率) 如果分账类型为02,则分账比率为0.5表示50%。如果分账类型为01,则分账金额以元为单位表示 是 + Fqnum string `json:"fqnum"` // 分期 + NotifyUrl string `json:"notify_url"` // 交易结果通知地址 接收交易结果的通知回调地址,通知url必须为直接可访问的url,不能携带参数。 https只支持默认端口 是 + Signtype string `json:"signtype"` /// 签名方式 是 + Unpid string `json:"unpid"` // 银联pid 仅支持代理商/服务商角色调用 + Sign string `json:"sign"` // 签名 详见安全规范 否 + Terminfo string `json:"terminfo"` // 终端信息终端信息的json字符串 详见附录5.10终端字段说明 否 + Operatorid string `json:"operatorid"` // 收银员号 是 +} +type TerminfoBase struct { + Termno string `json:"termno"` // 终端号 必填 + Devicetype string `json:"devicetype"` // 设备类型 必填 + Termsn string `json:"termsn"` // 终端序列号 + Encryptrandnum string `json:"encryptrandnum"` // 加密随机因子 + Secrettext string `json:"secrettext"` // 密文数据 + Appversion string `json:"appversion"` // 终端程序版本号 + Longitude string `json:"longitude"` // 经度 银联扫码必送 + Latitude string `json:"latitude"` // 纬度 银联扫码必送 + Deviceip string `json:"deviceip"` // 终端Ip 如微信支付宝交易经纬度未上送,该字段必送 +} + +// OnlinePayResult 扫码枪支付返回参数 +type OnlinePayResult struct { + Retcode string `json:"retcode"` // SUCCESS/FAIL + Retmsg string `json:"retmsg"` // 返回码说明 +} +type OnlinePayRetMsg struct { + Cusid string `json:"cusid"` // 商户号实际交易的商户号 否 + Appid string `json:"appid"` // 应用ID平台分配的APPID 否 + Trxid string `json:"trxid"` // 收银宝平台的交易流水号 + Chnltrxid string `json:"chnltrxid"` // 例如微信,支付宝平台的交易单号 + Reqsn string `json:"reqsn"` // 商户的交易订单号 + Trxstatus string `json:"trxstatus"` // 交易的状态, + Acct string `json:"acct"` // 微信支付-用户的微信openid支付宝支付-用户user_id + Trxcode string `json:"trxcode"` // 交易类型 + Fintime string `json:"fintime"` // yyyyMMddHHmmss + Errmsg string `json:"errmsg"` // 失败的原因说明 + Randomstr string `json:"randomstr"` // 随机生成的字符串 + Initamt string `json:"initamt"` // 原交易金额 + Trxamt string `json:"trxamt"` // 实际交易金额 + Fee string `json:"fee"` // 手续费 + Cmid string `json:"cmid"` // 渠道子商户号 + Chnlid string `json:"chnlid"` // 渠道号 + Chnldata string `json:"chnldata"` // 渠道信息 + Accttype string `json:"accttype"` // 借贷标识 + Sign string `json:"sign"` // 签名 +} + +// PayTransactionStatusQuery 交易状态查询 +type PayTransactionStatusQuery struct { + Orgid string `json:"orgid"` // 集团/代理商商户号 是 共享集团号/代理商参数时必填 + Cusid string `json:"cusid"` // 商户号实际交易的商户号 否 + Appid string `json:"appid"` // 应用ID平台分配的APPID 否 + Version string `json:"version"` // 版本号接口版本号 可 + Reqsn string `json:"reqsn"` // 商户交易单号 否 保证商户平台唯一 + Trxid string `json:"trxid"` // 收银宝平台的交易流水号 + Randomstr string `json:"randomstr"` // 随机字符串 否 商户自行生成的随机字符串 + Signtype string `json:"signtype"` /// 签名方式 是 + Sign string `json:"sign"` // 签名 详见安全规范 否 +} +type PayTransactionStatusResult struct { + Cusid string `json:"cusid"` // 商户号实际交易的商户号 否 + Appid string `json:"appid"` // 应用ID平台分配的APPID 否 + Trxid string `json:"trxid"` // 收银宝平台的交易流水号 + Chnltrxid string `json:"chnltrxid"` // 例如微信,支付宝平台的交易单号 + Reqsn string `json:"reqsn"` // 商户的交易订单号 + Trxcode string `json:"trxcode"` // 交易类型 + Trxamt string `json:"trxamt"` // 实际交易金额 + Trxstatus string `json:"trxstatus"` // 交易的状态, + Acct string `json:"acct"` // 支付平台用户标识 + Fintime string `json:"fintime"` // yyyyMMddHHmmss + Randomstr string `json:"randomstr"` // 随机生成的字符串 + Errmsg string `json:"errmsg"` // 失败的原因说明 + Cmid string `json:"cmid"` // 渠道子商户号 + Chnlid string `json:"chnlid"` // 渠道号 + Initamt string `json:"initamt"` // 原交易金额 + Fee string `json:"fee"` // 手续费 + Chnldata string `json:"chnldata"` // 渠道信息 + Accttype string `json:"accttype"` // 借贷标识 + Bankcode string `json:"bankcode"` // 所属银行 + Logonid string `json:"logonid"` // 买家账号 + Sign string `json:"sign"` // 签名 +} + +// OnLineOrderRefundParam 订单退款参数 +type OnLineOrderRefundParam struct { + Orgid string `json:"orgid"` // 集团/代理商商户号 是 共享集团号/代理商参数时必填 + Cusid string `json:"cusid"` // 商户号实际交易的商户号 否 + Appid string `json:"appid"` // 应用ID平台分配的APPID 否 + Version string `json:"version"` // 版本号接口版本号 可 + + Trxamt string `json:"trxamt"` // 交易金额单位为分 否 + Reqsn string `json:"reqsn"` // 商户交易单号 否 保证商户平台唯一 + Oldreqsn string `json:"oldreqsn"` // 原交易订单号 是 + Oldtrxid string `json:"oldtrxid"` // 原交易流水 是 + Remark string `json:"remark"` // 备注 是 禁止出现+,空格,/,?,%,#,&, = 这几类特殊符号 + Benefitdetail string `json:"benefitdetail"` // 优惠信息 + Randomstr string `json:"randomstr"` // 随机字符串 否 商户自行生成的随机字符串 + Signtype string `json:"signtype"` /// 签名方式 是 + Sign string `json:"sign"` // 签名 详见安全规范 否 +} +type OnLineOrderRefundResult struct { + Cusid string `json:"cusid"` // 商户号实际交易的商户号 否 + Appid string `json:"appid"` // 应用ID平台分配的APPID 否 + Trxid string `json:"trxid"` // 收银宝平台的交易流水号 + Reqsn string `json:"reqsn"` // 商户的交易订单号 + Trxstatus string `json:"trxstatus"` // 交易的状态, + Fintime string `json:"fintime"` // yyyyMMddHHmmss + Errmsg string `json:"errmsg"` // 失败的原因说明 + Fee string `json:"fee"` // 手续费 + Trxcode string `json:"trxcode"` // 交易类型 + Randomstr string `json:"randomstr"` // 随机生成的字符串 + Chnltrxid string `json:"chnltrxid"` // 例如微信,支付宝平台的交易单号 + Chnldata string `json:"chnldata"` // 渠道信息 + Bankcode string `json:"bankcode"` // 所属银行 + Sign string `json:"sign"` // 签名 +} diff --git a/platformapi/tonglianpayapi/tonglian_online_pay.go b/platformapi/tonglianpayapi/tonglian_online_pay.go new file mode 100644 index 00000000..729f0bd8 --- /dev/null +++ b/platformapi/tonglianpayapi/tonglian_online_pay.go @@ -0,0 +1,194 @@ +package tonglianpayapi + +import ( + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "fmt" + "git.rosy.net.cn/baseapi/platformapi" + "git.rosy.net.cn/baseapi/utils" + "net/http" + "sort" + "strings" +) + +const ( + //RSA私钥(非JAVA适用) + RSAPrivateKeyNotJava = `MIIEpAIBAAKCAQEAx9qhvqwyZ5hUKoxErxjgOgpjipbmJr20tGCtd/7UXCksVE9S/JWF6+LuT9jKAQpDDU4Dniu5mpwp6vfz2S7D51bO+S2mmSyiV6//NHpWZJwP9Lsb2fbU0tJUNiXGx3P79sA7ZDi5hkho59CX+tOr/gVDKXTb4Rbyj50+N+Rxt3i/Kd4Sf8AMRodP4SxcOjS4pbmwLFcD5H35czvXSRBQkHAptJ6keiYJLmi2ylsLWnBgf0BZYp04i5EVxiUT389ji7WNOUpNedb3wjOio877VatNm4XIZx0BFZVF8ZFEzEJkVXRAxRlR71nl1uKpNPVqfPQZ8pY3RrUmCxOwnJl5vQIDAQABAoIBAB3U4jjabkmtYL7bIjN6xJmVTGd2/9K/lXYpSitzs9Iv6SiKkKoYTZ24yXbMttZx6DFXuE2HXFSaQ92JdnIwO1jQSePC7y/FDFSHdlIogrXQ38bZmR4vbHQtphlRCTtjcjRSXGso1nSXYWVc6xqrNuybb3uEMIAIU1uhjpR8Ooc2sMfmpNdqCS5SKngIsRj5FQZzBPUoBiAlKiYck/UJzUgdVyxz5hpaFqB2V6VciD3E/PSa6OtSTPVO7a1KUD1rmvADZTmAYSqYwRBqZxkb42MNDtOW8YqRFC/rTlSQOo76XvT0AGSY2ZvFC89LrIZc9hF76k8OfCi8XjrwrMKgbpcCgYEA6/WiwthWy41Ch9R9xCubkPYfulNjq9CKbYyWnmmARnnhM5iRYJidqyfw1IbSVVm9Qy3o3VBnYocOTRJUTmOmpQgWqI4t/HjCZiFZeKF30F9ytl1urCbkXYWA2lygf2ZUYiCt3/CNC0bct4nZa42sGu3TXYZKcpVqlZT6tWywu8sCgYEA2NP4Zz5zJGzskhVdsEJbL7ttv/yX7X/g6AW3IhcI108bWfoA+i6YjbDsC8aftgIHPheaW/6n+oirDwoYdgrWa2/eCIKBo5uchtDmUb8qIXog1RbryKqOevLDkGRlJ8NAOIZfGwjT3pfhbtM+wvH5PCFBywoMlxz2sWZ2x4Vzf5cCgYEAza8Jeh9rSXSRkiXAm8gHi42AisM2FwdqI6RxhxUgJE8J6BgOYc2nYxMl85yyrIPVX0Idww3bkR95b+WSZ+Kl2SX72mJV48bAbpaTj3vxHUqWjDFVz+r3Fi7R64biwStKU195McRWroXO0I7xX7fXVoIJxXTSYJ+ukUWUZaGhTZECgYEAuHv6IVYYP8jRrCWztjFvRniRk8VGOxQP9zpNrBqvMgqjufWl+TfGIuCCpi5UW1b0dJc+hcFxiQ/Zg41SbLUh5P2ki9cGmH7hOi/pl2owXZV88/FxoiXD3sZJMMTK8H8HWFC0ANuM8RqG+3WPM+0P42JkiW2+cqB5IU2OCIr6T3cCgYBSLkui8zbMJB0TfE+KGxdm3e+sX26ZE2ZqQmTfJoRcxGgkWC7oY8HdIwws1uFeO38C5SMkkT8YlJghAjIIsfIUuCWQc5tBwvcb5f78/iJRUVgHXl3dH+9HvJKh/I4NeTIX6NTlaOrkUjgnkLLWbU7+b+3SCsVnvKH7uEm5cH0Ejg==` + //RSA公钥 + RSAPublicKey = `MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx9qhvqwyZ5hUKoxErxjgOgpjipbmJr20tGCtd/7UXCksVE9S/JWF6+LuT9jKAQpDDU4Dniu5mpwp6vfz2S7D51bO+S2mmSyiV6//NHpWZJwP9Lsb2fbU0tJUNiXGx3P79sA7ZDi5hkho59CX+tOr/gVDKXTb4Rbyj50+N+Rxt3i/Kd4Sf8AMRodP4SxcOjS4pbmwLFcD5H35czvXSRBQkHAptJ6keiYJLmi2ylsLWnBgf0BZYp04i5EVxiUT389ji7WNOUpNedb3wjOio877VatNm4XIZx0BFZVF8ZFEzEJkVXRAxRlR71nl1uKpNPVqfPQZ8pY3RrUmCxOwnJl5vQIDAQAB` + + //RSA私钥(JAVA适用) + RSAPrivateKeyJava = `MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDH2qG+rDJnmFQqjESvGOA6CmOKluYmvbS0YK13/tRcKSxUT1L8lYXr4u5P2MoBCkMNTgOeK7manCnq9/PZLsPnVs75LaaZLKJXr/80elZknA/0uxvZ9tTS0lQ2JcbHc/v2wDtkOLmGSGjn0Jf606v+BUMpdNvhFvKPnT435HG3eL8p3hJ/wAxGh0/hLFw6NLilubAsVwPkfflzO9dJEFCQcCm0nqR6JgkuaLbKWwtacGB/QFlinTiLkRXGJRPfz2OLtY05Sk151vfCM6KjzvtVq02bhchnHQEVlUXxkUTMQmRVdEDFGVHvWeXW4qk09Wp89BnyljdGtSYLE7CcmXm9AgMBAAECggEAHdTiONpuSa1gvtsiM3rEmZVMZ3b/0r+VdilKK3Oz0i/pKIqQqhhNnbjJdsy21nHoMVe4TYdcVJpD3Yl2cjA7WNBJ48LvL8UMVId2UiiCtdDfxtmZHi9sdC2mGVEJO2NyNFJcayjWdJdhZVzrGqs27Jtve4QwgAhTW6GOlHw6hzawx+ak12oJLlIqeAixGPkVBnME9SgGICUqJhyT9QnNSB1XLHPmGloWoHZXpVyIPcT89Jro61JM9U7trUpQPWua8ANlOYBhKpjBEGpnGRvjYw0O05bxipEUL+tOVJA6jvpe9PQAZJjZm8ULz0ushlz2EXvqTw58KLxeOvCswqBulwKBgQDr9aLC2FbLjUKH1H3EK5uQ9h+6U2Or0IptjJaeaYBGeeEzmJFgmJ2rJ/DUhtJVWb1DLejdUGdihw5NElROY6alCBaoji38eMJmIVl4oXfQX3K2XW6sJuRdhYDaXKB/ZlRiIK3f8I0LRty3idlrjawa7dNdhkpylWqVlPq1bLC7ywKBgQDY0/hnPnMkbOySFV2wQlsvu22 //Jftf+DoBbciFwjXTxtZ+gD6LpiNsOwLxp+2Agc+F5pb/qf6iKsPChh2CtZrb94IgoGjm5yG0OZRvyoheiDVFuvIqo568sOQZGUnw0A4hl8bCNPel+Fu0z7C8fk8IUHLCgyXHPaxZnbHhXN/lwKBgQDNrwl6H2tJdJGSJcCbyAeLjYCKwzYXB2ojpHGHFSAkTwnoGA5hzadjEyXznLKsg9VfQh3DDduRH3lv5ZJn4qXZJfvaYlXjxsBulpOPe/EdSpaMMVXP6vcWLtHrhuLBK0pTX3kxxFauhc7QjvFft9dWggnFdNJgn66RRZRloaFNkQKBgQC4e/ohVhg/yNGsJbO2MW9GeJGTxUY7FA/3Ok2sGq8yCqO59aX5N8Yi4IKmLlRbVvR0lz6FwXGJD9mDjVJstSHk/aSL1waYfuE6L+mXajBdlXzz8XGiJcPexkkwxMrwfwdYULQA24zxGob7dY8z7Q/jYmSJbb5yoHkhTY4IivpPdwKBgFIuS6LzNswkHRN8T4obF2bd76xfbpkTZmpCZN8mhFzEaCRYLuhjwd0jDCzW4V47fwLlIySRPxiUmCECMgix8hS4JZBzm0HC9xvl/vz+IlFRWAdeXd0f70e8kqH8jg15Mhfo1OVo6uRSOCeQstZtTv5v7dIKxWe8ofu4SblwfQSO` + //SM2公钥 + SM2PublicKey = `MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE7yCR6yp3jPOgBTqWqE9D3ugudcsi4epjq3EJ6mq3Y0nrR489Pld7V5yk+QuH2SUZMFmu46/adlnN/+cd496lkg==` + //SM2私钥 + SM2PrivateKey = `MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQg1Rj + skv1B3dGwe2wGy9OF6N4vqxAGM44MR6e2kWTxhegCgYIKoEcz1UBgi2hRANCAATvIJHrKneM86AFOpaoT0Pe6C51yyLh6mOrcQnqardjSetHjz0 + V3tXnKT5C4fZJRkwWa7jr9p2Wc3/5x3j3qWS` + // 测试私钥 + cusRsaPrivateTestKey = `MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJgHMGYsspghvP+yCbjLG43CkZuQ3YJyDcmEKxvmgblITfmiTPx2b9Y2iwDT9gnLGExTDm1BL2A8VzMobjaHfiCmTbDctu680MLmpDDkVXmJOqdlXh0tcLjhN4+iDA2KkRqiHxsDpiaKT6MMBuecXQbJtPlVc1XjVhoUlzUgPCrvAgMBAAECgYAV9saYTGbfsdLOF5kYo0dve1JxaO7dFMCcgkV+z2ujKtNmeHtU54DlhZXJiytQY5Dhc10cjb6xfFDrftuFcfKCaLiy6h5ETR8jyv5He6KH/+X6qkcGTkJBYG1XvyyFO3PxoszQAs0mrLCqq0UItlCDn0G72MR9/NuvdYabGHSzEQJBAMXB1/DUvBTHHH4LiKDiaREruBb3QtP72JQS1ATVXA2v6xJzGPMWMBGQDvRfPvuCPVmbHENX+lRxMLp39OvIn6kCQQDEzYpPcuHW/7h3TYHYc+T0O6z1VKQT2Mxv92Lj35g1XqV4Oi9xrTj2DtMeV1lMx6n/3icobkCQtuvTI+AcqfTXAkB6bCz9NwUUK8sUsJktV9xJN/JnrTxetOr3h8xfDaJGCuCQdFY+rj6lsLPBTnFUC+Vk4mQVwJIE0mmjFf22NWW5AkAmsVaRGkAmui41Xoq52MdZ8WWm8lY0BLrlBJlvveU6EPqtcZskWW9KiU2euIO5IcRdpvrB6zNMgHpLD9GfMRcPAkBUWOV/dH13v8V2Y/Fzuag/y5k3/oXi/WQnIxdYbltad2xjmofJ7DbB7MJqiZZD8jlr8PCZPwRNzc5ntDStc959` + + // 回调地址 + OnlinePayCallbackUrl = "https://callback.jxc4.com/tongLian/onLinePay" + // 接口地址 + OnlinePayUrl = `https://vsp.allinpay.com/apiweb/unitorder/scanqrpay` // 扫码支付 + OnLineTransactionQuery = `https://vsp.allinpay.com/apiweb/tranx/query` // 交易查询,查询订单的支付状态 + OnLineRefund = `https://vsp.allinpay.com/apiweb/tranx/refund` // 同一退款接口 +) + +const ( + EncryptionRsa = "RSA" // 加密方式 +) + +var ( + TransactionStatus = map[string]string{ + "0000": "交易成功", + "1001": "交易不存在", + "2008": "交易处理中,请查询交易,如果是实时交易(例如刷卡支付,交易撤销,退货),建议每隔一段时间(10秒)查询交易", + "2000": "交易处理中,请查询交易,如果是实时交易(例如刷卡支付,交易撤销,退货),建议每隔一段时间(10秒)查询交易", + "3888": "流水号重复", + "3099": "渠道商户错误", + "3014": "交易金额小于应收手续费", + "3031": "校验实名信息失败", + "3088": "交易未支付", + "3089": "撤销异常,如已影响资金24小时内会做差错退款处理", + "3050": "交易已被撤销", + "3999": "1", + "3889": "1", + "3045": "1", + } +) + +// CreateOnlineBankOrder 网银创建订单(扫码枪,扫二维码支付) +func (a *API) CreateOnlineBankOrder(param *OnlinePayParam) (statusCode, trxid, errRemake string, err error) { + onlineReq := utils.Struct2Map(param, "", false) + result, err := a.AccessAPI2(OnlinePayUrl, onlineReq) + if err != nil { + return "", "", "", err + } + // 请求接口成功后通联系统返回JSON字符串数据,把除sign之外的其他所有非空字段组成键值对,进行同样的排序和组装处理,并用通联的公钥进行验签 + // 暂时不考虑验签 + + if result["retcode"].(string) != "SUCCESS" { + return "", "", "", fmt.Errorf(result["retmsg"].(string)) + } + + payInfo := &OnlinePayRetMsg{} + if err = utils.Map2StructByJson(result, payInfo, true); err != nil { + return "", "", "", err + } + + return payInfo.Trxstatus, payInfo.Trxid, payInfo.Errmsg, nil +} + +// QueryPayTransaction 订单交易状态查询 +func (a *API) QueryPayTransaction(param *PayTransactionStatusQuery) (statusCode, errRemake string, err error) { + onlineReq := utils.Struct2Map(param, "", false) + result, err := a.AccessAPI2(OnLineTransactionQuery, onlineReq) + if err != nil { + return "", "", err + } + if result["retcode"].(string) != "SUCCESS" { + return "", "", fmt.Errorf(result["retmsg"].(string)) + } + + payInfo := &PayTransactionStatusResult{} + if err = utils.Map2StructByJson(result, payInfo, true); err != nil { + return "", "", err + } + + return payInfo.Trxstatus, payInfo.Errmsg, nil +} + +// OrderRefund 退单 +func (a *API) OrderRefund(param *OnLineOrderRefundParam) (statusCode, errRemake string, err error) { + onlineReq := utils.Struct2Map(param, "", false) + result, err := a.AccessAPI2(OnLineRefund, onlineReq) + if err != nil { + return "", "", err + } + if result["retcode"].(string) != "SUCCESS" { + return "", "", fmt.Errorf(result["retmsg"].(string)) + } + + payInfo := &OnLineOrderRefundResult{} + if err = utils.Map2StructByJson(result, payInfo, true); err != nil { + return "", "", err + } + + return payInfo.Trxstatus, payInfo.Errmsg, nil +} + +func (a *API) AccessAPI2(action string, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) { + params := make(map[string]interface{}) + params["appid"] = a.appID + params["cusid"] = a.cusID + params["randomstr"] = utils.GetUUID() + params["version"] = "12" + params = utils.MergeMaps(params, bizParams) + sign, err := a.signParamRSA(params, RSAPrivateKeyNotJava) + if err != nil { + return nil, err + } + params["sign"] = sign + + err = platformapi.AccessPlatformAPIWithRetry(a.client, + func() *http.Request { + request, _ := http.NewRequest(http.MethodPost, action, strings.NewReader(utils.Map2URLValues(params).Encode())) + request.Header.Set("charset", "UTF-8") + request.Header.Set("Content-Type", "application/x-www-form-urlencoded") + return request + }, + a.config, + func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) { + if jsonResult1 == nil { + return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil") + } + if err == nil { + returnCode := utils.Interface2String(jsonResult1["retcode"]) + if returnCode != OnlinePayStatusSuccess { + errLevel = platformapi.ErrLevelGeneralFail + err = utils.NewErrorCode(utils.Interface2String(jsonResult1["retcode"])+utils.Interface2String(jsonResult1["retmsg"]), returnCode) + } + retVal = jsonResult1 + } + return errLevel, err + }) + return retVal, err +} + +func (a *API) signParamRSA(params map[string]interface{}, RSAPrivate string) (sig string, err error) { + decodeString, err := base64.StdEncoding.DecodeString(RSAPrivate) + if err != nil { + return "", err + } + + private, err := x509.ParsePKCS1PrivateKey(decodeString) //之前看java demo中使用的是pkcs8 + if err != nil { + return "", err + } + + var valueList []string + for k, v := range params { + if k != sigKey { + if str := fmt.Sprint(v); str != "" { + valueList = append(valueList, fmt.Sprintf("%s=%s", k, str)) + } + } + } + sort.Sort(sort.StringSlice(valueList)) + context := strings.Join(valueList, "&") + h := crypto.Hash.New(crypto.SHA1) //进行SHA1的散列 + h.Write([]byte(context)) + hashed := h.Sum(nil) + + // 进行rsa加密签名 + signedData, err := rsa.SignPKCS1v15(rand.Reader, private, crypto.SHA1, hashed) + if err != nil { + return "", err + } + + signData := base64.StdEncoding.EncodeToString(signedData) + return signData, nil +} diff --git a/platformapi/tonglianpayapi/tonglian_online_pay_test.go b/platformapi/tonglianpayapi/tonglian_online_pay_test.go new file mode 100644 index 00000000..4482d7bf --- /dev/null +++ b/platformapi/tonglianpayapi/tonglian_online_pay_test.go @@ -0,0 +1,101 @@ +package tonglianpayapi + +import ( + "encoding/json" + "fmt" + "git.rosy.net.cn/baseapi/utils" + "testing" +) + +func TestSignRSA(t *testing.T) { + sginParma := map[string]interface{}{ + "appid": "00000051", + "cusid": "990581007426001", + "randomstr": "82712208", + "signtype": "RSA", + "trxid": "112094120001088317", + "version": "11", + } + sgin, _ := api.signParamRSA(sginParma, cusRsaPrivateTestKey) + fmt.Println(sgin) + data2 := `ce6EAOj4rhoBMJM5MJCNG4qQ/CVMTWkoRuSGpSzRAnD3U3V5QyHkQUEej2eZXRaa+qSbw2/IJJSPV0sPuAia1+ccb7OnvxyZqkV9wQyimX6qAMz0K+UWFhQ5McCcQ/XsFhhezoVd5QgL7PtdvuK1AtjuzA3J9yzNmwuPssPnKnc=` + fmt.Println(sgin == data2) +} + +func TestOnlinePayData(t *testing.T) { + param := &OnlinePayParam{ + Orgid: "", + Cusid: "", // 不用 + Appid: "", // 不用 + Version: "", // 不用 + Randomstr: "", // 不用 + Trxamt: "1", + Reqsn: "202504221811", + Body: "测试订单", + Remark: "这是一个测试", + Authcode: "133040799834193135", // 支付授权码 + LimitPay: "", + GoodsTag: "", + Benefitdetail: "", + SubAppid: "", + Chnlstoreid: "666667", + Subbranch: "", + Idno: "", + Extendparams: "", + Truename: "", + Asinfo: "", + Fqnum: "", + NotifyUrl: "https://www.callback.com/tonglian/onlinePay", + Signtype: "RSA", + Unpid: "", + Sign: "", + Terminfo: "", + Operatorid: "", + } + terminfo := &TerminfoBase{ + Termno: "N502F01147", + Devicetype: "10", + Termsn: "N502F01147", + Encryptrandnum: "", + Secrettext: "", + Appversion: "", + Longitude: "+121.48352", + Latitude: "-03.561345", + Deviceip: "", + } + terminfoBByte, _ := json.Marshal(terminfo) + param.Terminfo = string(terminfoBByte) + api.CreateOnlineBankOrder(param) +} + +func TestRefreshPayStatus(t *testing.T) { + param := &PayTransactionStatusQuery{ + //Orgid: "", + //Cusid: "", + //Appid: "", + //Version: "", + Reqsn: "202504221811", + Trxid: "", + Randomstr: utils.GetUUID(), + Signtype: "RSA", + Sign: "", + } + api.QueryPayTransaction(param) +} + +func TestOrderRefund(t *testing.T) { + param := &OnLineOrderRefundParam{ + Trxamt: utils.Int2Str(1), + Reqsn: "202504250924", + Oldreqsn: "202504221811", + Oldtrxid: "", + Remark: "测试退款", + Benefitdetail: "", + Randomstr: utils.GetUUID(), + Signtype: EncryptionRsa, + Sign: "", + } + + _, _, err := api.OrderRefund(param) + fmt.Println(err) +} diff --git a/platformapi/tonglianpayapi/tonglianpayapi.go b/platformapi/tonglianpayapi/tonglianpayapi.go index 6d194fcc..8c62ba0e 100644 --- a/platformapi/tonglianpayapi/tonglianpayapi.go +++ b/platformapi/tonglianpayapi/tonglianpayapi.go @@ -1,148 +1,14 @@ package tonglianpayapi import ( - "crypto/md5" "fmt" "net/http" - "sort" "strings" "git.rosy.net.cn/baseapi/platformapi" "git.rosy.net.cn/baseapi/utils" ) -const ( - prodURL = "https://vsp.allinpay.com/apiweb" - prodURL2 = "https://syb.allinpay.com/apiweb" - sepcAction = "unitorder/pay" - sepcAction2 = "h5unionpay/unionorder" - - sigKey = "sign" - PayTypeWxXcx = "W06" - PayTypeWxCode = "W01" - PayTypeZfbApp = "A03" - PayTypeZfbQrcode = "A01" - PayTypeZfbJS = "A02" - PayTypeH5 = "H5" - - ResponseCodeSuccess = "SUCCESS" - ResponseCodeFail = "FAIL" -) - -type API struct { - appID string - cusID string - appKey string - - client *http.Client - config *platformapi.APIConfig -} - -type CreateUnitorderOrderParam struct { - CusID string `json:"cusid"` - AppID string `json:"appid"` - Trxamt int `json:"trxamt"` //交易金额 单位为分 - Reqsn string `json:"reqsn"` //商户交易单号 - NotifyUrl string `json:"notifyUrl"` //接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 - Acct string `json:"acct"` - PayType string `json:"paytype"` - SubAppID string `json:"subAppID"` -} - -type CreateUnitOrderOrderResult struct { - RetCode string `json:"retCode"` - RetMsg string `json:"retMsg"` - CusID string `json:"cusID"` - AppID string `json:"appID"` - TrxID string `json:"trxID"` - ChnltrxID string `json:"chnltrxID"` - Reqsn string `json:"reqsn"` - RandomStr string `json:"randomStr"` - TrxStatus string `json:"trxStatus"` - FinTime string `json:"finTime"` - ErrMsg string `json:"errMsg"` - PayInfo string `json:"payInfo"` - Sign string `json:"sign"` -} - -type PayInfo struct { - AppID string `json:"appID"` - TimeStamp string `json:"timeStamp"` - NonceStr string `json:"nonceStr"` - Package string `json:"package"` - SignType string `json:"signType"` - PaySign string `json:"paySign"` -} - -type PayRefundParam struct { - CusID string `json:"cusid"` - AppID string `json:"appid"` - Trxamt int `json:"trxamt"` //交易金额 单位为分 - Reqsn string `json:"reqsn"` //商户交易单号 - OldReqsn string `json:"oldReqsn"` - Remark string `json:"remark"` - RandomStr string `json:"randomStr"` - Sign string `json:"sign"` - TrxID string `json:"trxID"` - OldTrxID string `json:"oldTrxID"` -} - -type PayRefundResult struct { - RetCode string `json:"retCode"` - RetMsg string `json:"retMsg"` - CusID string `json:"cusID"` - AppID string `json:"appID"` - TrxID string `json:"trxID"` - Reqsn string `json:"reqsn"` - TrxStatus string `json:"trxStatus"` - FinTime string `json:"finTime"` - ErrMsg string `json:"errMsg"` - RandomStr string `json:"randomStr"` - Sign string `json:"sign"` - Fee string `json:"fee"` - TrxCode string `json:"trxCode"` -} - -type CreateH5UnitorderOrderParam struct { - Trxamt int `json:"trxamt"` //交易金额 单位为分 - Reqsn string `json:"reqsn"` //商户交易单号 - NotifyUrl string `json:"notifyUrl"` //接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。 - Charset string `json:"charset"` - Returl string `json:"returl"` - Body string `json:"body"` -} - -func New(appID, appKey, cusID string, config ...*platformapi.APIConfig) *API { - curConfig := platformapi.DefAPIConfig - if len(config) > 0 { - curConfig = *config[0] - } - return &API{ - appID: appID, - appKey: appKey, - cusID: cusID, - client: &http.Client{Timeout: curConfig.ClientTimeout}, - config: &curConfig, - } -} - -func (a *API) signParam(params map[string]interface{}) (sig string) { - var valueList []string - for k, v := range params { - if k != sigKey { - if str := fmt.Sprint(v); str != "" { - valueList = append(valueList, fmt.Sprintf("%s=%s", k, str)) - } - } - } - valueList = append(valueList, fmt.Sprintf("key=%s", a.appKey)) - sort.Sort(sort.StringSlice(valueList)) - sig = strings.Join(valueList, "&") - binSig := md5.Sum([]byte(sig)) - sig = fmt.Sprintf("%X", binSig) - return sig -} - func (a *API) AccessAPI(action string, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) { params := make(map[string]interface{}) params["appid"] = a.appID diff --git a/platformapi/tonglianpayapi/tonglianpayapi_test.go b/platformapi/tonglianpayapi/tonglianpayapi_test.go index 3bedf7b7..1ab8a5ea 100644 --- a/platformapi/tonglianpayapi/tonglianpayapi_test.go +++ b/platformapi/tonglianpayapi/tonglianpayapi_test.go @@ -58,3 +58,51 @@ func TestPayRefund(t *testing.T) { OldTrxID: "122094350000087981", }) } + +// +//func TestMisPay(t *testing.T) { +// +// custData := &CustDataDetail{ +// BusinessId: BusinessTypeBUSIORDERTRANSQR, +// Amount: "1", +// OrderNo: "202504171111", +// Memo: "", +// QrCode: "", +// TramsCheck: "", +// PageAppendContent: "", +// ValidateInfo: "", +// BusInfo: "", +// } +// param := &OnMisPayReq{ +// AppId: api.appID, +// BusinessId: BusinessTypeBUSIORDERTRANSQR, +// CustData: "000000000002", +// CashId: "n502f01147", +// StoreId: "66666", +// SignData: "", +// AppPackageNm: "", +// AppClassNm: "", +// BusExtpara: "", +// } +// custByte, _ := json.Marshal(custData) +// param.CustData = string(custByte) +// +// api.CreateMisPayOrder(param, custData) +//} +// +//func TestSignMd5(t *testing.T) { +// custData := &CustDataDetail{ +// BusinessId: "100000003", +// Amount: "000000000002", +// OrderNo: "123123213", +// Memo: "", +// QrCode: "", +// TramsCheck: "", +// PageAppendContent: "", +// ValidateInfo: "", +// BusInfo: "", +// } +// licence := `9a432306-b93d-4d0d-aa7d-ac2bb89dee25-20200208002855` +// sig := api.signMisParam("100000003", licence, custData) +// fmt.Println(sig) +//} diff --git a/platformapi/tonglianpayapi/yun_mis_pay.go b/platformapi/tonglianpayapi/yun_mis_pay.go new file mode 100644 index 00000000..046de24e --- /dev/null +++ b/platformapi/tonglianpayapi/yun_mis_pay.go @@ -0,0 +1,149 @@ +package tonglianpayapi + +// +//import ( +// "crypto/md5" +// "fmt" +// "git.rosy.net.cn/baseapi/platformapi" +// "git.rosy.net.cn/baseapi/utils" +// "net/http" +// "strings" +//) +// +//const ( +// TongLianMisPay = "https://cloudmiscash.allinpay.com/cloudMis-transaction" // 通联mis支付(基础收款用于一般无特殊支付业务需求的标准行业,如零售、餐饮类商户及场景) +// MisPayAction = "api/v2/process" +//) +// +//const ( +// BusinessTypeBUSIORDERTRANS = "100000003" // 订单支付收款(不含查询流程) +// BusinessTypeBUSIORDERTRANSBANK = "100100003" // 订单支付银行卡收款(不含查询) +// BusinessTypeBUSIORDERTRANSQR = "100300003" // 订单支付扫码收款(不含查询) +// BusinessTypeBUSIORDERSALE = "100000002" // 订单支付收款(含查询流程) +// BusinessTypeBUSIORDERSALEBANK = "100100002" // 订单支付银行卡收款(含查询流程) +// BusinessTypeBUSIORDERSALEQR = "100300002" // 订单支付扫码收款(含查询流程) +//) +// +//func (a *API) signMisParam(businessID, licence string, custData *CustDataDetail) (sig string) { +// var signBuffer = new(strings.Builder) +// //signBuffer.Write([]byte(a.appID)) +// signBuffer.Write([]byte("app_id_001")) +// signBuffer.Write([]byte(businessID)) +// signBuffer.Write([]byte(fmt.Sprintf(`{"BUSINESS_ID":"%s","AMOUNT":"%s","ORDER_NO":"%s"}`, custData.BusinessId, custData.Amount, custData.OrderNo))) +// signBuffer.Write([]byte(licence)) +// fmt.Println(signBuffer.String()) +// binSig := md5.Sum([]byte(signBuffer.String())) +// sig = fmt.Sprintf("%x", binSig) +// return sig +//} +// +//func (a *API) AccessMisAPI(action string, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) { +// //params := make(map[string]interface{}) +// //params["APP_ID"] = a.appID +// ////params["randomstr"] = utils.GetUUID() +// ////params["version"] = "12" +// //params = utils.MergeMaps(params, bizParams) +// //signStr := a.signParam(params) +// //params["SIGN_DATA"] = signStr +// fullURL := utils.GenerateGetURL(TongLianMisPay, action, nil) +// err = platformapi.AccessPlatformAPIWithRetry(a.client, +// func() *http.Request { +// request, _ := http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(bizParams).Encode())) +// request.Header.Set("charset", "UTF-8") +// request.Header.Set("Content-Type", "application/x-www-form-urlencoded") +// return request +// }, +// a.config, +// func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) { +// if jsonResult1 == nil { +// return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil") +// } +// if err == nil { +// returnCode := utils.Interface2String(jsonResult1["RSP_CODE"]) +// if returnCode != MisStatusSuccess { +// errLevel = platformapi.ErrLevelGeneralFail +// err = utils.NewErrorCode(utils.Interface2String(jsonResult1["errmsg"])+utils.Interface2String(jsonResult1["retmsg"]), returnCode) +// } +// retVal = jsonResult1 +// } +// return errLevel, err +// }) +// return retVal, err +//} +// +//// CreateMisPayOrder 创建订单(扫码枪,扫二维码支付) +//func (a *API) CreateMisPayOrder(param *OnMisPayReq, custData *CustDataDetail) (err error) { +// param.AppId = a.appID +// param.SignData = a.signMisParam(param.BusinessId, "", custData) +// params := utils.Struct2MapByJson(param) +// result, err := a.AccessMisAPI(MisPayAction, params) +// fmt.Println(result) +// return +//} +// +//type OnMisPayReq struct { +// AppId string `json:"APP_ID"` // 必填 应用分配ID +// BusinessId string `json:"BUSINESS_ID"` // 必填 业务类型 +// CustData string `json:"CUST_DATA"` // 必填 具体的支付请求数据,JSON格式字符串 +// CashId string `json:"CASH_ID"` // 必填 收银机款台号 +// StoreId string `json:"STORE_ID"` // 必填 商户分店号 +// SignData string `json:"SIGN_DATA"` // 必填 计算方法参照附录1 +// AppPackageNm string `json:"APP_PACKAGE_NM"` // 非必填 指定APP包名 +// AppClassNm string `json:"APP_CLASS_NM"` // 非必填 指定类名 +// BusExtpara string `json:"BUS_EXTPARA"` // 非必填 业务扩展字段,用于特定功能,详见附录2 +//} +//type CustDataDetail struct { +// BusinessId string `json:"BUSINESS_ID"` // 必填 业务类型 +// Amount string `json:"AMOUNT"` // 必填 支付金额,十二位长度(单位:分,不足左补零) +// OrderNo string `json:"ORDER_NO"` // 必填 订单编号 +// Memo string `json:"MEMO"` // 非必填 订单编号 +// QrCode string `json:"QRCODE"` // 非必填 订单编号 +// TramsCheck string `json:"TRANS_CHECK"` // 非必填 订单编号 +// PageAppendContent string `json:"PAGE_APPEND_CONTENT"` // 非必填 订单编号 +// ValidateInfo string `json:"VALIDATE_INFO"` // 非必填 订单编号 +// BusInfo string `json:"BUS_INFO"` // 非必填 订单编号 +//} +// +//type MisPayResp struct { +// RspCode string `json:"RSP_CODE"` // 云MIS系统服务调用返回码, 支付返回代码请参考RSP_DATA里支付结果字段 +// RspData string `json:"RSP_DATA"` // 透传支付产品给云MIS的响应JSON字符串 +// RspDesc string `json:"RSP_DESC"` // 交易级返回描述 +// CloudMisTrxSsn string `json:"ClOUD_MIS_TRX_SSN"` // 交易成功返回,云MIS系统唯一流水号,用于查询或对账 +//} +// +//type MisPayRspData struct { +// BusinessId string `json:"BUSINESS_ID"` // 必填 业务类型 +// RejCode string `json:"REJCODE"` // 交易返回码 +// RejCodeCn string `json:"REJCODE_CN"` // 交易返回码注释 +// Cups string `json:"CUPS"` // 卡组织,银行卡为空 (ALP:支付宝 WXP:微信支付) +// MerchID string `json:"MERCH_ID"` // 商户编号 +// TerID string `json:"TER_ID"` // 终端编号 +// MerchName string `json:"MERCH_NAME"` // 商户名称 +// BatchNo string `json:"BATCH_NO"` // 批次号 +// TraceNo string `json:"TRACE_NO"` // 凭证号 +// TransTicketNo string `json:"TRANS_TICKET_NO"` // 交易单号 +// OrderNo string `json:"order_no"` // 订单编号 +// Amount string `json:"AMOUNT"` // 支付金额,十二位长度(单位:分) +// RefNo string `json:"REF_NO"` // 系统参考号 +// CarDno string `json:"CARDNO"` // 银行卡交易时返回卡号;扫码交易返回OPENID或USERID +// CardTypeIdenty string `json:"CARD_TYPE_IDENTY"` // 借贷记卡标识(银行卡) 0:借记 1:贷记 +// WildCardSign string `json:"WILD_CARD_SIGN"` //内外卡标识(银行卡) 0:内卡 1:外卡 +// ExpDate string `json:"EXP_DATE"` // 有效期 +// Date string `json:"DATE"` // 支付日期 +// Time string `json:"TIME"` // 支付时间 +// PrintFlag string `json:"PRINT_FLAG"` // 是否打印小票(1:打印,0:未打印) +// CardFee string `json:"CARD_FEE"` // 手续费,十二位长度(单位:分) +// TransCheck string `json:"TRANS_CHECK"` // 交易唯一标识 +// AuthNo string `json:"AUTH_NO"` // 授权码 +// Sign string `json:"SIGN"` // 签名 +// CardType string `json:"CARDTYPE"` // 卡类型 +// TransChannel string `json:"TRANS_CHANNEL"` // 交易渠道 +// IssNo string `json:"ISS_NO"` // 发卡行行号 +// IssName string `json:"ISS_NAME"` // 发卡行名称 +// Memo string `json:"MEMO"` // 备注信息 +// PrtCommon string `json:"PRT_COMMON"` // 打印内容公共部分(仅用于简易终端版) +// PrtCardholder string `json:"PRT_CARDHOLDER"` // 打印内容持卡人联部分(仅用于简易终端版) +// PrtMerchant string `json:"PRT_MERCHANT"` // 打印内容商户联部分(仅用于简易终端版) +// DiscAmount string `json:"DISC_AMOUNT"` // 优惠金额 +// ActuallyAmount string `json:"ACTUALLY_AMOUNT"` // 实付金额 +//}