Skip to content

Commit

Permalink
Add cashin and cashout endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
AchoArnold committed Oct 19, 2022
1 parent 9ea7d30 commit 2fb5ac1
Show file tree
Hide file tree
Showing 18 changed files with 596 additions and 49 deletions.
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: daily
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ jobs:
uses: actions/checkout@v2

- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3

- name: Setup Dependencies
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b $GOPATH/bin v1.24.0
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sudo sh -s -- -b $GOPATH/bin v1.50.0
golangci-lint --version
go get golang.org/x/tools/cmd/cover
go get -t -v ./...
Expand All @@ -32,7 +32,7 @@ jobs:
uses: actions/checkout@v2

- name: Setup Go
uses: actions/setup-go@v2
uses: actions/setup-go@v3

- name: Setup Dependencies
run: |
Expand Down
33 changes: 33 additions & 0 deletions cashin_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package smobilpay

import (
"context"
"encoding/json"
"fmt"
"net/http"
)

// cashinService is the API client for the `/cashin` endpoint
type cashinService service

// Get returns a list of all available cashout packages.
//
// https://apidocs.smobilpay.com/s3papi/API-Reference.2066448558.html
func (service *cashinService) Get(ctx context.Context, serviceID string, options ...RequestOption) ([]*PayItem, *Response, error) {
request, err := service.client.newRequest(ctx, options, http.MethodGet, fmt.Sprintf("/cashin?serviceid=%s", serviceID), nil)
if err != nil {
return nil, nil, err
}

response, err := service.client.do(request)
if err != nil {
return nil, response, err
}

var packages []*PayItem
if err = json.Unmarshal(*response.Body, &packages); err != nil {
return nil, response, err
}

return packages, response, nil
}
74 changes: 74 additions & 0 deletions cashin_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package smobilpay

import (
"context"
"net/http"
"testing"

"github.com/NdoleStudio/smobilpay-go/internal/helpers"
"github.com/NdoleStudio/smobilpay-go/internal/stubs"
"github.com/stretchr/testify/assert"
)

func TestCashinService_Get(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusOK, stubs.CashinGetOk())
accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
client := New(
WithBaseURL(server.URL),
WithAccessToken(accessToken),
WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
)
nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
serviceID := "30052"

// Act
payItems, response, err := client.Cashout.Get(
context.Background(),
serviceID,
WithRequestNonce(nonce),
)

// Assert
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
assert.Equal(t, 1, len(payItems))
assert.Equal(t, serviceID, payItems[0].ServiceID)

// Teardown
server.Close()
}

func TestCashinService_GetError(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusBadRequest, stubs.CashinGetError())
accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
client := New(
WithBaseURL(server.URL),
WithAccessToken(accessToken),
WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
)
nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
serviceID := "30052"

// Act
payItems, response, err := client.Cashout.Get(
context.Background(),
serviceID,
WithRequestNonce(nonce),
)

// Assert
assert.NotNil(t, err)
assert.Equal(t, http.StatusBadRequest, response.HTTPResponse.StatusCode)
assert.Equal(t, 0, len(payItems))

// Teardown
server.Close()
}
33 changes: 33 additions & 0 deletions cashout_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package smobilpay

import (
"context"
"encoding/json"
"fmt"
"net/http"
)

// cashoutService is the API client for the `/cashout` endpoint
type cashoutService service

// Get returns a list of all available cashout packages.
//
// https://apidocs.smobilpay.com/s3papi/API-Reference.2066448558.html
func (service *cashoutService) Get(ctx context.Context, serviceID string, options ...RequestOption) ([]*PayItem, *Response, error) {
request, err := service.client.newRequest(ctx, options, http.MethodGet, fmt.Sprintf("/cashout?serviceid=%s", serviceID), nil)
if err != nil {
return nil, nil, err
}

response, err := service.client.do(request)
if err != nil {
return nil, response, err
}

var packages []*PayItem
if err = json.Unmarshal(*response.Body, &packages); err != nil {
return nil, response, err
}

return packages, response, nil
}
74 changes: 74 additions & 0 deletions cashout_service_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package smobilpay

import (
"context"
"net/http"
"testing"

"github.com/NdoleStudio/smobilpay-go/internal/helpers"
"github.com/NdoleStudio/smobilpay-go/internal/stubs"
"github.com/stretchr/testify/assert"
)

func TestCashoutService_Get(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusOK, stubs.CashoutGetOk())
accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
client := New(
WithBaseURL(server.URL),
WithAccessToken(accessToken),
WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
)
nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
serviceID := "20053"

// Act
payItems, response, err := client.Cashout.Get(
context.Background(),
serviceID,
WithRequestNonce(nonce),
)

// Assert
assert.Nil(t, err)
assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
assert.Equal(t, 1, len(payItems))
assert.Equal(t, serviceID, payItems[0].ServiceID)

// Teardown
server.Close()
}

func TestCashoutService_GetError(t *testing.T) {
// Setup
t.Parallel()

// Arrange
server := helpers.MakeTestServer(http.StatusBadRequest, stubs.CashoutGetError())
accessToken := "6B352110-4716-11ED-963F-0800200C9A66"
client := New(
WithBaseURL(server.URL),
WithAccessToken(accessToken),
WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"),
)
nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995"
serviceID := "30052"

// Act
payItems, response, err := client.Cashout.Get(
context.Background(),
serviceID,
WithRequestNonce(nonce),
)

// Assert
assert.NotNil(t, err)
assert.Equal(t, http.StatusBadRequest, response.HTTPResponse.StatusCode)
assert.Equal(t, 0, len(payItems))

// Teardown
server.Close()
}
52 changes: 36 additions & 16 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"sort"
Expand All @@ -30,8 +29,13 @@ type Client struct {
accessToken string
accessSecret string

Topup *topupService
Bill *billService
collectSyncVerifyInterval time.Duration
collectSyncVerifyRetryCount uint

Topup *topupService
Bill *billService
Cashout *cashoutService
Cashin *cashinService
}

// New creates and returns a new *Client from a slice of Option.
Expand All @@ -43,15 +47,20 @@ func New(options ...Option) *Client {
}

client := &Client{
httpClient: config.httpClient,
accessToken: config.accessToken,
accessSecret: config.accessSecret,
baseURL: config.baseURL,
httpClient: config.httpClient,
accessToken: config.accessToken,
accessSecret: config.accessSecret,
baseURL: config.baseURL,
collectSyncVerifyRetryCount: config.collectSyncVerifyRetryCount,
collectSyncVerifyInterval: config.collectSyncVerifyInterval,
}

client.common.client = client
client.Topup = (*topupService)(&client.common)
client.Bill = (*billService)(&client.common)
client.Cashout = (*cashoutService)(&client.common)
client.Cashin = (*cashinService)(&client.common)

return client
}

Expand Down Expand Up @@ -137,17 +146,28 @@ func (client *Client) CollectSync(ctx context.Context, params *CollectParams, op
return transaction, response, err
}

// wait for completion in 5 minutes
number := transaction.PaymentTransactionNumber
counter := 1
for {
time.Sleep(20 * time.Second)
transaction, response, err = client.Verify(ctx, number)
if err != nil || !transaction.IsPending() || ctx.Err() != nil || counter == 15 {
paymentTransactionNumber := transaction.PaymentTransactionNumber
for counter := uint(0); counter < client.collectSyncVerifyRetryCount; counter++ {
client.sleepWithContext(ctx, client.collectSyncVerifyInterval)
transaction, response, err = client.Verify(ctx, paymentTransactionNumber)
if err != nil || !transaction.IsPending() {
return transaction, response, err
}
counter++
}

return transaction, response, err
}

func (client *Client) sleepWithContext(ctx context.Context, d time.Duration) {
timer := time.NewTimer(d)
select {
case <-ctx.Done():
if !timer.Stop() {
<-timer.C
}
case <-timer.C:
}
}

// Verify gets the current collection status
Expand Down Expand Up @@ -334,7 +354,7 @@ func (client *Client) do(req *http.Request) (*Response, error) {
return resp, err
}

_, err = io.Copy(ioutil.Discard, httpResponse.Body)
_, err = io.Copy(io.Discard, httpResponse.Body)
if err != nil {
return resp, err
}
Expand All @@ -351,7 +371,7 @@ func (client *Client) newResponse(httpResponse *http.Response) (*Response, error
resp := new(Response)
resp.HTTPResponse = httpResponse

buf, err := ioutil.ReadAll(resp.HTTPResponse.Body)
buf, err := io.ReadAll(resp.HTTPResponse.Body)
if err != nil {
return nil, err
}
Expand Down
21 changes: 14 additions & 7 deletions client_config.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package smobilpay

import "net/http"
import (
"net/http"
"time"
)

type clientConfig struct {
httpClient *http.Client
baseURL string
accessToken string
accessSecret string
httpClient *http.Client
collectSyncVerifyInterval time.Duration
collectSyncVerifyRetryCount uint
baseURL string
accessToken string
accessSecret string
}

func defaultClientConfig() *clientConfig {
return &clientConfig{
httpClient: http.DefaultClient,
baseURL: "https://s3p.smobilpay.staging.maviance.info/v2",
httpClient: http.DefaultClient,
collectSyncVerifyInterval: 20 * time.Second,
collectSyncVerifyRetryCount: 15,
baseURL: "https://s3p.smobilpay.staging.maviance.info/v2",
}
}

0 comments on commit 2fb5ac1

Please sign in to comment.