diff --git a/external/kassa/AgentType/enums.go b/external/kassa/AgentType/enums.go index 0241765..b23a852 100644 --- a/external/kassa/AgentType/enums.go +++ b/external/kassa/AgentType/enums.go @@ -3,11 +3,11 @@ package AgentType type AgentType string const ( - BANKING_PAYMENT_AGENT = "banking_payment_agent" // Банковский платежный агент - BANKING_PAYMENT_SUBAGENT = "banking_payment_subagent" // Банковский платежный субагент - PAYMENT_AGENT = "payment_agent" // Платежный агент - PAYMENT_SUBAGENT = "payment_subagent" // Платежный субагент - ATTORNEY = "attorney" // Поверенный - COMMISSIONER = "commissioner" // Комиссионер - AGENT = "agent" // Агент + BANKING_PAYMENT_AGENT AgentType = "banking_payment_agent" // Банковский платежный агент + BANKING_PAYMENT_SUBAGENT AgentType = "banking_payment_subagent" // Банковский платежный субагент + PAYMENT_AGENT AgentType = "payment_agent" // Платежный агент + PAYMENT_SUBAGENT AgentType = "payment_subagent" // Платежный субагент + ATTORNEY AgentType = "attorney" // Поверенный + COMMISSIONER AgentType = "commissioner" // Комиссионер + AGENT AgentType = "agent" // Агент ) diff --git a/external/kassa/Measure/enums.go b/external/kassa/Measure/enums.go index ab2da6d..8838472 100644 --- a/external/kassa/Measure/enums.go +++ b/external/kassa/Measure/enums.go @@ -3,28 +3,28 @@ package Measure type Measure string const ( - PIECE = "piece" // Штука, единица товара - GRAM = "gram" // Грамм - KILOGRAM = "kilogram" // Килограмм - TON = "ton" // Тонна - CENTIMETER = "centimeter" // Сантиметр - DECIMETER = "decimeter" // Дециметр - METER = "meter" // Метр - SQUARE_CENTIMETER = "square_centimeter" // Квадратный сантиметр - SQUARE_DECIMETER = "square_decimeter" // Квадратный дециметр - SQUARE_METER = "square_meter" // Квадратный метр - MILLILITER = "milliliter" // Миллилитр - LITER = "liter" // Литр - CUBIC_METER = "cubic_meter" // Кубический метр - KILOWATT_HOUR = "kilowatt_hour" // Килловат-час - GIGACALORIE = "gigacalorie" // Гигакалория - DAY = "day" // Сутки - HOUR = "hour" // Час - MINUTE = "minute" // Минута - SECOND = "second" // Секунда - KILOBYTE = "kilobyte" // Килобайт - MEGABYTE = "megabyte" // Мегабайт - GIGABYTE = "gigabyte" // Гигабайт - TERABYTE = "terabyte" // Терабайт - ANOTHER = "another" // Другое + PIECE Measure = "piece" // Штука, единица товара + GRAM Measure = "gram" // Грамм + KILOGRAM Measure = "kilogram" // Килограмм + TON Measure = "ton" // Тонна + CENTIMETER Measure = "centimeter" // Сантиметр + DECIMETER Measure = "decimeter" // Дециметр + METER Measure = "meter" // Метр + SQUARE_CENTIMETER Measure = "square_centimeter" // Квадратный сантиметр + SQUARE_DECIMETER Measure = "square_decimeter" // Квадратный дециметр + SQUARE_METER Measure = "square_meter" // Квадратный метр + MILLILITER Measure = "milliliter" // Миллилитр + LITER Measure = "liter" // Литр + CUBIC_METER Measure = "cubic_meter" // Кубический метр + KILOWATT_HOUR Measure = "kilowatt_hour" // Килловат-час + GIGACALORIE Measure = "gigacalorie" // Гигакалория + DAY Measure = "day" // Сутки + HOUR Measure = "hour" // Час + MINUTE Measure = "minute" // Минута + SECOND Measure = "second" // Секунда + KILOBYTE Measure = "kilobyte" // Килобайт + MEGABYTE Measure = "megabyte" // Мегабайт + GIGABYTE Measure = "gigabyte" // Гигабайт + TERABYTE Measure = "terabyte" // Терабайт + ANOTHER Measure = "another" // Другое ) diff --git a/external/kassa/PaymentMode/enums.go b/external/kassa/PaymentMode/enums.go index 1c9ac0c..3a1d64e 100644 --- a/external/kassa/PaymentMode/enums.go +++ b/external/kassa/PaymentMode/enums.go @@ -3,11 +3,11 @@ package PaymentMode type PaymentMode string const ( - FULL_PREPAYMENT = "full_prepayment" // Полная предоплата - PARTIAL_PREPAYMENT = "partial_prepayment" // Частичная предоплата - ADVANCE = "advance" // Аванс - FULL_PAYMENT = "full_payment" // Полный расчет - PARTIAL_PAYMENT = "partial_payment" // Частичный расчет и кредит - CREDIT = "credit" // Кредит - CREDIT_PAYMENT = "credit_payment" // Выплата по кредиту + FULL_PREPAYMENT PaymentMode = "full_prepayment" // Полная предоплата + PARTIAL_PREPAYMENT PaymentMode = "partial_prepayment" // Частичная предоплата + ADVANCE PaymentMode = "advance" // Аванс + FULL_PAYMENT PaymentMode = "full_payment" // Полный расчет + PARTIAL_PAYMENT PaymentMode = "partial_payment" // Частичный расчет и кредит + CREDIT PaymentMode = "credit" // Кредит + CREDIT_PAYMENT PaymentMode = "credit_payment" // Выплата по кредиту ) diff --git a/external/kassa/PaymentSubject/enums.go b/external/kassa/PaymentSubject/enums.go index e639f21..3bd75d5 100644 --- a/external/kassa/PaymentSubject/enums.go +++ b/external/kassa/PaymentSubject/enums.go @@ -3,23 +3,23 @@ package PaymentSubject type PaymentSubject string const ( - COMMODITY = "commodity" //Товар Товар - EXCISE = "excise" //Подакцизный товар - JOB = "job" //Работа - SERVICE = "service" //Услуга Услуга - PAYMENT = "payment" //Платеж Платеж - CASINO = "casino" // Платеж казино - GAMBLING_BET = "gambling_bet" //Ставка в азартной игре - GAMBLING_PRIZE = "gambling_prize" // Выигрыш азартной игры - LOTTERY = "lottery" // Лотерейный билет - LOTTERY_PRIZE = "lottery_prize" // Выигрыш в лотерею - INTELLECTUAL_ACTIVITY = "intellectual_activity" //Результаты интеллектуальной деятельности - AGENT_COMMISSION = "agent_commission" //Агентское вознаграждение - PROPERTY_RIGHT = "property_right" //Имущественное право - NON_OPERATING_GAIN = "non_operating_gain" //Внереализационный доход - INSURANCE_PREMIUM = "insurance_premium" //Страховой сбор - SALES_TAX = "sales_tax" //Торговый сбор - RESORT_FEE = "resort_fee" // Курортный сбор - COMPOSITE = "composite" // Несколько вариантов - ANOTHER = "another" // Другое + COMMODITY PaymentSubject = "commodity" //Товар Товар + EXCISE PaymentSubject = "excise" //Подакцизный товар + JOB PaymentSubject = "job" //Работа + SERVICE PaymentSubject = "service" //Услуга Услуга + PAYMENT PaymentSubject = "payment" //Платеж Платеж + CASINO PaymentSubject = "casino" // Платеж казино + GAMBLING_BET PaymentSubject = "gambling_bet" //Ставка в азартной игре + GAMBLING_PRIZE PaymentSubject = "gambling_prize" // Выигрыш азартной игры + LOTTERY PaymentSubject = "lottery" // Лотерейный билет + LOTTERY_PRIZE PaymentSubject = "lottery_prize" // Выигрыш в лотерею + INTELLECTUAL_ACTIVITY PaymentSubject = "intellectual_activity" //Результаты интеллектуальной деятельности + AGENT_COMMISSION PaymentSubject = "agent_commission" //Агентское вознаграждение + PROPERTY_RIGHT PaymentSubject = "property_right" //Имущественное право + NON_OPERATING_GAIN PaymentSubject = "non_operating_gain" //Внереализационный доход + INSURANCE_PREMIUM PaymentSubject = "insurance_premium" //Страховой сбор + SALES_TAX PaymentSubject = "sales_tax" //Торговый сбор + RESORT_FEE PaymentSubject = "resort_fee" // Курортный сбор + COMPOSITE PaymentSubject = "composite" // Несколько вариантов + ANOTHER PaymentSubject = "another" // Другое ) diff --git a/external/kassa/Settlements/enums.go b/external/kassa/Settlements/enums.go index c76c5a7..89f7709 100644 --- a/external/kassa/Settlements/enums.go +++ b/external/kassa/Settlements/enums.go @@ -3,8 +3,8 @@ package Settlements type Settlements string const ( - CASHLESS = "cashless" // Безналичный расчет - PREPAYMENT = "prepayment" // Предоплата (аванс) - POSTPAYMENT = "postpayment" // Постоплата (кредит) - CONSIDERATION = "consideration" // Встречное предоставление + CASHLESS Settlements = "cashless" // Безналичный расчет + PREPAYMENT Settlements = "prepayment" // Предоплата (аванс) + POSTPAYMENT Settlements = "postpayment" // Постоплата (кредит) + CONSIDERATION Settlements = "consideration" // Встречное предоставление ) diff --git a/external/kassa/TaxSystemCode/enums.go b/external/kassa/TaxSystemCode/enums.go index e0c61b0..590e412 100644 --- a/external/kassa/TaxSystemCode/enums.go +++ b/external/kassa/TaxSystemCode/enums.go @@ -3,7 +3,7 @@ package TaxSystemCode type TaxSystemCode int const ( - GENERAL = iota + 1 + GENERAL TaxSystemCode = iota + 1 USN_INCOME USN_INCOME_MINUS_EXPENCES ENVD diff --git a/external/kassa/kassa.go b/external/kassa/kassa.go index 2d0564b..97fe23f 100644 --- a/external/kassa/kassa.go +++ b/external/kassa/kassa.go @@ -80,7 +80,7 @@ func basicAuth(username, password string) string { return base64.StdEncoding.EncodeToString([]byte(auth)) } -func CreatePayment(orderId int, sum float64, fullName string, email string, phone string, items []KassaReceiptItems) (map[string]interface{}, error) { +func CreatePayment(orderId int, sum float64, fullName string, email string, phone string, items []KassaReceiptItems) (*KassaResult, error) { req := KassaPaymentReq{ Amount: KassaAmount{Value: fmt.Sprintf("%f", sum), Currency: "RUB"}, Description: fmt.Sprintf("Заказ №%d", orderId), @@ -111,7 +111,7 @@ func CreatePayment(orderId int, sum float64, fullName string, email string, phon response, err := client.Do(request) - result := map[string]interface{}{} + result := new(KassaResult) json.NewDecoder(response.Body).Decode(&result) if err != nil { diff --git a/handlers/order/endpoints/ep.go b/handlers/order/endpoints/ep.go index 7fe32a2..977d11d 100644 --- a/handlers/order/endpoints/ep.go +++ b/handlers/order/endpoints/ep.go @@ -3,7 +3,9 @@ package endpoints import ( "context" "fmt" + "net/http" "relynolli-server/models" + "relynolli-server/services" "relynolli-server/status" "relynolli-server/storage" "time" @@ -58,31 +60,51 @@ func (h handlers) GetTotal(c *gin.Context) { } func (h handlers) MakeOrder(c *gin.Context) { + ctx := context.Background() // VALIDATION validate := validator.New(validator.WithRequiredStructEnabled()) req := makeOrderRequest{} err := c.ShouldBindJSON(&req) + meta := models.Meta{ + RequestStarted: time.Now().Unix(), + } + resp := models.Response{ + Status: status.STATUS_OK, + Meta: &meta, + } if err != nil { - //c.JSON(400, models.Response{Status: http.StatusBadRequest, Info: fmt.Sprintf("ERROR: %s", err.Error())}) + meta.RequestFinished = time.Now().Unix() + resp.Info = fmt.Sprintf("ERROR: %s", err.Error()) + resp.Status = status.STATUS_BAD_REQUEST + c.JSON(400, resp) return } validationErr := validate.Struct(req) if validationErr != nil { - //responseErr := validationErr.(validator.ValidationErrors)[0] - //c.JSON(http.StatusBadRequest, models.Response{Status: http.StatusBadRequest, Info: fmt.Sprintf("Validation Error: Field %s should be %s", responseErr.Field(), responseErr.Tag())}) + responseErr := validationErr.(validator.ValidationErrors)[0] + meta.RequestFinished = time.Now().Unix() + resp.Info = fmt.Sprintf("Error: %s", responseErr.Error()) + resp.Status = status.STATUS_BAD_REQUEST + c.JSON(http.StatusBadRequest, resp) return } - //kassaResult, serviceErr := services.MakeOrder(req.FuserId, req.Email, req.FullName, req.PhoneNumber) + kassaResult, serviceErr := services.MakeOrder(ctx, req.FuserId, req.Email, req.FullName, req.PhoneNumber) - //if serviceErr != nil { - // c.JSON(http.StatusInternalServerError, models.Response{Status: http.StatusInternalServerError, Info: fmt.Sprintf("Error: %s", serviceErr.Error())}) - // return - //} + if serviceErr != nil { + meta.RequestFinished = time.Now().Unix() + resp.Info = fmt.Sprintf("Error: %s", serviceErr.Error()) + resp.Status = status.STATUS_SERVER_ERROR + c.JSON(http.StatusInternalServerError, resp) + return + } - //c.JSON(http.StatusOK, kassaResult) + resp.Data = kassaResult + meta.RequestFinished = time.Now().Unix() + + c.JSON(http.StatusOK, resp) } type Handlers interface { diff --git a/services/order.go b/services/order.go index 47d0749..c5409e0 100644 --- a/services/order.go +++ b/services/order.go @@ -1,131 +1,122 @@ package services -//func GetTotal(fuserId int) float64 { -// rdb := internal.InitRedis() -// keys, _ := rdb.Keys(context.Background(), fmt.Sprintf("api.api_cart.%d.*", fuserId)).Result() +import ( + "context" + "fmt" + "relynolli-server/external/bitrix" + "relynolli-server/external/kassa" + "relynolli-server/external/kassa/Measure" + "relynolli-server/external/kassa/PaymentMode" + "relynolli-server/external/kassa/PaymentSubject" + "relynolli-server/external/kassa/VatCodes" + "relynolli-server/internal" + "relynolli-server/storage" + "strconv" + "strings" +) + +func addProductsToOrder(ctx context.Context, storage storage.StorageCart, api bitrix.Bitrix, fuserId, orderId int) error { + // //Получаем данные из корзины + // + items, err := storage.GetCartItems(ctx, int64(fuserId)) + if err != nil { + return err + } + + for _, product := range *items { + err = api.AddProductToOrder(orderId, int(product.ProductId), product.Product.Price.BASE, int(product.Quantity)) + if err != nil { + return err + } + } + return nil + +} + // -// result := []models.CatalogWithQuantityWeb{} -// -// for _, key := range keys { -// str, _ := rdb.Get(context.Background(), key).Result() -// item := models.CatalogWithQuantityWeb{} -// -// json.Unmarshal([]byte(str), &item) -// result = append(result, item) -// } -// -// sum := float64(0) -// -// for _, catalogItem := range result { -// sum = sum + catalogItem.Price["BASE"].(float64)*float64(catalogItem.Quantity) -// } -// -// return sum -//} -// -//type addProductsToOrderReq struct { -// ProductId int `db:"product_id"` -// PriceTypeId int `db:"price_type_id"` -// Quantity int `db:"quantity"` -// Price float64 `db:"price"` -//} -// -//func addProductsToOrder(api bitrix.Bitrix, fuserId int, orderId int) error { -// //Получаем данные из корзины -// -// cartItems := GetCartItems(fuserId) -// -// rdb := internal.InitRedis() -// rdb.Keys(context.Background(), "") -// -// for _, product := range cartItems { -// err := api.AddProductToOrder(orderId, product.Id, product.Price["BASE"].(float64), product.Quantity) -// if err != nil { -// return err -// } -// } -// return nil -// -//} -// -//func MakeOrder(fuserId int, email string, fullName string, phone string) (map[string]interface{}, error) { -// -// // Инициализируем api -// -// api := bitrix.Initialize() -// -// // 1. Создаем анонимного пользователя -// -// userId, _ := api.CreateAnonymousUser() -// -// // 2. Создаем заказ -// orderId, _ := api.CreateOrder(userId) -// -// // --- обновляем контакт пользователя -// order, orderErr := api.GetOrderInfo(orderId) -// if orderErr != nil { -// return nil, orderErr -// } -// clientId, _ := strconv.Atoi(order.Clients[0].EntityId) -// api.UpdateContact(clientId, email, fullName, phone) -// -// // 3. Добавляем элементы в корзину -// addProductErr := addProductsToOrder(api, fuserId, orderId) -// if addProductErr != nil { -// return nil, addProductErr -// } -// -// // 4. Получаем обновленный ресурс заказа -// order, _ = api.GetOrderInfo(orderId) -// -// // 5. Добавляем способ оплаты товара -// createPaymentError := api.CreatePayment(orderId, order.Price) -// -// if createPaymentError != nil { -// return nil, createPaymentError -// } -// -// // 6. Получаем ресурс оплаты и url для нее -// paymentData, _ := kassa.CreatePayment(orderId, order.Price, fullName, email, phone, getItemsForPayment(order)) -// -// insPaymentDataStmt := fmt.Sprintf(` -// insert into api_youkassa_payment (payment_id, order_id, link, status) -// values ('%s', -// '%s', -// '%s', -// '%s'); -// `, paymentData["id"].(string), -// orderId, -// paymentData["confirmation"].(map[string]interface{})["confirmation_url"].(string), -// paymentData["status"].(string), -// ) -// -// db := internal.InitDatabase() -// -// db.Execute(insPaymentDataStmt) -// -// return paymentData, nil -//} -// -//func getItemsForPayment(order *bitrix.OrderResource) []kassa.KassaReceiptItems { -// result := []kassa.KassaReceiptItems{} -// -// for _, basketItem := range order.BasketItems { -// quantity, _ := strconv.Atoi(strings.Split(basketItem.Quantity, ".")[0]) -// item := kassa.KassaReceiptItems{ -// Description: basketItem.Name, -// Amount: kassa.KassaAmount{ -// Value: fmt.Sprintf("%f", basketItem.Price), -// Currency: "RUB", -// }, -// VatCode: VatCodes.NDS_20, -// Quantity: fmt.Sprintf("%d", quantity), -// Measure: Measure.PIECE, -// PaymentSubject: PaymentSubject.COMMODITY, -// PaymentMode: PaymentMode.FULL_PAYMENT, -// } -// result = append(result, item) -// } -// -// return result -//} + +func MakeOrder(ctx context.Context, fuserId int, email string, fullName string, phone string) (*kassa.KassaResult, error) { + // + // Инициализируем api + s := storage.NewStorageCart() + + api := bitrix.Initialize() + + // 1. Создаем анонимного пользователя + + userId, _ := api.CreateAnonymousUser() + + // 2. Создаем заказ + orderId, _ := api.CreateOrder(userId) + + // --- обновляем контакт пользователя + order, orderErr := api.GetOrderInfo(orderId) + + if orderErr != nil { + return nil, orderErr + } + + clientId, _ := strconv.Atoi(order.Clients[0].EntityId) + err := api.UpdateContact(clientId, email, fullName, phone) + if err != nil { + return nil, err + } + + // 3. Добавляем элементы в корзину + addProductErr := addProductsToOrder(ctx, s, api, fuserId, orderId) + if addProductErr != nil { + return nil, addProductErr + } + + // 4. Получаем обновленный ресурс заказа + order, _ = api.GetOrderInfo(orderId) + + // 5. Добавляем способ оплаты товара + createPaymentError := api.CreatePayment(orderId, order.Price) + + if createPaymentError != nil { + return nil, createPaymentError + } + + // 6. Получаем ресурс оплаты и url для нее + paymentData, _ := kassa.CreatePayment(orderId, order.Price, fullName, email, phone, getItemsForPayment(order)) + + db := internal.InitDatabase().GetInstance() + + db.NewRaw(` + insert into api_youkassa_payment (payment_id, order_id, link, status) + values (?, ?, ?,? );`, + paymentData.Id, + orderId, + paymentData.Confirmation.ConfirmationUrl, + paymentData.Status).Exec(ctx) + + if err != nil { + return nil, err + } + + return paymentData, nil +} + +func getItemsForPayment(order *bitrix.OrderResource) []kassa.KassaReceiptItems { + result := []kassa.KassaReceiptItems{} + + for _, basketItem := range order.BasketItems { + quantity, _ := strconv.Atoi(strings.Split(basketItem.Quantity, ".")[0]) + item := kassa.KassaReceiptItems{ + Description: basketItem.Name, + Amount: kassa.KassaAmount{ + Value: fmt.Sprintf("%f", basketItem.Price), + Currency: "RUB", + }, + VatCode: VatCodes.NDS_20, + Quantity: fmt.Sprintf("%d", quantity), + Measure: Measure.PIECE, + PaymentSubject: PaymentSubject.COMMODITY, + PaymentMode: PaymentMode.FULL_PAYMENT, + } + result = append(result, item) + } + + return result +}