package pay import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/x509" "database/sql" "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" "io/ioutil" rand2 "math/rand" "strconv" "strings" "system_pay/models" "system_pay/mysql" "system_pay/setting" "time" "github.com/astaxie/beego/httplib" uID "github.com/satori/go.uuid" ) // 拉卡拉支付 func UnifiedOrder(input *models.PlaceAnOrderParamInput, ip string) (interface{}, error) { //输入项check if input.SourceCode == 0 { return nil, errors.New("输入项「source_code」为空错误") } if input.PlatformType == 0 { return nil, errors.New("输入项「platform_type」为空错误") } fmt.Println("谛宝多多输入参数") fmt.Println(input) orderID := GetUID() attachMap := make(map[string]interface{}, 0) attachMap["store_sn"] = input.StoreSn attachMap["old_attach"] = input.AttachInfo attach, _ := json.Marshal(attachMap) input.AttachInfo = string(attach) // 插入数据库 db, err := mysql.NewPayConn() if err != nil { return 0, err } tx, err := db.Begin() if err != nil { return nil, err } defer mysql.CloseTx(tx, err) billID, err := InsertPayBill(tx, input, orderID) if err != nil { tx.Rollback() return nil, err } //数据重组 - start now := time.Now() now.Add(time.Minute * 60) date_time1 := now.Format("20060102150405") date_time2 := now.Add(time.Minute * 60).Format("20060102150405") var version, out_org_code, merchant_no1, merchant_no2, merchant_no3, term_no3, merchant_no4, term_no4 string //平台类型 1:saas 2:shop 3:shop mobile 4:收银台 6:bk_shop 7:bk_shop_mobile if input.PlatformType == 2 || input.PlatformType == 3 { // 谛宝多多自营 version = setting.Conf.Lakala.DbcVersion out_org_code = setting.Conf.Lakala.DbcAppid merchant_no1 = setting.Conf.Lakala.DbcMerchantNo1 merchant_no2 = setting.Conf.Lakala.DbcMerchantNo2 merchant_no3 = setting.Conf.Lakala.DbcMerchantNo3 term_no3 = setting.Conf.Lakala.DbcTermNo3 merchant_no4 = setting.Conf.Lakala.DbcMerchantNo4 term_no4 = setting.Conf.Lakala.DbcTermNo4 } else if input.PlatformType == 6 || input.PlatformType == 7 { // 必康自营 version = setting.Conf.Lakala.BkVersion out_org_code = setting.Conf.Lakala.BkAppid merchant_no1 = setting.Conf.Lakala.BkMerchantNo1 merchant_no2 = setting.Conf.Lakala.BkMerchantNo2 merchant_no3 = setting.Conf.Lakala.BkMerchantNo3 term_no3 = setting.Conf.Lakala.BkTermNo3 merchant_no4 = setting.Conf.Lakala.BkMerchantNo4 term_no4 = setting.Conf.Lakala.BkTermNo4 } else { // 必康医生 version = setting.Conf.Lakala.SaasVersion out_org_code = setting.Conf.Lakala.SaasAppid merchant_no1 = setting.Conf.Lakala.SaasMerchantNo1 merchant_no2 = setting.Conf.Lakala.SaasMerchantNo2 merchant_no3 = setting.Conf.Lakala.SaasMerchantNo3 term_no3 = setting.Conf.Lakala.SaasTermNo3 merchant_no4 = setting.Conf.Lakala.SaasMerchantNo4 term_no4 = setting.Conf.Lakala.SaasTermNo4 } var url string data := make(map[string]interface{}) //数据结构 data["req_time"] = date_time1 data["version"] = version data["out_org_code"] = out_org_code //source_code 1: 微信 Native 2:微信小程序 3:微信内支付 4:h5 跳微信 //5:支付宝(web)-扫码或登录支付宝账户 6:alipay(mobile) 7:alipay(app) //9: B2C 10:bk支付宝web 11:bk 支付宝手机 15:快捷支付(银行卡) //16:微信小程序-必康自营 17:微信JASPI-必康自营 18:支付宝 if input.SourceCode == 4 || input.SourceCode == 6 || input.SourceCode == 1 || input.SourceCode == 5 || input.SourceCode == 15 || input.SourceCode == 16 || input.SourceCode == 17 { //聚合收银台(微信H5、支付宝H5、微信扫码、支付宝扫码) //聚合收银台(快捷支付(银行卡)) //聚合收银台(微信小程序-必康自营) url = setting.Conf.Lakala.UrlCreate //聚合收银台 // 构造回调url input.NoticeURL = GetNoticeURL(input.SourceCode) data2 := make(map[string]interface{}) if input.SourceCode == 4 || input.SourceCode == 6 { data2["merchant_no"] = merchant_no1 //微信H5、支付宝H5 } else { data2["merchant_no"] = merchant_no2 //其它 } data2["total_amount"] = input.GoodsPrice * 100 data2["out_order_no"] = orderID //随机生成的订单号 //商户订单号 if input.SourceCode == 15 { //快捷支付 data2["counter_param"] = "{\"pay_mode\":\"QUICK_PAY\"}" if input.AuthCodes != "" { //LAKALA签约协议号列表 auth_codes := strings.Split(input.AuthCodes, ",") data2["sgn_info"] = auth_codes //签约协议号列表(字符串) } } else if input.SourceCode < 5 || input.SourceCode == 16 || input.SourceCode == 17 { //微信 data2["counter_param"] = "{\"pay_mode\":\"WECHAT\"}" } else { //支付宝 data2["counter_param"] = "{\"pay_mode\":\"ALIPAY\"}" } data2["order_efficient_time"] = date_time2 //订单有效期 格式yyyyMMddHHmmss,最大支持下单时间+2天 data2["notify_url"] = input.NoticeURL //订单支付成功后商户接收订单通知的地址 http://xxx.xxx.com data2["callback_url"] = input.ReturnURL //客户端下单完成支付后返回的商户网页跳转地址 data2["order_info"] = input.PlatformInfo //订单标题,在使用收银台扫码支付时必输入,交易时送往账户端 //data2["goods_mark"] = input.AttachInfo //商品信息标识 (1:含商品信息,不填默认不含商品信息) data2["support_refund"] = 1 //是否支持退款 默认0 不支持 data["req_data"] = make(map[string]interface{}) data["req_data"] = data2 } else if input.SourceCode == 2 || input.SourceCode == 3 || input.SourceCode == 18 { //聚合主扫(微信JSAPI、微信小程序、支付宝) url = setting.Conf.Lakala.UrlPreorder //聚合主扫 // 构造回调url input.NoticeURL = GetNoticeURL(input.SourceCode) data2 := make(map[string]interface{}) data2["merchant_no"] = merchant_no3 data2["term_no"] = term_no3 data2["total_amount"] = input.GoodsPrice * 100 data2["out_trade_no"] = orderID //随机生成的订单号 //商户交易流水号 //data2["order_efficient_time"] = date_time2 //订单有效期 格式yyyyMMddHHmmss,最大支持下单时间+2天 data2["notify_url"] = input.NoticeURL //订单支付成功后商户接收订单通知的地址 http://xxx.xxx.com //data2["callback_url"] = input.ReturnURL //客户端下单完成支付后返回的商户网页跳转地址 data2["subject"] = input.PlatformInfo //订单标题,在使用收银台扫码支付时必输入,交易时送往账户端 //data2["goods_mark"] = input.AttachInfo //商品信息标识 (1:含商品信息,不填默认不含商品信息) //微信JSAPI、微信小程序 if input.SourceCode == 2 { data2["trans_type"] = "71" //接入方式:微信小程序 } else if input.SourceCode == 18 { data2["trans_type"] = "41" //接入方式:ALIPAY } else { data2["trans_type"] = "51" //接入方式:微信JSAPI } //地址位置信息 data3 := make(map[string]interface{}) data3["request_ip"] = ip data2["location_info"] = make(map[string]interface{}) data2["location_info"] = data3 if input.SourceCode == 18 { data2["account_type"] = "ALIPAY" //钱包类型 } else { data2["account_type"] = "WECHAT" //钱包类型 //账户端业务信息域 data4 := make(map[string]interface{}) data4["sub_appid"] = input.AppID //子商户公众账号ID data4["user_id"] = input.OpenID //用户标识(sub_openid) data2["acc_busi_fields"] = make(map[string]interface{}) data2["acc_busi_fields"] = data4 } data["req_data"] = make(map[string]interface{}) data["req_data"] = data2 } else if input.SourceCode == 9 { //聚合被扫(扫码枪) if input.DynamicID == "" { tx.Rollback() return nil, errors.New("输入项「dynamic_id」为空错误") } url = setting.Conf.Lakala.UrlMicropay //聚合被扫 data2 := make(map[string]interface{}) data2["merchant_no"] = merchant_no4 data2["term_no"] = term_no4 data2["out_trade_no"] = orderID //随机生成的订单号 //商户交易流水号 //扫码支付授权码,设备读取用户APP中的条码或者二维码信息,用户付款码条形码规则见说明 data2["auth_code"] = input.DynamicID data2["total_amount"] = input.GoodsPrice * 100 data3 := make(map[string]interface{}) data3["request_ip"] = ip //data3["location"] = "+37.123456789,-121.123456789" data2["location_info"] = make(map[string]interface{}) data2["location_info"] = data3 data["req_data"] = make(map[string]interface{}) data["req_data"] = data2 } else { tx.Rollback() return nil, errors.New("输入参数「source_code」错误,有效值为[1-6,9,15-17]") } //数据重组 - end fmt.Println("拉卡拉输入参数") fmt.Println(data) fmt.Println("拉卡拉接口url") fmt.Println(url) // 插入下单请求参数 err = InsertPayBillDetailRequestBody(tx, billID, data) if err != nil { tx.Rollback() return nil, err } data_json, err := json.Marshal(data) if err != nil { tx.Rollback() return nil, err } if input.SourceCode == 9 { //收钱吧(扫码枪) //调拉卡拉接口 go lakala_post(input, url, orderID, data_json) //返回值 response := make(map[string]string) response["payment_order_code"] = orderID //随机生成的订单号 return response, nil } //调拉卡拉接口 err, response, lakala_rtn := lakala_post(input, url, orderID, data_json) if err != nil { InsertPayBillDetailResponseBody(tx, billID, lakala_rtn) //todo tx.Rollback() return nil, err } fmt.Println("谛宝多多拼接后返回值:") fmt.Println(response) // 插入下单成功后返回的参数 err = InsertPayBillDetailResponseBody(tx, billID, lakala_rtn) if err != nil { //beego.Error("下单请求成功 --- 但插入成功后的参数失败") tx.Rollback() return nil, err } tx.Commit() return response, nil } //支付、退款、支付查询 共通调用 func lakala_post(input *models.PlaceAnOrderParamInput, url, order_id string, data_json []byte) (error, interface{}, interface{}) { var source_code, platform_type uint8 if input == nil { source_code = 0 //支付方式 platform_type = 0 //平台类型 } else { source_code = input.SourceCode //支付方式 platform_type = input.PlatformType //平台类型 } authorization, err := getAuthorization(platform_type, string(data_json)) if err != nil { return err, "", nil } fmt.Println(url) fmt.Println(authorization) req := httplib.Post(url) //req.Header("Authorization", "LKLAPI-SHA256withRSA appid=\"\", serial_no=\"\", timestamp=\"\", nonce_str=\"\", signature=\"2233\"") req.Header("Authorization", authorization) req.Header("Accept", "application/json") req.Header("Content-Type", "application/json") req.Body(data_json) body, err := req.Bytes() if err != nil { return err, "", nil } temp := make(map[string]interface{}, 0) if err = json.Unmarshal(body, &temp); err != nil { return err, "", nil } fmt.Println("拉卡拉返回值") fmt.Println(temp) if temp["code"] != "000000" && temp["code"] != "BBS00000" { //this.Data["json"] = utils.CheckError(errors.New("拉卡拉错误"), temp["msg"].(string)) //todo //this.ServeJSON() return errors.New(temp["msg"].(string)), "", temp } response := make(map[string]string) response["out_trade_no"] = order_id if source_code == 4 || source_code == 6 || source_code == 1 || source_code == 5 || source_code == 15 || input.SourceCode == 16 || input.SourceCode == 17 { //聚合收银台(微信H5、支付宝H5、微信扫码、支付宝扫码、快捷支付(银行卡)(包括微信小程序)) //聚合收银台(微信小程序-必康自营)、聚合JSAPI(微信小程序-必康自营) temp2, _ := temp["resp_data"].(map[string]interface{}) if temp2["counter_url"] == "" { return errors.New("拉卡拉返回值「counter_url」为空错误"), "", temp } if source_code == 4 || source_code == 6 || source_code == 15 || source_code == 17 { //微信H5、支付宝H5、快捷支付(银行卡)(包括微信小程序) //聚合收银台(微信JSAPI-必康自营) response["m_web_url"] = temp2["counter_url"].(string) } else if source_code == 1 || source_code == 5 { //微信扫码、支付宝扫码 response["qr_code_url"] = temp2["counter_url"].(string) } else { //聚合收银台(微信小程序-必康自营) response["counter_url"] = temp2["counter_url"].(string) } } else if source_code == 2 || source_code == 3 { //聚合主扫(微信JSAPI、微信小程序)(必康自营的微信小程序除外) temp2, _ := temp["resp_data"].(map[string]interface{}) temp3, _ := temp2["acc_resp_fields"].(map[string]interface{}) if temp2["acc_resp_fields"] == "" { return errors.New("拉卡拉返回值「acc_resp_fields」为空错误"), "", temp } response["app_id"] = temp3["app_id"].(string) response["trade_no"] = temp2["trade_no"].(string) response["nonce_str"] = temp3["nonce_str"].(string) response["package"] = temp3["package"].(string) response["pay_sign"] = temp3["pay_sign"].(string) response["prepay_id"] = temp3["prepay_id"].(string) response["sign_type"] = temp3["sign_type"].(string) response["timeStamp"] = temp3["time_stamp"].(string) } else if source_code == 18 { //聚合主扫(支付宝) temp2, _ := temp["resp_data"].(map[string]interface{}) if temp2["out_trade_no"] == "" { return errors.New("拉卡拉返回值「out_trade_no」为空错误"), "", temp } out_trade_no := temp2["out_trade_no"].(string) trade_no := temp2["trade_no"].(string) if temp2["acc_resp_fields"] == "" { return errors.New("拉卡拉返回值「acc_resp_fields」为空错误"), "", temp } temp3, _ := temp2["acc_resp_fields"].(map[string]interface{}) url := temp3["code"].(string) //out_trade_no := temp3["out_trade_no"].(string) response["url"] = url + "?biz_content={\"out_trade_no\":\"" + out_trade_no + "\", \"trade_no\":\"" + trade_no + "\"}" return nil, response["url"], temp } else if source_code == 9 { //扫码枪 //mBody := map[string]string{ // "payment_order_code": orderID, //} response["payment_order_code"] = order_id } return nil, response, temp } func getAuthorization(platform_type uint8, body string) (string, error) { var path_private_key, appid, mchSerialNo string //私钥文件地址 //平台类型 1:saas 2:shop 3:shop mobile 4:收银台 6:bk_shop 7:bk_shop_mobile if platform_type == 2 || platform_type == 3 { // 谛宝多多自营 appid = setting.Conf.Lakala.DbcAppid mchSerialNo = setting.Conf.Lakala.DbcSerialNo path_private_key = setting.Conf.Lakala.DbcPathPrivateKey } else if platform_type == 6 || platform_type == 7 { // 必康自营 appid = setting.Conf.Lakala.BkAppid mchSerialNo = setting.Conf.Lakala.BkSerialNo path_private_key = setting.Conf.Lakala.BkPathPrivateKey } else { // 必康医生 appid = setting.Conf.Lakala.SaasAppid mchSerialNo = setting.Conf.Lakala.SaasSerialNo path_private_key = setting.Conf.Lakala.SaasPathPrivateKey } nonceStr := RandomString(32) // 构造随机数 timestamp := strconv.FormatInt(time.Now().Unix(), 10) message := appid + "\n" + mchSerialNo + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n" //todo 退款时,证书??? //return "", errors.New("输入项「平台类型(platform_type)」数据错误") //base64Sig, err := RSASign([]byte(message), "./cert/dev/OP00000003_private_key.pem") base64Sig, err := RSASign([]byte(message), path_private_key) if err != nil { return "", err } //fmt.Println("签名2:", base64Sig) signature := base64Sig authorization := "LKLAPI-SHA256withRSA " + "appid=\"" + appid + "\"," + "serial_no=\"" + mchSerialNo + "\"," + "timestamp=\"" + timestamp + "\"," + "nonce_str=\"" + nonceStr + "\"," + "signature=\"" + signature + "\"" //System.out.println("authorization message :" + authorization); return authorization, nil } //生产随机字符串 func RandomString(n int) string { var letters = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") result := make([]byte, n) rand2.Seed(time.Now().Unix()) for i := range result { result[i] = letters[rand2.Intn(len(letters))] } return string(result) } // 私钥签名过程 func RSASign(data []byte, filename string) (string, error) { // 1、选择hash算法,对需要签名的数据进行hash运算 myhash := crypto.SHA256 hashInstance := myhash.New() hashInstance.Write(data) hashed := hashInstance.Sum(nil) // 2、读取私钥文件,解析出私钥对象 privateKey, err := ReadParsePrivaterKey(filename) if err != nil { return "", err } // 3、RSA数字签名(参数是随机数、私钥对象、哈希类型、签名文件的哈希串),生成base64编码的签名字符串 bytes, err := rsa.SignPKCS1v15(rand.Reader, privateKey, myhash, hashed) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(bytes), nil } // 读取私钥文件,解析出私钥对象 func ReadParsePrivaterKey(filename string) (*rsa.PrivateKey, error) { // 1、读取私钥文件,获取私钥字节 privateKeyBytes, err := ioutil.ReadFile(filename) if err != nil { return nil, err } // 2、对私钥文件进行编码,生成加密块对象 block, _ := pem.Decode(privateKeyBytes) fmt.Println(block.Type) if block == nil { return nil, errors.New("私钥信息错误!") } // 3、解析DER编码的私钥,生成私钥对象 prkI, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return nil, err } privateKey := prkI.(*rsa.PrivateKey) return privateKey, nil } // GetNoticeURL is 获取回调地址 func GetNoticeURL(source_code uint8) string { domainName := setting.Conf.PayUrl.DomainName if source_code == 2 || source_code == 3 || source_code == 9 || source_code == 18 { //聚合扫码+聚合被扫 return domainName + "/api/v1/pay/scan_notice" } else { //其它支付 return domainName + "/api/v1/pay/cashier_notice" } } //InsertPayBill is 插入 支付订单表中 func InsertPayBill(tx *sql.Tx, p *models.PlaceAnOrderParamInput, orderID string) (int64, error) { var billID int64 insertSQL := `insert system_pay_bill set platform_type = ?, platform_info = ?, source_code = ?, payment_order_code = ?, paymoney = ?*1000, commodity_describe = ?, commodity_detail = ?, attach = ?, notify_pay_url = ?, pay_type = ?, is_serve = ?` result, err := tx.Exec(insertSQL, p.PlatformType, p.PlatformInfo, p.SourceCode, orderID, p.GoodsPrice, p.GoodsDes, p.GoodsDetail, p.AttachInfo, p.NoticeURL, p.PayType, p.IsServe) if err != nil { return billID, err } billID, err = result.LastInsertId() if err != nil { return billID, err } return billID, nil } // InsertPayBillDetailRequestBody is 插入支付订单详情表中的 下单参数字段 request_body func InsertPayBillDetailRequestBody(tx *sql.Tx, billID int64, requestBody interface{}) error { body, err := json.Marshal(requestBody) if err != nil { return err } insertPayBillDetailSQL := `insert system_pay_bill_detail set pay_bill_id = ?, request_body = ?` result, err := tx.Exec(insertPayBillDetailSQL, billID, string(body)) if err != nil { return err } _, err = result.LastInsertId() if err != nil { return err } return nil } // InsertPayBillDetailResponseBody is 插入支付订单详情表中的 下单参数字段 response_body func InsertPayBillDetailResponseBody(tx *sql.Tx, billID int64, responseBody interface{}) error { body, err := json.Marshal(responseBody) if err != nil { return err } insertPayBillDetailSQL := `update system_pay_bill_detail set response_body = ? where pay_bill_id = ?` result, err := tx.Exec(insertPayBillDetailSQL, string(body), billID) if err != nil { return err } _, err = result.RowsAffected() if err != nil { return err } return nil } //支付查询 插入db func InsertPayBillDetailResponseBodyString(tx *sql.Tx, billID int64, responseBody string) error { insertPayBillDetailSQL := `update system_pay_bill_detail set response_body = ? where pay_bill_id = ?` result, err := tx.Exec(insertPayBillDetailSQL, responseBody, billID) if err != nil { return err } _, err = result.RowsAffected() if err != nil { return err } return nil } func GetUID() string { u4 := uID.NewV4() //if err != nil { // return "" //} id := fmt.Sprintf("%s", u4) return strings.Replace(id, "-", "", -1) } // 拉卡拉退款 func UnifiedRefund(input *models.RefundParamInput, ip string) (interface{}, error) { fmt.Println("谛宝多多输入参数(退款)") fmt.Println(input) refundID := GetUID() // 插入数据库 db, err := mysql.NewPayConn() if err != nil { return 0, err } tx, err := db.Begin() if err != nil { return nil, err } defer mysql.CloseTx(tx, err) //退款订单存在check log_no, trans_term_no, source_code, platform_type, order_create_time, payer_account_no, err := selectRefundBill(tx, input) if err != nil { return nil, err } billID, err := InsertRefundBill(tx, input, refundID) if err != nil { return nil, err } //数据重组 - start now := time.Now() now.Add(time.Minute * 60) date_time1 := now.Format("20060102150405") var version, out_org_code, merchant_no1, merchant_no2, merchant_no3, term_no3, merchant_no4, term_no4 string //平台类型 1:saas 2:shop 3:shop mobile 4:收银台 6:bk_shop 7:bk_shop_mobile if platform_type == 2 || platform_type == 3 { // 谛宝多多自营 version = setting.Conf.Lakala.DbcVersion out_org_code = setting.Conf.Lakala.DbcAppid merchant_no1 = setting.Conf.Lakala.DbcMerchantNo1 merchant_no2 = setting.Conf.Lakala.DbcMerchantNo2 merchant_no3 = setting.Conf.Lakala.DbcMerchantNo3 term_no3 = setting.Conf.Lakala.DbcTermNo3 merchant_no4 = setting.Conf.Lakala.DbcMerchantNo4 term_no4 = setting.Conf.Lakala.DbcTermNo4 } else if platform_type == 6 || platform_type == 7 { // 必康自营 version = setting.Conf.Lakala.BkVersion out_org_code = setting.Conf.Lakala.BkAppid merchant_no1 = setting.Conf.Lakala.BkMerchantNo1 merchant_no2 = setting.Conf.Lakala.BkMerchantNo2 merchant_no3 = setting.Conf.Lakala.BkMerchantNo3 term_no3 = setting.Conf.Lakala.BkTermNo3 merchant_no4 = setting.Conf.Lakala.BkMerchantNo4 term_no4 = setting.Conf.Lakala.BkTermNo4 } else { // 必康医生 version = setting.Conf.Lakala.SaasVersion out_org_code = setting.Conf.Lakala.SaasAppid merchant_no1 = setting.Conf.Lakala.SaasMerchantNo1 merchant_no2 = setting.Conf.Lakala.SaasMerchantNo2 merchant_no3 = setting.Conf.Lakala.SaasMerchantNo3 term_no3 = setting.Conf.Lakala.SaasTermNo3 merchant_no4 = setting.Conf.Lakala.SaasMerchantNo4 term_no4 = setting.Conf.Lakala.SaasTermNo4 } var url string data := make(map[string]interface{}) if source_code == 15 { //快捷支付(银行卡) url = setting.Conf.Lakala.UrlTradeRefund //统一退货接口 } else { //其它 url = setting.Conf.Lakala.UrlRefund //聚合扫码(退款交易) } data["req_time"] = date_time1 data["version"] = version data["out_org_code"] = out_org_code data2 := make(map[string]interface{}) //source_code 1: 微信 Native 2:微信小程序 3:微信内支付 4:h5 跳微信 //5:支付宝(web)-扫码或登录支付宝账户 6:alipay(mobile) 7:alipay(app) //9: B2C 10:bk支付宝web 11:bk 支付宝手机 15:快捷支付(银行卡) //16:微信小程序-必康自营 17:微信JASPI-必康自营 if source_code == 4 || source_code == 6 || source_code == 1 || source_code == 5 || source_code == 15 || source_code == 16 || source_code == 17 { //聚合收银台(微信H5、支付宝H5、微信扫码、支付宝扫码) //聚合收银台(快捷支付(银行卡)) //聚合收银台(微信小程序-必康自营、微信JASPI-必康自营) //data2["merchant_no"] = "8221210701101SB" //data2["merchant_no"] = "8222900581201QB" if source_code == 4 || source_code == 6 { data2["merchant_no"] = merchant_no1 //微信H5、支付宝H5 } else { data2["merchant_no"] = merchant_no2 //其它 } //收银台支付时,没有传「term_no」,所以需要取得支付返回值的「trans_term_no」 data2["term_no"] = trans_term_no } else if source_code == 2 || source_code == 3 { //聚合主扫(微信JSAPI、微信小程序、支付宝) //data2["merchant_no"] = "8222900701107M5" //data2["term_no"] = "A1062976" //data2["merchant_no"] = "8221210701101SB" data2["merchant_no"] = merchant_no3 data2["term_no"] = term_no3 } else if source_code == 9 { //扫码枪 data2["merchant_no"] = merchant_no4 data2["term_no"] = term_no4 } else { return nil, errors.New("「source_code」错误,有效值为[1-6,9,15-17]") } data2["out_trade_no"] = refundID //随机生成的订单号 //商户交易流水号 //data2["out_trade_no"] = RandomString(32) //扫码支付授权码,设备读取用户APP中的条码或者二维码信息,用户付款码条形码规则见说明 //data2["auth_code"] = "135178236713755038" //data2["auth_code"] = input.DynamicID data2["refund_amount"] = input.RefundAmount * 100 //退款金额 data2["refund_reason"] = input.RefundReason //退款原因 //input.OrderId = "2023070566210308960791" //data2["origin_out_trade_no"] = input.OrderId //原商户交易流水号 //data2["origin_trade_no"] = input.OrderId //原拉卡拉交易流水号 //data2["origin_log_no"] = input.OrderId //原对账单流水号 if source_code == 15 { //快捷支付(银行卡) data2["origin_biz_type"] = "4" //原交易类型:1 银行卡,2 外卡,3 扫码,4 线上 data2["refund_type"] = "00" //当商户进件时退货模式配置的为 指定模式退货时,该字段有效。00:退货帐户,05:商户余额退货,06:终端余额退货 //data2["origin_card_no"] = "621700*********0114" todo data2["origin_card_no"] = payer_account_no //原交易银行卡号,银行卡退款必填 //data2["origin_trade_no"] = //原交易拉卡拉交易订单号 data2["origin_trade_date"] = order_create_time //原交易日期:yyyyMMdd } data2["origin_log_no"] = log_no //原对账单流水号 data3 := make(map[string]interface{}) //data3["request_ip"] = "10.176.1.192" data3["request_ip"] = ip //data3["location"] = "+37.123456789,-121.123456789" data2["location_info"] = make(map[string]interface{}) data2["location_info"] = data3 data["req_data"] = make(map[string]interface{}) data["req_data"] = data2 //return nil, errors.New("输入参数「source_code」错误,有效值为[1-6,9,15-17]") //数据重组 - end fmt.Println("拉卡拉输入参数(退款)") fmt.Println(data) fmt.Println("拉卡拉接口url(退款)") fmt.Println(url) // 插入退款请求参数 err = InsertPayBillDetailRequestBody(tx, billID, data) if err != nil { return nil, err } data_json, err := json.Marshal(data) if err != nil { //this.Data["json"] = utils.CheckError(errors.New("Map转化为byte数组失败"),"异常") //this.ServeJSON() return nil, err } var input2 models.PlaceAnOrderParamInput input2.SourceCode = source_code input2.PlatformType = platform_type //调拉卡拉接口 err, _, lakala_rtn := lakala_post(&input2, url, "", data_json) if err != nil { InsertPayBillDetailResponseBody(tx, billID, lakala_rtn) return nil, err } // 插入退款成功后返回的参数 err = InsertPayBillDetailResponseBody(tx, billID, lakala_rtn) if err != nil { //beego.Error("退款请求成功 --- 但退款成功后的参数失败") return nil, err } response := make(map[string]string) response["payment_order_code"] = refundID return response, nil } //退款订单存在check func selectRefundBill(tx *sql.Tx, input *models.RefundParamInput) (string, string, uint8, uint8, string, string, error) { var billID int64 var createtime string var status uint var platform_type uint8 var source_code uint8 var notice_request interface{} var notice_response []byte var notice_request2 []byte //notice_request2 = []byte(`abc`) //payment_order_code, paymoney selectRefundBillSQL := `select b.id, DATE_FORMAT(b.createtime, '%Y%m%d') createtime, b.result_code, b.source_code, b.platform_type, bd.notice_request_body, bd.response_body from system_pay_bill b left join system_pay_bill_detail bd on b.id=bd.pay_bill_id where b._type=0 and b.payment_order_code=?` err := tx.QueryRow(selectRefundBillSQL, input.RefundNo).Scan(&billID, &createtime, &status, &source_code, &platform_type, ¬ice_request2, ¬ice_response) if err != nil { return "", "", 0, 0, "", "", err } if source_code == 2 || source_code == 3 { //聚合扫码 notice_request = new(models.ScanNoticeInput) if err := json.Unmarshal(notice_request2, ¬ice_request); err != nil { return "", "", 0, 0, "", "", err } } else if source_code == 9 { //聚合被扫 notice_request = make(map[string]interface{}, 0) if err := json.Unmarshal(notice_response, ¬ice_request); err != nil { return "", "", 0, 0, "", "", err } //notice_request = notice_response } else { //聚合收银台 notice_request = new(models.CashierNoticeInput) if err := json.Unmarshal(notice_request2, ¬ice_request); err != nil { return "", "", 0, 0, "", "", err } } //fmt.Println(notice_request2) fmt.Println(notice_request) fmt.Println("退款订单存在check返回值:") fmt.Println(billID) fmt.Println(createtime) fmt.Println(status) fmt.Println(source_code) fmt.Println(platform_type) //fmt.Println(notice_request2) // 订单不存在 if billID <= 0 { return "", "", 0, 0, "", "", errors.New("订单不存在2") } // 订单未结算 if status != 1 { return "", "", 0, 0, "", "", errors.New("订单未结算") } if source_code == 0 { return "", "", 0, 0, "", "", errors.New("「source_code」错误,有效值为[1-6,9,15-17]") } // 获取「对账单流水号」、「交易终端号」和「付款人账号」,退款用 var log_no, trans_term_no, payer_account_no string if source_code == 2 || source_code == 3 { //聚合扫码 log_no = notice_request.(*models.ScanNoticeInput).LogNo if log_no == "" { return "", "", 0, 0, "", "", errors.New("「对账单流水号」未取得错误") } } else if source_code == 9 { //聚合被扫 resp_data := notice_request.(map[string]interface{})["resp_data"] fmt.Println("resp_data返回值:", resp_data) if resp_data == nil { return "", "", 0, 0, "", "", errors.New("数据错误:resp_data返回值为空") } log_no = resp_data.(map[string]interface{})["log_no"].(string) //拉卡拉对账单流水号 //log_no = "66210310187398" fmt.Println("log_no返回值:", log_no) if log_no == "" { return "", "", 0, 0, "", "", errors.New("数据错误:拉卡拉对账单流水号为空") } } else { //其它支付 // 针对有的支付,不需要传「交易终端号」时,需要用支付返回的「交易终端号」进行退款 log_no = notice_request.(*models.CashierNoticeInput).OrderTradeInfo.(map[string]interface{})["log_no"].(string) if log_no == "" { return "", "", 0, 0, "", "", errors.New("「对账单流水号」未取得错误") } trans_term_no = notice_request.(*models.CashierNoticeInput).TransTermNo if trans_term_no == "" { return "", "", 0, 0, "", "", errors.New("「交易终端号」未取得错误") } // 银行卡支付时,获取付款人账号 payer_account_no = notice_request.(*models.CashierNoticeInput).OrderTradeInfo.(map[string]interface{})["payer_account_no"].(string) } return log_no, trans_term_no, source_code, platform_type, createtime, payer_account_no, nil } //InsertRefundBill is 插入 支付订单表中 func InsertRefundBill(tx *sql.Tx, p *models.RefundParamInput, refundID string) (int64, error) { var billID int64 //todo 原source //insertSQL := `insert system_pay_bill set platform_type = ?, source_code = ?, //payment_order_code = ?, paymoney = ?*1000, pay_type = 4, attach = ?, _type = 1, original_payment_order_code = ?` //platformType, sourceCode, checkSn, refundAmount, attach, orderID //result, err := tx.Exec(insertSQL, p.PlatformType, p.PlatformInfo, p.SourceCode, // orderID, p.GoodsPrice, p.GoodsDes, p.GoodsDetail, p.AttachInfo, p.NoticeURL, p.PayType, p.IsServe) insertSQL := `insert system_pay_bill set original_payment_order_code=?, payment_order_code=?, paymoney=?*1000, _type=1` //差 platformType, sourceCode, attach 和 pay_type = 4 result, err := tx.Exec(insertSQL, p.RefundNo, refundID, p.RefundAmount) if err != nil { return billID, err } billID, err = result.LastInsertId() if err != nil { return billID, err } return billID, nil } // 拉卡拉支付查询 func OrderState(order_id string) (interface{}, error) { fmt.Println("谛宝多多输入参数(支付查询)") fmt.Println(order_id) if order_id == "" { return nil, errors.New("输入参数为空错误") } // 插入数据库 db, err := mysql.NewPayConn() if err != nil { return 0, err } tx, err := db.Begin() if err != nil { return nil, err } defer mysql.CloseTx(tx, err) var isExist uint err = tx.QueryRow(`select count(id) from system_pay_bill where payment_order_code = ?`, order_id).Scan(&isExist) if err != nil { return nil, err } if isExist == 0 { return nil, fmt.Errorf("账单不存在") } //todo 删除不需要的变量 querySQL := `select id, source_code, result_code, payment_order_code, attach, _type, platform_type from system_pay_bill where payment_order_code = ?` result := new(models.OrderState) var sourceCode, platformType uint var paymentOrderCode string var attach string var billID int64 var billType uint8 err = db.QueryRow(querySQL, order_id).Scan(&billID, &sourceCode, &result.State, &paymentOrderCode, &attach, &billType, &platformType) if err != nil { return nil, err } // 如果是 未结算成功、并且是收钱吧渠道的账单 if result.State == 0 && sourceCode == 9 { //todo wangpei // 查询收钱吧支付是否成功 state, err := SqbQueryOrderState(tx, paymentOrderCode, attach, billID, billType, platformType, sourceCode) if err != nil { return nil, err } result.State = state } return result, nil } //订单状态 0: 未结算 1: 结算成功 2: 结算失败 func SqbQueryOrderState(tx *sql.Tx, paymentOrderCode, attach string, billID int64, billType uint8, platformType, sourceCode uint) (uint8, error) { fmt.Println("attachMap: ", attach) var attachMap map[string]interface{} err := json.Unmarshal([]byte(attach), &attachMap) if err != nil { return 0, err } //storeSn := attachMap["store_sn"].(string) //if storeSn == "" { // return 0, fmt.Errorf("参数错误") //} var input2 models.PlaceAnOrderParamInput //input2.SourceCode = source_code //input2.PlatformType = platform_type //url := "https://test.wsmsd.cn/sit/api/v3/labs/query/tradequery" //测试 //url := "https://s2.lakala.com/api/v3/labs/query/tradequery" //正式 url := setting.Conf.Lakala.UrlOrderState //聚合扫码-交易查询 //数据重组 - start var merchant_no3, term_no3, merchant_no4, term_no4 string var merchant_no, term_no, version, out_org_code string now := time.Now() now.Add(time.Minute * 60) date_time1 := now.Format("20060102150405") //平台类型 1:saas 2:shop 3:shop mobile 4:收银台 6:bk_shop 7:bk_shop_mobile if platformType == 2 || platformType == 3 { // 谛宝多多自营 version = setting.Conf.Lakala.DbcVersion out_org_code = setting.Conf.Lakala.DbcAppid //merchant_no1 = setting.Conf.Lakala.DbcMerchantNo1 //merchant_no2 = setting.Conf.Lakala.DbcMerchantNo2 merchant_no3 = setting.Conf.Lakala.DbcMerchantNo3 term_no3 = setting.Conf.Lakala.DbcTermNo3 merchant_no4 = setting.Conf.Lakala.DbcMerchantNo4 term_no4 = setting.Conf.Lakala.DbcTermNo4 } else if platformType == 6 || platformType == 7 { // 必康自营 version = setting.Conf.Lakala.BkVersion out_org_code = setting.Conf.Lakala.BkAppid //merchant_no1 = setting.Conf.Lakala.BkMerchantNo1 //merchant_no2 = setting.Conf.Lakala.BkMerchantNo2 merchant_no3 = setting.Conf.Lakala.BkMerchantNo3 term_no3 = setting.Conf.Lakala.BkTermNo3 merchant_no4 = setting.Conf.Lakala.BkMerchantNo4 term_no4 = setting.Conf.Lakala.BkTermNo4 } else { // 必康医生 version = setting.Conf.Lakala.SaasVersion out_org_code = setting.Conf.Lakala.SaasAppid //merchant_no1 = setting.Conf.Lakala.SaasMerchantNo1 //merchant_no2 = setting.Conf.Lakala.SaasMerchantNo2 merchant_no3 = setting.Conf.Lakala.SaasMerchantNo3 term_no3 = setting.Conf.Lakala.SaasTermNo3 merchant_no4 = setting.Conf.Lakala.SaasMerchantNo4 term_no4 = setting.Conf.Lakala.SaasTermNo4 } if sourceCode == 2 || sourceCode == 3 { //聚合主扫(微信JSAPI、微信小程序) merchant_no = merchant_no3 term_no = term_no3 } else if sourceCode == 9 { //聚合被扫(扫码枪) merchant_no = merchant_no4 term_no = term_no4 } data := make(map[string]interface{}) data["req_time"] = date_time1 data["version"] = version data["out_org_code"] = out_org_code data2 := make(map[string]interface{}) data2["merchant_no"] = merchant_no //商户号 data2["term_no"] = term_no //终端号 data2["out_trade_no"] = paymentOrderCode //商户交易流水号 //data["trade_no"] = trade_no //拉卡拉交易流水号 data["req_data"] = data2 data_json, err := json.Marshal(data) if err != nil { return 0, err } fmt.Println("拉卡拉请求参数(支付查询)", data_json) //调拉卡拉接口 err, _, lakala_rtn := lakala_post(&input2, url, "", data_json) if err != nil { InsertPayBillDetailResponseBody(tx, billID, lakala_rtn) return 0, err } // 插入成功后返回的结果 err = InsertPayBillDetailResponseBody(tx, billID, lakala_rtn) if err != nil { //beego.Error("支付查询请求成功 --- 但退成功后的插入参数失败") return 0, err } //INIT-初始化 CREATE-下单成功 SUCCESS-交易成功 FAIL-交易失败 DEAL-交易处理中 //UNKNOWN-未知状态 CLOSE-订单关闭 PART_REFUND-部分退款 REFUND-全部退款(或订单被撤销) code := lakala_rtn.(map[string]interface{})["code"] //交易code fmt.Println("code返回值:", code) if code != "BBS00000" { // 返回失败 return 0, nil //todo 返回 2: 结算失败 ? } resp_data := lakala_rtn.(map[string]interface{})["resp_data"] fmt.Println("resp_data返回值:", resp_data) if resp_data == nil { // 返回失败 return 0, nil //todo 返回 2: 结算失败 ? } trade_state := resp_data.(map[string]interface{})["trade_state"] //交易状态 fmt.Println("trade_state返回值:", trade_state) if trade_state != "SUCCESS" { // 返回未结算 return 0, nil //todo 返回 2: 结算失败 ? } querySQL := `select result_code from system_pay_bill where id = ?` var resultCode uint err = tx.QueryRow(querySQL, billID).Scan(&resultCode) if err != nil { return 0, errors.New("查询失败:" + err.Error()) } fmt.Println("resultCode返回值:", resultCode) // 如果支付过、则直接返回 if resultCode != 0 && resultCode != 2 { // 返回支付成功 fmt.Println("支付过、则直接返回支付成功") return 1, nil } // 如果支付成功则改变状态 err = BillPayStateSuccess(tx, billID) if err != nil { //beego.Error("改变支付状态为成功时错误: ", err) //tx.Rollback() fmt.Println("改变支付状态为成功时错误:", billID) return 0, nil } return 1, nil }