package controllers import ( "XT_New/enums" "XT_New/models" "XT_New/service" "bytes" "crypto/md5" "encoding/hex" "encoding/json" "encoding/xml" "errors" "fmt" "github.com/astaxie/beego" "io/ioutil" "math/rand" "net/http" "regexp" "sort" "strconv" "strings" "time" ) const ( pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder" ) type PayApiController struct { BaseServeAPIController parameters map[string]string resultParameters map[string]string payUrl string } type PayApiController2 struct { beego.Controller parameters map[string]string resultParameters map[string]string payUrl string } func PayApiRegistRouters() { beego.Router("/api/my/service", &PayApiController{}, "Get:GetMyService") beego.Router("/api/product", &PayApiController{}, "Get:GetProduct") beego.Router("/api/pay", &PayApiController{}, "Get:GetPayUrl") beego.Router("/api/pay/transfer", &PayApiController{}, "Post:PostTransferStatus") beego.Router("/api/order", &PayApiController{}, "Post:PostOrderInfo") beego.Router("/api/order/get", &PayApiController{}, "Get:GetOrderInfo") beego.Router("/api/pay/notify", &PayApiController2{}, "Post:WxPaySuccessNotify") beego.Router("/api/order/cancel", &PayApiController{}, "Post:CancelOrder") beego.Router("/api/order/list", &PayApiController{}, "Get:GetOrderList") beego.Router("/api/order/hetong", &PayApiController{}, "Get:GetHeTong") beego.Router("/api/order/hetong", &PayApiController{}, "Post:CreateHeTong") } func (c *PayApiController) GetOrderList() { adminUserInfo := c.GetAdminUserInfo() serviceOrderList, err := service.GetOrderList(adminUserInfo.CurrentOrgId) if err == nil { c.ServeSuccessJSON(map[string]interface{}{ "list": serviceOrderList, }) } else { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) } } func (c *PayApiController) CancelOrder() { adminUserInfo := c.GetAdminUserInfo() orderId, _ := c.GetInt64("id", 0) if orderId == 0 { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong) return } order, err := service.FindServeOrderByID(adminUserInfo.CurrentOrgId, orderId) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } if order == nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) return } errs := service.UpdateOrderStatus(adminUserInfo.CurrentOrgId, orderId) if errs == nil { c.ServeSuccessJSON(map[string]interface{}{ "msg": "取消成功", }) } else { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) } } func (c *PayApiController) PostTransferStatus() { adminUserInfo := c.GetAdminUserInfo() orderId, _ := c.GetInt64("id", 0) if orderId == 0 { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong) return } order, err := service.FindServeOrderByID(adminUserInfo.CurrentOrgId, orderId) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } if order == nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) return } errs := service.UpdateOrderPayType(adminUserInfo.CurrentOrgId, orderId) if errs == nil { c.ServeSuccessJSON(map[string]interface{}{ "msg": "确认成功", }) } else { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) } } func (c *PayApiController) GetOrderInfo() { adminUserInfo := c.GetAdminUserInfo() orderId, _ := c.GetInt64("id", 0) order, err := service.FindServeOrderByID(adminUserInfo.CurrentOrgId, orderId) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } if order == nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) return } orderInfo, err := service.FindOrderInfomationByID(order.OrderNumber, adminUserInfo.CurrentOrgId) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } if orderInfo == nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) return } c.ServeSuccessJSON(map[string]interface{}{ "total": order.PaymentAmount, "orderNumber": order.OrderNumber, "OrgName": adminUserInfo.Orgs[adminUserInfo.CurrentOrgId].OrgName, "order": order, "orderInfo": orderInfo, }) } func (c *PayApiController) PostOrderInfo() { adminUserInfo := c.GetAdminUserInfo() amount, _ := c.GetInt64("amount", 0) productId, _ := c.GetInt64("id", 0) product, err := service.FindProductByID(productId) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } if product == nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeProductError) return } timestamp := c.GetTimestamp() //自定义订单号 bill_no := "S" + strconv.FormatInt(timestamp, 10) + strconv.FormatInt(adminUserInfo.CurrentOrgId, 10) order := models.ServeOrder{ OrgId: adminUserInfo.CurrentOrgId, Period: amount * 12, Status: 1, CreatedTime: time.Now().Unix(), UpdatedTime: time.Now().Unix(), OrderNumber: bill_no, OrderStatus: 1, PayableAmount: float64(amount) * product.Price, PaymentAmount: float64(amount) * product.Price, ServeName: product.ServeName, ServeDesc: product.ServeDesc, OrderExpireTime: time.Now().Unix() + (7 * 24 * 3600), Quantity: amount, Price: product.Price, ServeId: productId, } infomation := models.ServeOrderInfomation{ OrgId: adminUserInfo.CurrentOrgId, OrderNumber: bill_no, Status: 1, ProductId: product.ID, ProductName: product.ServeName, ProductDesc: product.ServeDesc, Price: product.Price, Quantity: amount, MarketPrice: product.OriginalPrice, } service.CreateOrderRecord(&order) service.CreateOrderInfomation(&infomation) c.ServeSuccessJSON(map[string]interface{}{ "order": order, "msg": "提交订单成功", }) } func (c *PayApiController) GetProduct() { adminUserInfo := c.GetAdminUserInfo() products, err := service.FindAllProduct() subscibe, _ := service.FindServiceSubscibeByOrgId(adminUserInfo.CurrentOrgId) if err == nil { c.ServeSuccessJSON(map[string]interface{}{ "products": products, "subscibe": subscibe, "serviceTime": time.Now().Unix(), //"OrgName": adminUserInfo.Orgs[adminUserInfo.CurrentOrgId].OrgName, }) } else { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeSystemError) } } func (c *PayApiController) GetMyService() { adminUserInfo := c.GetAdminUserInfo() subscibe, err := service.FindServiceSubscibeByOrgId(adminUserInfo.CurrentOrgId) if err == nil { c.ServeSuccessJSON(map[string]interface{}{ "subscibe": subscibe, "serviceTime": time.Now().Unix(), }) } else { } } func (c *PayApiController) GetPayUrl() { adminUserInfo := c.GetAdminUserInfo() orderId, _ := c.GetInt64("id", 0) if orderId == 0 { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong) return } order, err := service.FindServeOrderByID(adminUserInfo.CurrentOrgId, orderId) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } if order == nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) return } orderInfo, err := service.FindOrderInfomationByID(order.OrderNumber, adminUserInfo.CurrentOrgId) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } if orderInfo == nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) return } totalPrice := orderInfo.Price * float64(orderInfo.Quantity) totalFee := fmt.Sprintf("%.0f", totalPrice*100) c.SetParameter("out_trade_no", orderInfo.OrderNumber) c.SetParameter("total_fee", totalFee) c.SetParameter("trade_type", "NATIVE") c.SetParameter("body", order.ServeName) notify_url := beego.AppConfig.String("httpdomain") + "/api/pay/notify" c.SetParameter("product_id", orderInfo.OrderNumber) c.SetParameter("notify_url", notify_url) c.SetParameter("spbill_create_ip", c.GetClientIp()) fmt.Println(c.GetClientIp()) url, err := c.GetPayCodeUrl() fmt.Println(err) if err == nil { c.SetPayUrl(url) c.ServeSuccessJSON(map[string]interface{}{ "payUrl": url, "price": order.PayableAmount, //": adminUserInfo.Orgs[adminUserInfo.CurrentOrgId].OrgName, }) } else { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) // utils.LogError(err) } } func (this *PayApiController2) WxPaySuccessNotify() { type PaySuccessXmlResp struct { ReturnCode string `xml:"return_code"` Appid string `xml:"appid"` BankType string `xml:"bank_type"` CashFee int64 `xml:"cash_fee"` FeeType string `xml:"fee_type"` IsSubscribe string `xml:"is_subscribe"` MchId string `xml:"mch_id"` NonceStr string `xml:"nonce_str"` OpenId string `xml:"openid"` OutTradeNo string `xml:"out_trade_no"` ResultCode string `xml:"result_code"` Sign string `xml:"sign"` TimeEnd string `xml:"time_end"` TotalFee string `xml:"total_fee"` TradeType string `xml:"trade_type"` TransactionId string `xml:"transaction_id"` } defer this.Ctx.Request.Body.Close() result, _ := ioutil.ReadAll(this.Ctx.Request.Body) res := new(PaySuccessXmlResp) xmlErr := xml.Unmarshal(result, &res) if xmlErr != nil { } order, _ := service.FindServeOrderByOrderNumber(res.OutTradeNo) service.UpdateOrder(order.OrderNumber, order.Quantity, order.OrgId, res.TransactionId, order.PeriodEnd, order.PeriodStart) this.SetResultParameter("return_code", res.ReturnCode) results := this.ParamsToXml2(this.resultParameters) this.Ctx.WriteString(results) this.ServeXML() } func (this *PayApiController) GetTimestamp() int64 { return time.Now().UnixNano() / 1000000 //毫秒 } func (this *PayApiController) GetClientIp() string { ip := this.Ctx.Request.Header.Get("Remote_addr") if ip == "" { ip = this.Ctx.Request.RemoteAddr } if strings.Contains(ip, ":") { ip = this.Substr(ip, 0, strings.Index(ip, ":")) } return ip } // 截取字符串 start 起点下标 end 终点下标(不包括) func (this *PayApiController) Substr(str string, start int, end int) string { rs := []rune(str) length := len(rs) if start < 0 || start > length { return "" } if end < 0 || end > length { return "" } return string(rs[start:end]) } // 设置请求参数 func (this *PayApiController) SetParameter(key string, value string) { if this.parameters == nil { this.parameters = make(map[string]string) } this.parameters[key] = value } // 设置请求参数 func (this *PayApiController2) SetResultParameter(key string, value string) { if this.resultParameters == nil { this.resultParameters = make(map[string]string) } this.resultParameters[key] = value } // 设置prepay_id func (this *PayApiController) SetPayUrl(payUrl string) { this.payUrl = payUrl } type XmlResp struct { Return_code string `xml:"return_code"` Return_msg string `xml:"return_msg"` Result_code string `xml:"result_code"` Err_code string `xml:"err_code"` Err_code_des string `xml:"err_code_des"` Prepay_id string `xml:"prepay_id"` Code_url string `xml:"code_url"` } func (this *PayApiController) GetPayCodeUrl() (string, error) { strXml, err := this.CreateXml() if err != nil { return "", this.Error("get pay_code_url error", err) } result, err := this.http_post(pay_url, strXml) if err != nil { return "", this.Error("get pay_code_url error", err) } res := new(XmlResp) xmlErr := xml.Unmarshal(result, &res) if xmlErr != nil { return "", this.Error("get pay_code_url xml error", xmlErr) } if res.Return_code != "SUCCESS" { return "", this.Error("get pay_code_url result error: "+res.Return_msg, nil) } if res.Result_code != "SUCCESS" { return "", this.Error("get pay_code_url result error: "+res.Err_code+"-"+res.Err_code_des, nil) } if res.Code_url == "" { return "", this.Error("get pay_code_url result error: not get pay_code_url", nil) } return res.Code_url, nil } func (this *PayApiController) http_post(url string, xml string) ([]byte, error) { bc := &http.Client{ Timeout: 30 * time.Second, //设置超时时间30s } res, err := bc.Post(url, "text/xml:charset=UTF-8", strings.NewReader(xml)) if err != nil { return nil, this.Error("post", err) } result, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { return nil, this.Error("post result err", err) } return result, nil } func (this *PayApiController) CreateXml() (string, error) { //检测必填参数 if this.parameters["out_trade_no"] == "" { return "", this.Error("缺少统一支付接口必填参数out_trade_no(商户订单号)!", nil) } else if this.parameters["body"] == "" { return "", this.Error("缺少统一支付接口必填参数body(商品描述)!", nil) } else if this.parameters["total_fee"] == "" { return "", this.Error("缺少统一支付接口必填参数total_fee(交易金额)!", nil) } else if this.parameters["notify_url"] == "" { return "", this.Error("缺少统一支付接口必填参数notify_url(异步接收微信支付结果通知的回调地址)!", nil) } else if this.parameters["trade_type"] == "" { return "", this.Error("缺少统一支付接口必填参数trade_type(交易类型)!", nil) } else if this.parameters["spbill_create_ip"] == "" { return "", this.Error("缺少统一支付接口必填参数spbill_create_ip(终端ip)", nil) } else if this.parameters["trade_type"] == "NATIVE" && this.parameters["product_id"] == "" { return "", this.Error("统一支付接口中,缺少必填参数product_id!trade_type为NATIVE时,product_id为必填参数!", nil) } this.parameters["appid"] = beego.AppConfig.String("appid") //公众账号ID this.parameters["mch_id"] = beego.AppConfig.String("mchid") //商户号 this.parameters["nonce_str"] = this.CreateNoncestr(32) //随机字符串 this.parameters["sign"] = this.GetSign(this.parameters, beego.AppConfig.String("key")) //签名 return this.ParamsToXml(this.parameters), nil } // 产生随机字符串,不长于32位 func (this *PayApiController) CreateNoncestr(length int) (nonceStr string) { chars := "abcdefghijklmnopqrstuvwxyz0123456789" for i := 0; i < length; i++ { idx := rand.Intn(len(chars) - 1) nonceStr += chars[idx : idx+1] } return } func (this *PayApiController) Error(strMsg string, err error) error { if err == nil { return errors.New(strMsg) } else { return errors.New(strMsg + ": " + err.Error()) } } // 格式化参数,签名过程需要使用 func (this *PayApiController) FormatParams(paramsMap map[string]string) string { //STEP 1, 对key进行升序排序. var sorted_keys []string for k, _ := range paramsMap { sorted_keys = append(sorted_keys, k) } sort.Strings(sorted_keys) //STEP2, 对key=value的键值对用&连接起来,略过空值 var paramsStr []string for _, k := range sorted_keys { v := fmt.Sprintf("%v", strings.TrimSpace(paramsMap[k])) if v != "" { paramsStr = append(paramsStr, fmt.Sprintf("%s=%s", k, v)) } } return strings.Join(paramsStr, "&") } // 生成签名 func (this *PayApiController) GetSign(paramsMap map[string]string, wxKey string) string { //STEP 1:按字典序排序参数 paramsStr := this.FormatParams(paramsMap) //STEP 2:在string后加入KEY signStr := paramsStr + "&key=" + wxKey //STEP 3:MD5加密 sign := md5.New() sign.Write([]byte(signStr)) //STEP 3:所有字符转为大写 return strings.ToUpper(hex.EncodeToString(sign.Sum(nil))) } // xml结构 func (this *PayApiController) ParamsToXml(data map[string]string) string { fmt.Println(data) buf := bytes.NewBufferString("") for k, v := range data { str := "" flag, _ := regexp.MatchString("^\\d+\\.?\\d*$", v) if flag { str = "%s" } buf.WriteString(fmt.Sprintf("<%s>"+str+"", k, v, k)) } buf.WriteString("") return buf.String() } // xml结构 func (this *PayApiController2) ParamsToXml2(data map[string]string) string { fmt.Println(data) buf := bytes.NewBufferString("") for k, v := range data { str := "" flag, _ := regexp.MatchString("^\\d+\\.?\\d*$", v) if flag { str = "%s" } buf.WriteString(fmt.Sprintf("<%s>"+str+"", k, v, k)) } buf.WriteString("") return buf.String() } func (this *PayApiController) SetUrl(payUrl string) { this.payUrl = payUrl } func (c *PayApiController) GetHeTong() { order_id, _ := c.GetInt64("order_id", 0) if order_id <= 0 { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong) return } admin := c.GetAdminUserInfo() ht, err := service.GetHetong(admin.CurrentOrgId, order_id) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } c.ServeSuccessJSON(map[string]interface{}{ "ht": ht, }) } func (c *PayApiController) CreateHeTong() { orderId, _ := c.GetInt64("order_id", 0) if orderId <= 0 { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong) return } admin := c.GetAdminUserInfo() order, err := service.FindServeOrderByID(admin.CurrentOrgId, orderId) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } if order == nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeServeNotExist) return } ht, err := service.GetHetong(admin.CurrentOrgId, orderId) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeDataException) return } if ht != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeHetongHad) return } var hetong models.ServeOrderContract err = json.Unmarshal(c.Ctx.Input.RequestBody, &hetong) if err != nil { fmt.Println(err) c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeParamWrong) return } hetong.OrderId = orderId hetong.OrderNumber = order.OrderNumber hetong.CreatedTime = time.Now().Unix() hetong.UpdatedTime = time.Now().Unix() hetong.Status = 1 hetong.OrgId = admin.CurrentOrgId err = service.CreateHetong(&hetong) if err != nil { c.ServeFailJSONWithSGJErrorCode(enums.ErrorCodeCreateHetongFail) return } c.ServeSuccessJSON(map[string]interface{}{ "ht": hetong, }) }