| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- package controller
- import (
- "bytes"
- "crawler/config"
- "crawler/model"
- "crawler/utility"
- "crypto/sha256"
- "encoding/base64"
- "encoding/json"
- "fmt"
- "github.com/gin-gonic/gin"
- "io"
- "log"
- "net/http"
- "reflect"
- "strconv"
- "strings"
- "time"
- )
- type G2AInterface interface {
- Init()
- Products(c *gin.Context)
- Order(c *gin.Context)
- }
- type G2A struct {
- ApiURL string
- IsTest bool
- Credentials config.G2ACredential
- G2AProductModel model.G2AProductModel
- G2AReportModel model.G2AReportModel
- G2AOrderModel model.G2AOrderModel
- G2AErrorModel model.G2AErrorModel
- G2AErrorResponse
- OrderDetail model.OrderDetailModel
- ShopConfig model.ShopConfigModel
- }
- // G2A 오류
- type G2AErrorResponse struct {
- Status any
- Message any
- Code any
- }
- // G2A 환경설정 조회
- func (this *G2A) Init() {
- this.IsTest = this.ShopConfig.G2AIsTest()
- if this.IsTest == true {
- this.Credentials = config.G2A.Sandbox
- } else {
- switch config.Env.DeveloperEnv {
- case config.LOCAL:
- this.Credentials = config.G2A.Sandbox
- case config.DEV:
- this.Credentials = config.G2A.Export
- default:
- log.Panic("G2A 설정이 잘못되었습니다.")
- }
- }
- var checksum = sha256.Sum256([]byte(fmt.Sprintf("%s%s%s",
- this.Credentials.Client.ID, this.Credentials.Email, this.Credentials.Client.Secret,
- )))
- this.Credentials.Hash = fmt.Sprintf("%x", checksum[:])
- }
- // G2A 상품 조회
- func (this *G2A) Products(c *gin.Context) {
- this.Init()
- fmt.Println("G2A 상품 수집을 시작합니다.")
- var (
- result = this.G2AProductModel.G2AProductResult
- total, insert, update, error, page = 0, 0, 0, 0, 1
- startTime = time.Now()
- endTime float64
- output = func(msg string) string {
- s := fmt.Sprintf("Total : %d\n", total)
- s += fmt.Sprintf("Error: %d\n", error)
- s += fmt.Sprintf("Insert: %d\n", insert)
- s += fmt.Sprintf("Update: %d\n", update)
- s += fmt.Sprintf("Page: %d\n", page)
- s += fmt.Sprintf("소요시간: %f초\n", endTime)
- return msg + "\n" + s
- }
- )
- for {
- this.ApiURL = fmt.Sprintf("%s/products?page=%d", this.Credentials.API, page)
- receivedDTO := this.HttpGetRequest()
- byteData, _ := json.Marshal(receivedDTO)
- err := json.Unmarshal(byteData, &result)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- // 상품 수집
- if len(result.Docs) > 0 {
- for i, product := range result.Docs {
- if this.G2AProductModel.IsExists(product.ID) {
- update++
- } else {
- insert++
- }
- if this.IsTest {
- result.Docs[i].IsTest = 1
- } else {
- result.Docs[i].IsTest = 0
- }
- }
- err = this.G2AProductModel.Insert(result.Docs)
- if err != nil {
- error++
- utility.SendMessageToG2AError(output("[G2A 상품수집 중 오류]"))
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- break
- }
- total += len(result.Docs)
- page++
- fmt.Printf("G2A 상품 수집 중... / total: %d, page: %d\n", total, page)
- } else {
- endTime = time.Since(startTime).Seconds()
- fmt.Printf("G2A 상품 수집 종료! / total: %d, page: %d\n", total, page)
- break
- }
- }
- // 통계 저장
- this.G2AReportModel.Insert(model.G2AReport{
- TotalCnt: total,
- InsertCnt: insert,
- UpdateCnt: update,
- ProcessAt: endTime,
- LastPage: page,
- })
- // 텔레그램 알림
- utility.SendMessageToG2AProduct(output("[G2A 상품수집 완료]"))
- c.JSON(http.StatusOK, gin.H{
- "total": total,
- "insert": insert,
- "update": update,
- "page": page,
- })
- }
- // G2A 품절 확인 후 상품 정보 갱신
- func (this *G2A) CheckOutOfStock(c *gin.Context) {
- this.Init()
- fmt.Println("G2A 상품 품절 확인")
- var (
- result = this.G2AProductModel.G2AProductResult
- productID = c.Query("id")
- inStock = "yes"
- )
- if productID == "" {
- c.JSON(http.StatusOK, gin.H{
- "inStock": "no",
- })
- return
- }
- this.ApiURL = fmt.Sprintf("%s/products?id=%s", this.Credentials.API, productID)
- receivedDTO := this.HttpGetRequest()
- byteData, _ := json.Marshal(receivedDTO)
- err := json.Unmarshal(byteData, &result)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- // 상품 정보 확인
- if len(result.Docs) > 0 {
- for _, product := range result.Docs {
- // 조회된 상품 정보를 한번 갱신한다.
- err = this.G2AProductModel.Update(product)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- if product.Qty <= 0 { // 재고가 0 이하면 품절이다.
- inStock = "no"
- break
- }
- }
- }
- c.JSON(http.StatusOK, gin.H{
- "inStock": inStock,
- })
- }
- // G2A 주문
- // Add an Order -> Pay for an order
- func (this *G2A) Order(c *gin.Context) {
- var requestKey = c.Query("requestKey")
- if requestKey == "" {
- c.JSON(http.StatusForbidden, G2AErrorResponse{
- Code: http.StatusForbidden,
- Message: "잘못된 요청입니다.",
- })
- return
- }
- // base64_decode 처리
- rawRequestKey, err := base64.URLEncoding.DecodeString(requestKey)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- requestKey = string(rawRequestKey)
- var (
- key = utility.MakeMD5(config.ENCRYPT_KEY)
- iv = config.ENCRYPT_IV
- )
- // 복호화
- decryptCode, err := utility.AesDecrypt(requestKey, key, iv)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- // 이미 주문한 주문건이 있는지 확인
- if this.G2AOrderModel.IsExists(decryptCode) {
- c.JSON(http.StatusOK, gin.H{
- "result": "OK",
- })
- return
- }
- id := strings.Split(decryptCode, "/")
- var (
- orderID, _ = strconv.Atoi(id[0])
- orderDetailID, _ = strconv.Atoi(id[1])
- productID = id[2]
- )
- if this.OrderDetail.IsExists(orderID, orderDetailID) == false {
- c.JSON(http.StatusNotFound, G2AErrorResponse{
- Code: http.StatusNotFound,
- Message: "비 정상적인 주문서 입니다.",
- })
- return
- }
- //orderDetail, err := this.OrderDetail.Info(orderID, orderDetailID)
- //utility.Check(err, config.ERROR_LOG_PATH_G2A)
- tmpProductID, _ := strconv.Atoi(productID)
- G2AProductInfo, err := this.G2AProductModel.Info(tmpProductID)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- var params = this.G2AOrderModel.G2AOrderParams
- params.ProductID = productID
- params.Currency = "KRW"
- params.MaxPrice = G2AProductInfo.RetailMinPrice
- if err := c.ShouldBind(¶ms); err != nil {
- c.JSON(http.StatusBadRequest, G2AErrorResponse{
- Code: http.StatusBadRequest,
- Message: err.Error(),
- })
- return
- }
- this.Init()
- // 주문을 요청한다.
- this.ApiURL = fmt.Sprintf("%s/order", this.Credentials.API)
- var (
- orderResult = this.G2AOrderModel.G2AOrderResult
- receivedDTO = this.HttpPostRequest(params)
- typeName = reflect.TypeOf(receivedDTO).Name()
- )
- if typeName == "G2AErrorModel" {
- c.JSON(http.StatusBadRequest, receivedDTO)
- return
- }
- byteData, _ := json.Marshal(receivedDTO)
- err = json.Unmarshal(byteData, &orderResult)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- // 주문 번호를 결제 처리한다.
- this.ApiURL = fmt.Sprintf("%s/order/pay/%s", this.Credentials.API, orderResult.OrderID)
- var orderPayResult = this.G2AOrderModel.G2AOrderPayResult
- receivedDTO = this.HttpPutRequest()
- typeName = reflect.TypeOf(receivedDTO).Name()
- if typeName == "G2AErrorModel" {
- c.JSON(http.StatusBadRequest, receivedDTO)
- return
- }
- byteData, _ = json.Marshal(receivedDTO)
- err = json.Unmarshal(byteData, &orderPayResult)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- // 결제 확인
- if orderPayResult.Status == false {
- c.JSON(http.StatusOK, gin.H{
- "message": "결제에 실패하였습니다.",
- })
- return
- }
- // 결제 및 주문이 제대로 되었는지 확인한다.
- this.ApiURL = fmt.Sprintf("%s/order/details/%s", this.Credentials.API, orderResult.OrderID)
- var orderDetailResult = this.G2AOrderModel.G2AOrderDetailResult
- receivedDTO = this.HttpGetRequest()
- typeName = reflect.TypeOf(receivedDTO).Name()
- if typeName == "G2AErrorModel" {
- c.JSON(http.StatusBadRequest, receivedDTO)
- return
- }
- byteData, _ = json.Marshal(receivedDTO)
- err = json.Unmarshal(byteData, &orderDetailResult)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- time.Sleep(1 * time.Second)
- // 결제 및 주문이 제대로 되었는지 확인한다.
- this.ApiURL = fmt.Sprintf("%s/order/key/%s", this.Credentials.API, orderResult.OrderID)
- var orderKeyResult = this.G2AOrderModel.G2AOrderKeyResult
- receivedDTO = this.HttpGetRequest()
- typeName = reflect.TypeOf(receivedDTO).Name()
- if typeName == "G2AErrorModel" {
- c.JSON(http.StatusBadRequest, receivedDTO)
- return
- }
- byteData, _ = json.Marshal(receivedDTO)
- err = json.Unmarshal(byteData, &orderKeyResult)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- // 주문 기록 저장
- this.G2AOrderModel.Insert(model.G2AOrder{
- RequestKey: decryptCode,
- RequestOrderID: orderID,
- RequestOrderDetailID: orderDetailID,
- Status: orderDetailResult.Status,
- OrderID: orderResult.OrderID,
- ProductID: params.ProductID,
- Code: orderKeyResult.Key,
- Price: orderDetailResult.Price,
- Currency: orderDetailResult.Currency,
- TransactionID: orderPayResult.TransactionID,
- })
- // 텔레그램 알림
- utility.SendMessageToG2AOrder(func() string {
- msg := fmt.Sprintf("[G2A 주문완료]\n")
- msg += fmt.Sprintf("Order ID : %d\n", orderID)
- msg += fmt.Sprintf("Order Detail ID : %d\n", orderDetailID)
- msg += fmt.Sprintf("Status : %s\n", orderDetailResult.Status)
- msg += fmt.Sprintf("Code: %s\n", orderKeyResult.Key)
- msg += fmt.Sprintf("Price: %f\n", orderDetailResult.Price)
- msg += fmt.Sprintf("Currency: %s\n", orderDetailResult.Currency)
- return msg
- }())
- c.JSON(http.StatusOK, gin.H{
- "result": "OK",
- })
- }
- // HTTP Get CALL
- func (this *G2A) HttpGetRequest() interface{} {
- req, err := http.NewRequest("GET", this.ApiURL, nil)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- req.Header.Set("Accept", "application/json")
- req.Header.Add("Authorization", fmt.Sprintf("%s, %s", this.Credentials.Client.ID, this.Credentials.Hash))
- client := &http.Client{
- Timeout: 8 * time.Second,
- }
- res, err := client.Do(req)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- defer res.Body.Close()
- data, err := io.ReadAll(res.Body)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- if res.StatusCode != 200 {
- return this.SetG2AError(data, nil)
- }
- var receivedDTO interface{}
- err = json.Unmarshal(data, &receivedDTO)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- return receivedDTO
- }
- // HTTP Post CALL
- func (this *G2A) HttpPostRequest(params model.G2AOrderParams) interface{} {
- payloadBytes, err := json.Marshal(params)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- body := bytes.NewReader(payloadBytes)
- req, err := http.NewRequest("POST", this.ApiURL, body)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- req.Header.Set("Content-Type", "application/json")
- req.Header.Add("Authorization", fmt.Sprintf("%s, %s", this.Credentials.Client.ID, this.Credentials.Hash))
- client := &http.Client{
- Timeout: 8 * time.Second,
- }
- res, err := client.Do(req)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- defer res.Body.Close()
- data, err := io.ReadAll(res.Body)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- if res.StatusCode != 200 {
- return this.SetG2AError(data, payloadBytes)
- }
- var receivedDTO interface{}
- err = json.Unmarshal(data, &receivedDTO)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- return receivedDTO
- }
- // HTTP Put CALL
- func (this *G2A) HttpPutRequest() interface{} {
- req, err := http.NewRequest("PUT", this.ApiURL, nil)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- req.Header.Set("Content-type", "application/json")
- req.Header.Set("Content-Length", "0")
- req.Header.Add("Authorization", fmt.Sprintf("%s, %s", this.Credentials.Client.ID, this.Credentials.Hash))
- fmt.Println(fmt.Sprintf("%s, %s", this.Credentials.Client.ID, this.Credentials.Hash))
- client := &http.Client{
- Timeout: 8 * time.Second,
- }
- res, err := client.Do(req)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- defer res.Body.Close()
- data, err := io.ReadAll(res.Body)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- if res.StatusCode != 200 {
- return this.SetG2AError(data, nil)
- }
- var receivedDTO interface{}
- err = json.Unmarshal(data, &receivedDTO)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- return receivedDTO
- }
- // G2A 주문 API 오류 발생 처리
- func (this *G2A) SetG2AError(data []byte, params interface{}) model.G2AErrorModel {
- var G2AError = this.G2AErrorModel
- err := json.Unmarshal(data, &G2AError)
- utility.Check(err, config.ERROR_LOG_PATH_G2A)
- G2AError.URL = this.ApiURL
- if params != nil {
- G2AError.Params = string(params.([]byte))
- }
- G2AError.Save()
- // 텔레그램 알림
- if err != nil {
- utility.SendMessageToG2AError(fmt.Sprintf("[G2A 주문 중 오류]\n%s", err.Error()))
- }
- return G2AError
- }
|