From 2e6aa172063d75e24e74245ca8051f081146c9e1 Mon Sep 17 00:00:00 2001 From: Abdulah Wahdi Date: Mon, 25 Jan 2021 14:16:28 +0700 Subject: [PATCH] Initial Commit (#1) --- .editorconfig | 21 ++++ .gitignore | 3 + LICENSE | 13 +++ Makefile | 7 ++ cicil.go | 190 +++++++++++++++++++++++++++++++++++ cicil_test.go | 210 +++++++++++++++++++++++++++++++++++++++ example/main.go | 106 ++++++++++++++++++++ go.mod | 5 + go.sum | 10 ++ http.go | 58 +++++++++++ logger.go | 54 ++++++++++ mocks/CicilService.go | 97 ++++++++++++++++++ request.go | 77 ++++++++++++++ response.go | 18 ++++ service.go | 8 ++ sonar-project.properties | 3 + 16 files changed, 880 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 cicil.go create mode 100644 cicil_test.go create mode 100644 example/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 http.go create mode 100644 logger.go create mode 100644 mocks/CicilService.go create mode 100644 request.go create mode 100644 response.go create mode 100644 service.go create mode 100644 sonar-project.properties diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..781eee4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig coding styles definitions. For more information about the +# properties used in this file, please see the EditorConfig documentation: +# http://editorconfig.org/ + +# indicate this is the root of the project +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f1c927 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +.vscode/ +.DS_Store diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7845a16 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright 2021 Bhinneka.com + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3805ccc --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: test cover + +test: + go test -race + +cover: + go test -race -coverprofile=coverage.txt -covermode=atomic diff --git a/cicil.go b/cicil.go new file mode 100644 index 0000000..fbec3c5 --- /dev/null +++ b/cicil.go @@ -0,0 +1,190 @@ +package cicil + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + b64 "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "strings" + "time" +) + +// cicil entry struick for cicil +type cicil struct { + MerchantID string + APIKey string + MerchantSecret string + BaseURL string + client *cicilHttpClient + *logger +} + +/*New Function, create cicil pointer +Required parameter : +1. Your MerchantID (this from Team cicil) +2. Your MerchantSecret (this from Team cicil) +3. Your APIKey (this from Team cicil) +3. BaseURL (hit to endpoint ex: https://sandbox-api.cicil.dev/v1 for sandbox. +this value based on https://docs.cicil.app/#introduction) +*/ +func New(baseUrl string, apiKey string, merchantId string, merchantSecret string, timeout time.Duration) *cicil { + httpRequest := newRequest(timeout) + return &cicil{ + APIKey: apiKey, + MerchantID: merchantId, + MerchantSecret: merchantSecret, + BaseURL: baseUrl, + client: httpRequest, + logger: newLogger(), + } +} + +func (c *cicil) call(method string, path string, body io.Reader, v interface{}, headers map[string]string) error { + c.info().Println("Starting http call..") + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + + path = fmt.Sprintf("%s%s", c.BaseURL, path) + return c.client.exec(method, path, body, v, headers) +} + +func (c *cicil) GetToken() (digest string, dateNowReturn string) { + dateNow := time.Now().Format(time.RFC1123) + formattedMessage := fmt.Sprintf("%s%s", c.MerchantSecret, dateNow) + // Create a new HMAC by defining the hash type and the key (as byte array) + h := hmac.New(sha256.New, []byte(c.APIKey)) + // Write Data to it + h.Write([]byte(formattedMessage)) + + // Get result and encode as hexadecimal string + digestData := hex.EncodeToString(h.Sum(nil)) + basicData := fmt.Sprintf("%s:%s", c.MerchantID, digestData) + output := b64.URLEncoding.EncodeToString([]byte(basicData)) + return output, dateNow +} + +func (c *cicil) GetCheckoutURL(param CheckoutRequest) (resp CheckoutResponse, err error) { + c.info().Println("Starting Get Order URL Cicil") + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + if err != nil { + c.error().Println(err.Error()) + } + }() + var getCheckoutURLResponse CheckoutResponse + + // get auth data + getDiggest, getDate := c.GetToken() + + // set header + headers := make(map[string]string) + headers["Content-Type"] = "application/json" + headers["Date"] = getDate + headers["Authorization"] = fmt.Sprintf("Basic %s", getDiggest) + + pathGetOrderURL := "po" + //Marshal Order + payload, errPayload := json.Marshal(param) + if errPayload != nil { + return getCheckoutURLResponse, err + } + + err = c.call("POST", pathGetOrderURL, bytes.NewBuffer(payload), &getCheckoutURLResponse, headers) + if err != nil { + return getCheckoutURLResponse, err + } + if len(getCheckoutURLResponse.Message) > 0 { + err = errors.New(getCheckoutURLResponse.Message) + return getCheckoutURLResponse, err + } + + return getCheckoutURLResponse, nil +} + +func (c *cicil) SetCancelOrder (param CancelOrderRequest) (resp CancelOrderResponse, err error) { + c.info().Println("Starting Set Cancel Order Cicil") + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + if err != nil { + c.error().Println(err.Error()) + } + }() + var setCancelOrderResponse CancelOrderResponse + + // get auth data + getDiggest, getDate := c.GetToken() + + // set header + headers := make(map[string]string) + headers["Content-Type"] = "application/json" + headers["Date"] = getDate + headers["Authorization"] = fmt.Sprintf("Basic %s", getDiggest) + + pathGetOrderURL := "po/cancel" + //Marshal Order + payload, errPayload := json.Marshal(param) + if errPayload != nil { + return setCancelOrderResponse, err + } + + err = c.call("POST", pathGetOrderURL, bytes.NewBuffer(payload), &setCancelOrderResponse, headers) + if err != nil { + return setCancelOrderResponse, err + } + if len(setCancelOrderResponse.Message) > 0 { + err = errors.New(setCancelOrderResponse.Message) + return setCancelOrderResponse, err + } + + return setCancelOrderResponse, nil +} + +func (c *cicil) UpdateStatus (param UpdateStatusRequest) (resp UpdateStatusResponse, err error) { + c.info().Println("Starting Set Cancel Order Cicil") + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + if err != nil { + c.error().Println(err.Error()) + } + }() + var updateStatusResponse UpdateStatusResponse + + // get auth data + getDiggest, getDate := c.GetToken() + + // set header + headers := make(map[string]string) + headers["Content-Type"] = "application/json" + headers["Date"] = getDate + headers["Authorization"] = fmt.Sprintf("Basic %s", getDiggest) + + pathGetOrderURL := "po/update" + //Marshal Order + payload, errPayload := json.Marshal(param) + if errPayload != nil { + return updateStatusResponse, err + } + + err = c.call("POST", pathGetOrderURL, bytes.NewBuffer(payload), &updateStatusResponse, headers) + if err != nil { + return updateStatusResponse, err + } + if len(updateStatusResponse.Message) > 0 { + err = errors.New(updateStatusResponse.Message) + return updateStatusResponse, err + } + + return updateStatusResponse, nil +} \ No newline at end of file diff --git a/cicil_test.go b/cicil_test.go new file mode 100644 index 0000000..7a016df --- /dev/null +++ b/cicil_test.go @@ -0,0 +1,210 @@ +package cicil + +import ( + "encoding/json" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +const ( + BaseURL = "https://sandbox-api.cicil.dev/v1" + MerchantID = "YOUR_MERCHANT_ID" + MerchantSecret = "YOUR_MERCHANT_SERCRET" + APIKey = "YOUR_API_KEY" + DefaultTimeout = 20 * time.Second +) + +func TestCicilCreation(t *testing.T){ + t.Run("Test Cicil Creation", func(t *testing.T) { + //construct cicil + //and add required parameters + cic := New(BaseURL,APIKey, MerchantID, MerchantSecret,DefaultTimeout) + + if cic == nil { + t.Error("Cannot Call Cicil Constructor") + } + }) +} + +func TestCicil_GetCheckoutURL(t *testing.T) { + orderData := []byte(`{"transaction": {"total_amount": 13119000,"transaction_id": "ORD10111808","item_list": [{"item_id":"SKU101112","type": "product","name":"Notebook CICIL C12","price":12999000,"category":"laptop","url":"https://www.tokocicil.com/product/sku101112","quantity":1,"seller_id":"tokocicil-official"},{"item_id":"SKU131415","type": "product","name":"Sticker Aja","price":60000,"category":"accessories","url":"https://www.tokocicil.com/product/sku131415","quantity":2},{"item_id":"insurance","type": "fee","name":"Insurance Fee","price":5000,"quantity":1},{"item_id":"shipment_cost","type": "shipment_cost","name":"Shipping Fee","price":45000,"quantity":1},{"item_id":"CICIL1212","type": "discount","name":"Promo Harbolnas CICIL","price":-50000,"quantity":1}]},"buyer": {"fullname": "John Doe","email": "john.doe@mail.com","phone": "085322984060","address": "Jl. Sd Inpres RT.003/RW.006 No.174A 13950 Cakung, Pulogebang","city": "Jakarta Timur","district": "JK","postal_code": "11630","company": "Cicil","country": "ID"},"shipment": {"shipment_provider": "Flat rate","shipping_price": 40000,"shipping_tax": 0,"name": "John Doe","address": "Jl. Sd Inpres RT.003/RW.006 No.174A 13950 Cakung, Pulogebang","city": "Jakarta Timur","district": "Jakarta Timur","postal_code": "11630","phone": "085322984060","company": "Cicil","country": "ID"},"push_url": "https://api.tokocicil.com/update","redirect_url": "https://toko.cicil.dev","back_url": "https://toko.cicil.dev"}`) + + t.Run("Test Cicil Get Order URL", func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if r.Method != "POST" { + t.Errorf("Expected ‘POST’ request, got ‘%s’", r.Method) + } + + w.Header().Set("Content-Type", "application-json; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{ "message": "", "po_number": "PO210122-143655", "status": "success", "url": "https://sandbox-staging.cicil.dev/po/UE8yMTAxMjItMTQzNjU1" }`)) + })) + //close server + defer ts.Close() + + //construct cicil + amm := New(ts.URL, APIKey, MerchantID, MerchantSecret,DefaultTimeout) + var order CheckoutRequest + err := json.Unmarshal(orderData, &order) + if err != nil { + t.Error("Cannot Unmarshal Order JSON data") + } + + _, errGetOrderURL := amm.GetCheckoutURL(order) + if errGetOrderURL != nil { + t.Errorf("GetOrderURL() returned an error: %s", errGetOrderURL.Error()) + } + }) + t.Run("Test Negative Case Cicil Get Order URL", func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if r.Method != "POST" { + t.Errorf("Expected ‘POST’ request, got ‘%s’", r.Method) + } + + w.Header().Set("Content-Type", "application-json; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"message":"invalid parameter on order request"}`)) + + })) + //close server + defer ts.Close() + + //construct cicil + amm := New(ts.URL, APIKey, MerchantID, MerchantSecret,DefaultTimeout) + var order CheckoutRequest + err := json.Unmarshal(orderData, &order) + if err != nil { + t.Error("Cannot Unmarshal Order JSON data") + } + + _, errGetOrderURL := amm.GetCheckoutURL(order) + if errGetOrderURL != nil { + assert.Error(t, errGetOrderURL) + } + }) +} + +func TestCicil_SetCancelOrder(t *testing.T) { + orderData := []byte(`{"po_number": "PO181112-123456","reason": "out of stock","cancelled_by": "tokocicil","total_amount":13119000,"transaction_id":"ORD10111808"}`) + + t.Run("Test Cicil Get Order URL", func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if r.Method != "POST" { + t.Errorf("Expected ‘POST’ request, got ‘%s’", r.Method) + } + + w.Header().Set("Content-Type", "application-json; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"status":"success","message":""}`)) + })) + //close server + defer ts.Close() + + //construct cicil + cic := New(ts.URL, APIKey, MerchantID, MerchantSecret,DefaultTimeout) + var cancelOrder CancelOrderRequest + err := json.Unmarshal(orderData, &cancelOrder) + if err != nil { + t.Error("Cannot Unmarshal Order JSON data") + } + + _, errCancelOrder := cic.SetCancelOrder(cancelOrder) + if errCancelOrder != nil { + t.Errorf("SetCancelOrder() returned an error: %s", errCancelOrder.Error()) + } + }) + t.Run("Test Negative Case Cicil Get Order URL", func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if r.Method != "POST" { + t.Errorf("Expected ‘POST’ request, got ‘%s’", r.Method) + } + + w.Header().Set("Content-Type", "application-json; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"message":"invalid parameter on order request"}`)) + + })) + //close server + defer ts.Close() + + //construct cicil + amm := New(ts.URL, APIKey, MerchantID, MerchantSecret,DefaultTimeout) + var cancelOrder CancelOrderRequest + err := json.Unmarshal(orderData, &cancelOrder) + if err != nil { + t.Error("Cannot Unmarshal Order JSON data") + } + + _, errCancelOrder := amm.SetCancelOrder(cancelOrder) + if errCancelOrder != nil { + assert.Error(t, errCancelOrder) + } + }) +} + +func TestCicil_UpdateStatus(t *testing.T) { + orderData := []byte(`{"po_number": "PO181112-123456","po_status": "delivered","transaction_id":"ORD10111808"}`) + + t.Run("Test Cicil Update Status", func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if r.Method != "POST" { + t.Errorf("Expected ‘POST’ request, got ‘%s’", r.Method) + } + + w.Header().Set("Content-Type", "application-json; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"status":"success","message":""}`)) + })) + //close server + defer ts.Close() + + //construct cicil + amm := New(ts.URL, APIKey, MerchantID, MerchantSecret,DefaultTimeout) + var updateStatus UpdateStatusRequest + err := json.Unmarshal(orderData, &updateStatus) + if err != nil { + t.Error("Cannot Unmarshal Order JSON data") + } + + _, errUpdateStatus := amm.UpdateStatus(updateStatus) + if errUpdateStatus != nil { + t.Errorf("GetOrderURL() returned an error: %s", errUpdateStatus.Error()) + } + }) + t.Run("Test Negative Case Cicil Update Status", func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if r.Method != "POST" { + t.Errorf("Expected ‘POST’ request, got ‘%s’", r.Method) + } + + w.Header().Set("Content-Type", "application-json; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"message":"invalid parameter on order request"}`)) + + })) + //close server + defer ts.Close() + + //construct cicil + amm := New(ts.URL, APIKey, MerchantID, MerchantSecret,DefaultTimeout) + var updateStatus UpdateStatusRequest + err := json.Unmarshal(orderData, &updateStatus) + if err != nil { + t.Error("Cannot Unmarshal Order JSON data") + } + + _, errUpdateStatus := amm.UpdateStatus(updateStatus) + if errUpdateStatus != nil { + assert.Error(t, errUpdateStatus) + } + }) +} diff --git a/example/main.go b/example/main.go new file mode 100644 index 0000000..bc3bd47 --- /dev/null +++ b/example/main.go @@ -0,0 +1,106 @@ +package main + +import ( + "encoding/json" + "fmt" + "github.com/abdulahwahdi/cicil-go" + "time" +) + +const ( + BaseURL = "https://sandbox-api.cicil.dev/v1" + MerchantID = "YOUR_MERCHANT_ID" + MerchantSecret = "YOUR_MERCHANT_SERCRET" + APIKey = "YOUR_API_KEY" + DefaultTimeout = 20 * time.Second +) + +func main() { + newCicil := cicil.New(BaseURL,APIKey, MerchantID, MerchantSecret,DefaultTimeout) + SampleCheckout(newCicil) +} + +func SampleCheckout(cic cicil.CicilService) { + dataSample := `{ + "transaction": { + "total_amount": 13119000, + "transaction_id": "ORD10111808", + "item_list": [ + { + "item_id":"SKU101112", + "type": "product", + "name":"Notebook CICIL C12", + "price":12999000, + "category":"laptop", + "url":"https://www.tokocicil.com/product/sku101112", + "quantity":1, + "seller_id":"tokocicil-official" + }, + { + "item_id":"SKU131415", + "type": "product", + "name":"Sticker Aja", + "price":60000, + "category":"accessories", + "url":"https://www.tokocicil.com/product/sku131415", + "quantity":2 + }, + { + "item_id":"insurance", + "type": "fee", + "name":"Insurance Fee", + "price":5000, + "quantity":1 + }, + { + "item_id":"shipment_cost", + "type": "shipment_cost", + "name":"Shipping Fee", + "price":45000, + "quantity":1 + }, + { + "item_id":"CICIL1212", + "type": "discount", + "name":"Promo Harbolnas CICIL", + "price":-50000, + "quantity":1 + } + ] + }, + "buyer": { + "fullname": "John Doe", + "email": "john.doe@mail.com", + "phone": "085322984060", + "address": "Jl. Sd Inpres RT.003/RW.006 No.174A 13950 Cakung, Pulogebang", + "city": "Jakarta Timur", + "district": "JK", + "postal_code": "11630", + "company": "Cicil", + "country": "ID" + }, + "shipment": { + "shipment_provider": "Flat rate", + "shipping_price": 40000, + "shipping_tax": 0, + "name": "John Doe", + "address": "Jl. Sd Inpres RT.003/RW.006 No.174A 13950 Cakung, Pulogebang", + "city": "Jakarta Timur", + "district": "Jakarta Timur", + "postal_code": "11630", + "phone": "085322984060", + "company": "Cicil", + "country": "ID" + }, + "push_url": "https://api.tokocicil.com/update", + "redirect_url": "https://toko.cicil.dev", + "back_url": "https://toko.cicil.dev" + }` + dataSampleStruct := cicil.CheckoutRequest{} + json.Unmarshal([]byte(dataSample), &dataSampleStruct) + respGetCheckoutUrl, errGetCheckoutUrl := cic.GetCheckoutURL(dataSampleStruct) + if errGetCheckoutUrl != nil { + fmt.Println(errGetCheckoutUrl.Error()) + } + fmt.Println(respGetCheckoutUrl) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..516ca54 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/abdulahwahdi/cicil-go + +go 1.13 + +require github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b380ae4 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/http.go b/http.go new file mode 100644 index 0000000..6d6c669 --- /dev/null +++ b/http.go @@ -0,0 +1,58 @@ +package cicil + +import ( + "encoding/json" + "io" + "net/http" + "time" +) + +// httpRequest data model +type cicilHttpClient struct { + httpClient *http.Client +} + +// newRequest function for intialize httpRequest object +// Paramter, timeout in time.Duration +func newRequest(timeout time.Duration) *cicilHttpClient { + return &cicilHttpClient{ + httpClient: &http.Client{Timeout: time.Second * timeout}, + } +} + +// newReq function for initalize http request, +// paramters, http method, uri path, body, and headers +func (c *cicilHttpClient) newReq(method string, fullPath string, body io.Reader, headers map[string]string) (*http.Request, error) { + req, err := http.NewRequest(method, fullPath, body) + if err != nil { + return nil, err + } + + for key, value := range headers { + req.Header.Set(key, value) + } + + return req, nil +} + +// exec private function for call http request +func (c *cicilHttpClient) exec(method, path string, body io.Reader, v interface{}, headers map[string]string) error { + req, err := c.newReq(method, path, body, headers) + + if err != nil { + return err + } + + res, errHttpClient := c.httpClient.Do(req) + if errHttpClient != nil { + return err + } + + defer res.Body.Close() + + if v != nil { + return json.NewDecoder(res.Body).Decode(v) + } + + return nil +} \ No newline at end of file diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..dd85961 --- /dev/null +++ b/logger.go @@ -0,0 +1,54 @@ +package cicil + +import ( + "io" + "io/ioutil" + "log" + "os" +) + +//logger +type logger struct { + traceHandle io.Writer + infoHandle io.Writer + warningHandle io.Writer + errorHandle io.Writer +} + +//newLogger, logger constructor +func newLogger() *logger { + return &logger{ + traceHandle: ioutil.Discard, + infoHandle: os.Stdout, + warningHandle: os.Stdout, + errorHandle: os.Stderr, + } +} + +//trace +func (l *logger) trace() *log.Logger { + return log.New(l.traceHandle, + "TRACE: ", + log.Ldate|log.Ltime|log.Lshortfile) +} + +//info +func (l *logger) info() *log.Logger { + return log.New(l.infoHandle, + "INFO: ", + log.Ldate|log.Ltime|log.Lshortfile) +} + +//warning +func (l *logger) warning() *log.Logger { + return log.New(l.warningHandle, + "WARNING: ", + log.Ldate|log.Ltime|log.Lshortfile) +} + +//error +func (l *logger) error() *log.Logger { + return log.New(l.errorHandle, + "ERROR: ", + log.Ldate|log.Ltime|log.Lshortfile) +} \ No newline at end of file diff --git a/mocks/CicilService.go b/mocks/CicilService.go new file mode 100644 index 0000000..668f60f --- /dev/null +++ b/mocks/CicilService.go @@ -0,0 +1,97 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package mocks + +import ( + cicil "github.com/abdulahwahdi/cicil-go" + mock "github.com/stretchr/testify/mock" +) + +// CicilService is an autogenerated mock type for the CicilService type +type CicilService struct { + mock.Mock +} + +// GetCheckoutURL provides a mock function with given fields: param +func (_m *CicilService) GetCheckoutURL(param cicil.CheckoutRequest) (cicil.CheckoutResponse, error) { + ret := _m.Called(param) + + var r0 cicil.CheckoutResponse + if rf, ok := ret.Get(0).(func(cicil.CheckoutRequest) cicil.CheckoutResponse); ok { + r0 = rf(param) + } else { + r0 = ret.Get(0).(cicil.CheckoutResponse) + } + + var r1 error + if rf, ok := ret.Get(1).(func(cicil.CheckoutRequest) error); ok { + r1 = rf(param) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetToken provides a mock function with given fields: +func (_m *CicilService) GetToken() (string, string) { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + var r1 string + if rf, ok := ret.Get(1).(func() string); ok { + r1 = rf() + } else { + r1 = ret.Get(1).(string) + } + + return r0, r1 +} + +// SetCancelOrder provides a mock function with given fields: param +func (_m *CicilService) SetCancelOrder(param cicil.CancelOrderRequest) (cicil.CancelOrderResponse, error) { + ret := _m.Called(param) + + var r0 cicil.CancelOrderResponse + if rf, ok := ret.Get(0).(func(cicil.CancelOrderRequest) cicil.CancelOrderResponse); ok { + r0 = rf(param) + } else { + r0 = ret.Get(0).(cicil.CancelOrderResponse) + } + + var r1 error + if rf, ok := ret.Get(1).(func(cicil.CancelOrderRequest) error); ok { + r1 = rf(param) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateStatus provides a mock function with given fields: param +func (_m *CicilService) UpdateStatus(param cicil.UpdateStatusRequest) (cicil.UpdateStatusResponse, error) { + ret := _m.Called(param) + + var r0 cicil.UpdateStatusResponse + if rf, ok := ret.Get(0).(func(cicil.UpdateStatusRequest) cicil.UpdateStatusResponse); ok { + r0 = rf(param) + } else { + r0 = ret.Get(0).(cicil.UpdateStatusResponse) + } + + var r1 error + if rf, ok := ret.Get(1).(func(cicil.UpdateStatusRequest) error); ok { + r1 = rf(param) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/request.go b/request.go new file mode 100644 index 0000000..1d5ab75 --- /dev/null +++ b/request.go @@ -0,0 +1,77 @@ +package cicil + +type CheckoutRequest struct { + Transaction Transaction `json:"transaction"` + PushURL string `json:"push_url"` + RedirectURL string `json:"redirect_url"` + Buyer Buyer `json:"buyer"` + Shipment Shipment `json:"shipment"` + SellerList []SellerList `json:"seller_list,omitempty"` + BackURL string `json:"back_url"` +} + +type Transaction struct { + TotalAmount int `json:"total_amount"` + TransactionID string `json:"transaction_id"` + ItemLists []ItemList `json:"item_list"` +} + +type ItemList struct { + ItemID string `json:"item_id"` + Type string `json:"type"` + Name string `json:"name"` + Price int `json:"price"` + Quantity int `json:"quantity"` + Category string `json:"category,omitempty"` + Url string `json:"url,omitempty"` + SellerID string `json:"seller_id,omitempty"` +} + +type Buyer struct { + Fullname string `json:"fullname"` + Email string `json:"email"` + Phone string `json:"phone"` + Address string `json:"address"` + City string `json:"city"` + District string `json:"district"` + PostalCode string `json:"postal_code"` + Company string `json:"company"` + Country string `json:"country"` +} + +type Shipment struct { + ShipmentProvider string `json:"shipment_provider"` + ShipmentPrice int `json:"shipment_price"` + ShipmentTax int `json:"shipment_tax"` + Address string `json:"address"` + City string `json:"city"` + District string `json:"district"` + PostalCode string `json:"postal_code"` + Phone string `json:"phone"` + Company string `json:"company"` + Name string `json:"name"` + Country string `json:"country"` +} + +type SellerList struct { + Name string `json:"name"` + Email string `json:"email"` + Phone string `json:"phone"` + Url string `json:"url"` +} + +type CancelOrderRequest struct { + PONumber string `json:"po_number"` + Reason string `json:"reason"` + CancelledBy string `json:"cancelled_by"` + TotalAmount int `json:"total_amount"` + TransactionID string `json:"transaction_id"` +} + +type UpdateStatusRequest struct { + PONumber string `json:"po_number"` + POStatus string `json:"po_status"` + TransactionID string `json:"transaction_id"` + Shipment Shipment `json:"shipment,omitempty"` + Reason string `json:"reason,omitempty"` +} diff --git a/response.go b/response.go new file mode 100644 index 0000000..54ee04a --- /dev/null +++ b/response.go @@ -0,0 +1,18 @@ +package cicil + +type CheckoutResponse struct { + Status string `json:"status"` + Message string `json:"message"` + PoNumber string `json:"po_number"` + Url string `json:"url"` +} + +type CancelOrderResponse struct { + Status string `json:"status"` + Message string `json:"message"` +} + +type UpdateStatusResponse struct { + Status string `json:"status"` + Message string `json:"message"` +} \ No newline at end of file diff --git a/service.go b/service.go new file mode 100644 index 0000000..f916acd --- /dev/null +++ b/service.go @@ -0,0 +1,8 @@ +package cicil + +type CicilService interface { + GetToken() (digest string, dateNowReturn string) + GetCheckoutURL(param CheckoutRequest) (resp CheckoutResponse, err error) + SetCancelOrder(param CancelOrderRequest) (resp CancelOrderResponse, err error) + UpdateStatus (param UpdateStatusRequest) (resp UpdateStatusResponse, err error) +} diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..91420c5 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,3 @@ +sonar.tests=. +sonar.test.inclusions=**/**_test.go +sonar.go.coverage.reportPaths=coverage.txt