Skip to content

Commit

Permalink
Merge pull request #10 from Xaxxis/main
Browse files Browse the repository at this point in the history
v1.3.0 Comply midtrans error with Golang error interface
  • Loading branch information
Xaxxis authored Apr 6, 2022
2 parents 9b3634e + 443d3f3 commit 323c890
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 16 deletions.
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -616,14 +616,31 @@ midtrans.DefaultGoHttpClient = &http.Client{
## 4. Handling Error
When using function that result in Midtrans API call e.g: c.ChargeTransaction(...) or s.CreateTransaction(...) there's a chance it may throw error (Midtrans [Error object](/error.go)), the error object will contains below properties that can be used as information to your error handling logic:
```go
_, err = c.chargeTransaction(param);
_, err = c.chargeTransaction(param)
if err != nil {
msg := err.GetMessage() // general message error
msg := err.Error() // general message error
stsCode := err.GetStatusCode() // HTTP status code e.g: 400, 401, etc.
rawApiRes := err.GetRawApiResponse() // raw Go HTTP response object
rawErr := err.GetRawError() // raw Go err object
rawErr := err.Unwrap() // raw Go err object
}
```
midtrans.error complies with [Go standard error](https://go.dev/blog/go1.13-errors). which support `Error, Unwrap, Is, As`.
```go
// sample using errors.As
_, err := c.chargeTransaction(param)
var Err *midtrans.Error
if errors.As(err, &Err) {
fmt.Println(Err.Message)
fmt.Println(Err.StatusCode)
}

// sample using unwrap
_, err := c.chargeTransaction(param)
if err != nil {
log.Print(errors.Unwrap(err))
fmt.Print(err)
}
```

## 5. Examples
Examples are available on [/examples](example) folder
Expand Down
13 changes: 8 additions & 5 deletions coreapi/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ func timestamp() string {
}

func getCardToken(cardNumber string) string {
year := time.Now().Year() + 1
midtrans.ClientKey = sandboxClientKey
res, _ := CardToken(cardNumber, 12, 21, "123")
res, _ := CardToken(cardNumber, 12, year, "123")
return res.TokenID
}

Expand All @@ -45,26 +46,28 @@ func createPayload(orderId string, paymentType CoreapiPaymentType, cardToken str
}

func TestRegisterCard(t *testing.T) {
year := time.Now().Year() + 1
midtrans.ClientKey = sandboxClientKey
resp1, _ := RegisterCard(sampleCardNumber, 12, 2021)
resp1, _ := RegisterCard(sampleCardNumber, 12, year)
assert.Equal(t, resp1.StatusCode, "200")
assert.Equal(t, resp1.MaskCard, "481111-1114")

c := Client{}
c.New(sandboxServerKey, midtrans.Sandbox)
resp2, _ := c.RegisterCard(bniCardNumber, 12, 2021, sandboxClientKey)
resp2, _ := c.RegisterCard(bniCardNumber, 12, year, sandboxClientKey)
assert.Equal(t, resp2.StatusCode, "200")
assert.Equal(t, resp2.MaskCard, "410505-1467")
}

func TestCardToken(t *testing.T) {
year := time.Now().Year() + 1
midtrans.ClientKey = sandboxClientKey
resp1, _ := CardToken(sampleCardNumber, 12, 2021, "123")
resp1, _ := CardToken(sampleCardNumber, 12, year, "123")
assert.Equal(t, resp1.StatusCode, "200")

c := Client{}
c.New(sandboxServerKey, midtrans.Sandbox)
resp2, _ := c.CardToken(bniCardNumber, 12, 2021, "123", sandboxClientKey)
resp2, _ := c.CardToken(bniCardNumber, 12, year, "123", sandboxClientKey)
assert.Equal(t, resp2.StatusCode, "200")
}

Expand Down
17 changes: 17 additions & 0 deletions error.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
package midtrans

import "fmt"

type Error struct {
Message string
StatusCode int
RawError error
RawApiResponse *ApiResponse
}

// Error returns error message.
// To comply midtrans.Error with Go error interface.
func (e *Error) Error() string {
if e.RawError != nil {
return fmt.Sprintf("%s: %s", e.Message, e.RawError.Error())
}
return e.Message
}

// Unwrap method that returns its contained error
// if there is RawError supplied during error creation, return RawError. Else, will return nil
func (e *Error) Unwrap() error {
return e.RawError
}

// GetMessage this get general message error when call api
func (e *Error) GetMessage() string {
return e.Message
Expand Down
41 changes: 41 additions & 0 deletions error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package midtrans

import (
"bytes"
"encoding/json"
"errors"
"net/http"
"testing"

assert "github.com/stretchr/testify/require"
)

func TestErrorStruct(t *testing.T) {
err := &Error{
Message: "Error Test Message",
StatusCode: 200,
RawError: errors.New("TEST FROM GO ERROR"),
RawApiResponse: nil,
}
var midError *Error
assert.Error(t, err)
assert.True(t, true, errors.Is(err, err))
assert.True(t, true, errors.As(err, &midError))
assert.Equal(t, `Error Test Message`, err.GetMessage())
assert.Equal(t, 200, err.GetStatusCode())
assert.Equal(t, "Error Test Message: TEST FROM GO ERROR", err.Error())
}

func TestErrorResponse(t *testing.T) {
serverKey := "dummy"
c := GetHttpClient(Environment)
jsonReq, _ := json.Marshal("{\"transaction_details\": {\"order_id\": \"TEST-1648108994111\", \"gross_amount\": 10000}}")
err := c.Call(http.MethodPost, "https://app.midtrans.com/snap/v1/transactions", &serverKey, nil, bytes.NewBuffer(jsonReq), nil)

var midError *Error
assert.True(t, true, errors.Is(err, err))
assert.True(t, true, errors.As(err, &midError))
assert.Error(t, err)
assert.Equal(t, 401, err.StatusCode)
assert.Equal(t, "app.midtrans.com", err.RawApiResponse.Request.Host)
}
16 changes: 10 additions & 6 deletions httpclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package midtrans

import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -67,16 +68,16 @@ func (c *HttpClientImplementation) Call(method string, url string, apiKey *strin
Message: "The API Key (ServerKey/IrisApiKey) is invalid, as it is an empty string. Please double-check your API key. " +
"You can check from the Midtrans Dashboard. " +
"See https://docs.midtrans.com/en/midtrans-account/overview?id=retrieving-api-access-keys " +
"for the details or contact support at support@midtrans.com if you have any questions.",
"for the details or please contact us via https://midtrans.com/contact-us. ",
}
c.Logger.Error("Authentication: ", err.GetMessage())
return err
} else if strings.Contains(key, " ") {
err := &Error{
Message: "The API Key (ServerKey/IrisApiKey) is contains white-space. Please double-check your API key. " +
Message: "The API Key (ServerKey/IrisApiKey) contains white-space. Please double-check your API key. " +
"You can check the ServerKey from the Midtrans Dashboard. " +
"See https://docs.midtrans.com/en/midtrans-account/overview?id=retrieving-api-access-keys " +
"for the details or contact support at support@midtrans.com if you have any questions.",
"for the details or please contact us via https://midtrans.com/contact-us. ",
}
c.Logger.Error("Authentication: ", err.GetMessage())
return err
Expand Down Expand Up @@ -151,21 +152,24 @@ func (c *HttpClientImplementation) DoRequest(req *http.Request, result interface
if found, data := HasOwnProperty("status_code", resBody); found {
statusCode, _ := strconv.Atoi(data["status_code"].(string))
if statusCode >= 401 && statusCode != 407 {
errMessage := fmt.Sprintf("Midtrans API is returning API error. HTTP status code: %s API response: %s", strconv.Itoa(statusCode), string(resBody))
return &Error{
Message: fmt.Sprintf("Midtrans API is returning API error. HTTP status code: %s API response: %s", strconv.Itoa(statusCode), string(resBody)),
Message: errMessage,
StatusCode: statusCode,
RawError: errors.New(errMessage),
RawApiResponse: rawResponse,
}
}
}

// Check StatusCode from Midtrans HTTP response api StatusCode
if res.StatusCode >= 400 {
errMessage := fmt.Sprintf("Midtrans API is returning API error. HTTP status code: %s API response: %s", strconv.Itoa(res.StatusCode), string(resBody))
return &Error{
Message: fmt.Sprintf("Midtrans API is returning API error. HTTP status code: %s API response: %s", strconv.Itoa(res.StatusCode), string(resBody)),
Message: errMessage,
StatusCode: res.StatusCode,
RawError: errors.New(errMessage),
RawApiResponse: rawResponse,
RawError: err,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion midtrans.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (
Production

//libraryVersion : midtrans go library version
libraryVersion = "v1.2.2"
libraryVersion = "v1.3.0"
)

//ServerKey is config payment API key for global use
Expand Down
2 changes: 1 addition & 1 deletion snap/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ type ExpiryDetails struct {
Duration int64 `json:"duration"`
}

// Callbacks : Redirect URL after transaction is successfully paid (Overriden by JS callback).
// Callbacks : Redirect URL after transaction is successfully paid (Overridden by JS callback).
// Can also be set via Snap Settings menu in your dashboard.
type Callbacks struct {
Finish string `json:"finish,omitempty"`
Expand Down

0 comments on commit 323c890

Please sign in to comment.