Skip to content

Commit

Permalink
Adds request and get payment contexts endpoints (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
armando-rodriguez-cko authored Dec 22, 2023
1 parent 374a0ef commit a1d7494
Show file tree
Hide file tree
Showing 13 changed files with 595 additions and 8 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build-master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
go-version: ${{ matrix.go }}
- id: build-and-test
env:
CHECKOUT_PROCESSING_CHANNEL_ID: ${{ secrets.IT_CHECKOUT_PROCESSING_CHANNEL_ID }}
CHECKOUT_PREVIOUS_SECRET_KEY: ${{ secrets.IT_CHECKOUT_PREVIOUS_SECRET_KEY }}
CHECKOUT_PREVIOUS_PUBLIC_KEY: ${{ secrets.IT_CHECKOUT_PREVIOUS_PUBLIC_KEY }}
CHECKOUT_DEFAULT_SECRET_KEY: ${{ secrets.IT_CHECKOUT_DEFAULT_SECRET_KEY }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
go-version: ${{ matrix.go }}
- id: build-and-test
env:
CHECKOUT_PROCESSING_CHANNEL_ID: ${{ secrets.IT_CHECKOUT_PROCESSING_CHANNEL_ID }}
CHECKOUT_PREVIOUS_SECRET_KEY: ${{ secrets.IT_CHECKOUT_PREVIOUS_SECRET_KEY }}
CHECKOUT_PREVIOUS_PUBLIC_KEY: ${{ secrets.IT_CHECKOUT_PREVIOUS_PUBLIC_KEY }}
CHECKOUT_DEFAULT_SECRET_KEY: ${{ secrets.IT_CHECKOUT_DEFAULT_SECRET_KEY }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
go-version: ${{ matrix.go }}
- id: build-and-test
env:
CHECKOUT_PROCESSING_CHANNEL_ID: ${{ secrets.IT_CHECKOUT_PROCESSING_CHANNEL_ID }}
CHECKOUT_PREVIOUS_SECRET_KEY: ${{ secrets.IT_CHECKOUT_PREVIOUS_SECRET_KEY }}
CHECKOUT_PREVIOUS_PUBLIC_KEY: ${{ secrets.IT_CHECKOUT_PREVIOUS_PUBLIC_KEY }}
CHECKOUT_DEFAULT_SECRET_KEY: ${{ secrets.IT_CHECKOUT_DEFAULT_SECRET_KEY }}
Expand Down
1 change: 1 addition & 0 deletions configuration/oauth_scopes.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ const (
IssuingCardMgmt = "issuing:card-mgmt"
IssuingControlsRead = "issuing:controls-read"
IssuingControlsWrite = "issuing:controls-write"
PaymentContexts = "Payment Contexts"
)
3 changes: 3 additions & 0 deletions nas/checkout_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
instruments "github.com/checkout/checkout-sdk-go/instruments/nas"
"github.com/checkout/checkout-sdk-go/issuing"
"github.com/checkout/checkout-sdk-go/metadata"
"github.com/checkout/checkout-sdk-go/payments/contexts"
"github.com/checkout/checkout-sdk-go/payments/hosted"
"github.com/checkout/checkout-sdk-go/payments/links"
payments "github.com/checkout/checkout-sdk-go/payments/nas"
Expand Down Expand Up @@ -43,6 +44,7 @@ type Api struct {
WorkFlows *workflows.Client
Reports *reports.Client
Issuing *issuing.Client
Contexts *contexts.Client

Ideal *ideal.Client
Klarna *klarna.Client
Expand Down Expand Up @@ -70,6 +72,7 @@ func CheckoutApi(configuration *configuration.Configuration) *Api {
api.WorkFlows = workflows.NewClient(configuration, apiClient)
api.Reports = reports.NewClient(configuration, apiClient)
api.Issuing = issuing.NewClient(configuration, apiClient)
api.Contexts = contexts.NewClient(configuration, apiClient)

api.Ideal = ideal.NewClient(configuration, apiClient)
api.Klarna = klarna.NewClient(configuration, apiClient)
Expand Down
49 changes: 49 additions & 0 deletions payments/contexts/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package contexts

import (
"github.com/checkout/checkout-sdk-go/client"
"github.com/checkout/checkout-sdk-go/common"
"github.com/checkout/checkout-sdk-go/configuration"
)

type Client struct {
configuration *configuration.Configuration
apiClient client.HttpClient
}

func NewClient(configuration *configuration.Configuration, apiClient client.HttpClient) *Client {
return &Client{
configuration: configuration,
apiClient: apiClient,
}
}

func (c *Client) RequestPaymentContexts(request PaymentContextsRequest) (*PaymentContextsRequestResponse, error) {
auth, err := c.configuration.Credentials.GetAuthorization(configuration.SecretKeyOrOauth)
if err != nil {
return nil, err
}

var response PaymentContextsRequestResponse
err = c.apiClient.Post(common.BuildPath(PaymentContextsPath), auth, request, &response, nil)
if err != nil {
return nil, err
}

return &response, nil
}

func (c *Client) GetPaymentContextDetails(paymentContextId string) (*PaymentContextDetailsResponse, error) {
auth, err := c.configuration.Credentials.GetAuthorization(configuration.SecretKeyOrOauth)
if err != nil {
return nil, err
}

var response PaymentContextDetailsResponse
err = c.apiClient.Get(common.BuildPath(PaymentContextsPath, paymentContextId), auth, &response)
if err != nil {
return nil, err
}

return &response, nil
}
235 changes: 235 additions & 0 deletions payments/contexts/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
package contexts

import (
"net/http"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"

"github.com/checkout/checkout-sdk-go/common"
"github.com/checkout/checkout-sdk-go/configuration"
"github.com/checkout/checkout-sdk-go/errors"
"github.com/checkout/checkout-sdk-go/mocks"
"github.com/checkout/checkout-sdk-go/payments"
"github.com/checkout/checkout-sdk-go/payments/nas"
"github.com/checkout/checkout-sdk-go/payments/nas/sources/contexts"
)

func TestCreateAPaymentContext(t *testing.T) {
var (
paymentContextsRequestResponse = PaymentContextsRequestResponse{
HttpMetadata: mocks.HttpMetadataStatusCreated,
Id: "pct_y3oqhf46pyzuxjbcn2giaqnb44",
PartnerMetadata: &PaymentContextsPartnerMetadata{
OrderId: "test_order_123",
CustomerId: "cus_123",
},
Links: map[string]common.Link{
"self": {
HRef: &[]string{"https://api.checkout.com/payment-contexts/pct_y3oqhf46pyzuxjbcn2giaqnb44"}[0],
},
},
}
)

cases := []struct {
name string
request PaymentContextsRequest
getAuthorization func(*mock.Mock) mock.Call
apiPost func(*mock.Mock) mock.Call
checker func(*PaymentContextsRequestResponse, error)
}{
{
name: "when request is correct then create a payment context",
request: PaymentContextsRequest{
Source: contexts.NewPaymentContextsPaypalSource(),
Amount: 2000,
Currency: common.EUR,
PaymentType: payments.Regular,
},
getAuthorization: func(m *mock.Mock) mock.Call {
return *m.On("GetAuthorization", mock.Anything).
Return(&configuration.SdkAuthorization{}, nil)
},
apiPost: func(m *mock.Mock) mock.Call {
return *m.On("Post", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return(nil).
Run(func(args mock.Arguments) {
respMapping := args.Get(3).(*PaymentContextsRequestResponse)
*respMapping = paymentContextsRequestResponse
})
},
checker: func(response *PaymentContextsRequestResponse, err error) {
assert.Nil(t, err)
assert.NotNil(t, response)
assert.Equal(t, http.StatusCreated, response.HttpMetadata.StatusCode)
},
},
{
name: "when credentials invalid then return error",
getAuthorization: func(m *mock.Mock) mock.Call {
return *m.On("GetAuthorization", mock.Anything).
Return(nil, errors.CheckoutAuthorizationError("Invalid authorization type"))
},
apiPost: func(m *mock.Mock) mock.Call {
return *m.On("Post", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return(nil)
},
checker: func(response *PaymentContextsRequestResponse, err error) {
assert.Nil(t, response)
assert.NotNil(t, err)
chkErr := err.(errors.CheckoutAuthorizationError)
assert.Equal(t, "Invalid authorization type", chkErr.Error())
},
},
{
name: "when request invalid then return error",
request: PaymentContextsRequest{},
getAuthorization: func(m *mock.Mock) mock.Call {
return *m.On("GetAuthorization", mock.Anything).
Return(&configuration.SdkAuthorization{}, nil)
},
apiPost: func(m *mock.Mock) mock.Call {
return *m.On("Post", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).
Return(
errors.CheckoutAPIError{
StatusCode: http.StatusUnprocessableEntity,
Status: "422 Invalid Request",
Data: &errors.ErrorDetails{ErrorType: "request_invalid"},
})
},
checker: func(response *PaymentContextsRequestResponse, err error) {
assert.Nil(t, response)
assert.NotNil(t, err)
chkErr := err.(errors.CheckoutAPIError)
assert.Equal(t, http.StatusUnprocessableEntity, chkErr.StatusCode)
assert.Equal(t, "request_invalid", chkErr.Data.ErrorType)
},
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
apiClient := new(mocks.ApiClientMock)
credentials := new(mocks.CredentialsMock)
environment := new(mocks.EnvironmentMock)

tc.getAuthorization(&credentials.Mock)
tc.apiPost(&apiClient.Mock)

configuration := configuration.NewConfiguration(credentials, environment, &http.Client{}, nil)
client := NewClient(configuration, apiClient)

tc.checker(client.RequestPaymentContexts(tc.request))
})
}
}

func TestGetAPaymentContext(t *testing.T) {
var (
paymentContextResponse = &PaymentContextsResponse{
Source: &nas.SourceResponse{
PaymentContextsPayPayResponseSource: &nas.PaymentContextsPayPayResponseSource{
Type: payments.PayPalSource,
},
},
Amount: 2000,
Currency: common.EUR,
PaymentType: payments.Regular,
Capture: true,
}

paymentContextDetailsResponse = PaymentContextDetailsResponse{
HttpMetadata: mocks.HttpMetadataStatusOk,
PaymentRequest: paymentContextResponse,
}
)

cases := []struct {
name string
paymentContextId string
getAuthorization func(*mock.Mock) mock.Call
apiGet func(*mock.Mock) mock.Call
checker func(*PaymentContextDetailsResponse, error)
}{
{
name: "when paymentContextId is correct then return payment context details",
paymentContextId: "pay_1234",
getAuthorization: func(m *mock.Mock) mock.Call {
return *m.On("GetAuthorization", mock.Anything).
Return(&configuration.SdkAuthorization{}, nil)
},
apiGet: func(m *mock.Mock) mock.Call {
return *m.On("Get", mock.Anything, mock.Anything, mock.Anything).
Return(nil).
Run(func(args mock.Arguments) {
respMapping := args.Get(2).(*PaymentContextDetailsResponse)
*respMapping = paymentContextDetailsResponse
})
},
checker: func(response *PaymentContextDetailsResponse, err error) {
assert.Nil(t, err)
assert.NotNil(t, response)
assert.Equal(t, http.StatusOK, response.HttpMetadata.StatusCode)
assert.Equal(t, paymentContextDetailsResponse.PaymentRequest.Amount, response.PaymentRequest.Amount)
},
},
{
name: "when credentials invalid then return error",
getAuthorization: func(m *mock.Mock) mock.Call {
return *m.On("GetAuthorization", mock.Anything).
Return(nil, errors.CheckoutAuthorizationError("Invalid authorization type"))
},
apiGet: func(m *mock.Mock) mock.Call {
return *m.On("Get", mock.Anything, mock.Anything, mock.Anything).
Return(nil)
},
checker: func(response *PaymentContextDetailsResponse, err error) {
assert.Nil(t, response)
assert.NotNil(t, err)
chkErr := err.(errors.CheckoutAuthorizationError)
assert.Equal(t, "Invalid authorization type", chkErr.Error())
},
},
{
name: "when paymentContextId not found then return error",
paymentContextId: "not_found",
getAuthorization: func(m *mock.Mock) mock.Call {
return *m.On("GetAuthorization", mock.Anything).
Return(&configuration.SdkAuthorization{}, nil)
},
apiGet: func(m *mock.Mock) mock.Call {
return *m.On("Get", mock.Anything, mock.Anything, mock.Anything).
Return(
errors.CheckoutAPIError{
StatusCode: http.StatusNotFound,
Status: "404 Not Found",
})
},
checker: func(response *PaymentContextDetailsResponse, err error) {
assert.Nil(t, response)
assert.NotNil(t, err)
chkErr := err.(errors.CheckoutAPIError)
assert.Equal(t, http.StatusNotFound, chkErr.StatusCode)
assert.Equal(t, "404 Not Found", chkErr.Status)
},
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
apiClient := new(mocks.ApiClientMock)
credentials := new(mocks.CredentialsMock)
environment := new(mocks.EnvironmentMock)

tc.getAuthorization(&credentials.Mock)
tc.apiGet(&apiClient.Mock)

configuration := configuration.NewConfiguration(credentials, environment, &http.Client{}, nil)
client := NewClient(configuration, apiClient)

tc.checker(client.GetPaymentContextDetails(tc.paymentContextId))
})
}
}
Loading

0 comments on commit a1d7494

Please sign in to comment.