From 2fb5ac1ceb8d54b4f44a367b3845bc3982764991 Mon Sep 17 00:00:00 2001 From: Acho Arnold Date: Wed, 19 Oct 2022 19:57:42 +0300 Subject: [PATCH] Add cashin and cashout endpoints --- .github/dependabot.yml | 6 ++ .github/workflows/main.yml | 6 +- cashin_service.go | 33 +++++++ cashin_service_test.go | 74 ++++++++++++++++ cashout_service.go | 33 +++++++ cashout_service_test.go | 74 ++++++++++++++++ client.go | 52 +++++++---- client_config.go | 21 +++-- client_option.go | 15 ++++ client_option_test.go | 31 +++++++ client_test.go | 132 ++++++++++++++++++++++++++++ constructs.go | 15 ++++ internal/helpers/test_helper.go | 18 ++-- internal/stubs/cashin_responses.go | 34 +++++++ internal/stubs/cashout_responses.go | 34 +++++++ internal/stubs/responses.go | 46 ++++++++++ topup.go | 15 ---- topup_service.go | 6 +- 18 files changed, 596 insertions(+), 49 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 cashin_service.go create mode 100644 cashin_service_test.go create mode 100644 cashout_service.go create mode 100644 cashout_service_test.go create mode 100644 internal/stubs/cashin_responses.go create mode 100644 internal/stubs/cashout_responses.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..36b24f2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: gomod + directory: / + schedule: + interval: daily diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8a2cef9..aff759c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 ./... @@ -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: | diff --git a/cashin_service.go b/cashin_service.go new file mode 100644 index 0000000..22f2953 --- /dev/null +++ b/cashin_service.go @@ -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 +} diff --git a/cashin_service_test.go b/cashin_service_test.go new file mode 100644 index 0000000..291b366 --- /dev/null +++ b/cashin_service_test.go @@ -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() +} diff --git a/cashout_service.go b/cashout_service.go new file mode 100644 index 0000000..a62ba23 --- /dev/null +++ b/cashout_service.go @@ -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 +} diff --git a/cashout_service_test.go b/cashout_service_test.go new file mode 100644 index 0000000..b9307cb --- /dev/null +++ b/cashout_service_test.go @@ -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() +} diff --git a/client.go b/client.go index 5b35f2d..6f1e6dc 100644 --- a/client.go +++ b/client.go @@ -9,7 +9,6 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "sort" @@ -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. @@ -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 } @@ -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 @@ -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 } @@ -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 } diff --git a/client_config.go b/client_config.go index 8ae644f..3ebf195 100644 --- a/client_config.go +++ b/client_config.go @@ -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", } } diff --git a/client_option.go b/client_option.go index 9101d31..ec7b194 100644 --- a/client_option.go +++ b/client_option.go @@ -3,6 +3,7 @@ package smobilpay import ( "net/http" "strings" + "time" ) // Option is options for constructing a client @@ -48,3 +49,17 @@ func WithAccessSecret(accessSecret string) Option { config.accessSecret = accessSecret }) } + +// WithCollectSyncVerifyInterval sets the interval for calling the `/verifytx` endpoint to check the status of pending transactions +func WithCollectSyncVerifyInterval(interval time.Duration) Option { + return clientOptionFunc(func(config *clientConfig) { + config.collectSyncVerifyInterval = interval + }) +} + +// WithCollectSyncVerifyRetryCount sets the number of retries for calling the `/verifytx` endpoint to check the status of pending transactions +func WithCollectSyncVerifyRetryCount(retryCount uint) Option { + return clientOptionFunc(func(config *clientConfig) { + config.collectSyncVerifyRetryCount = retryCount + }) +} diff --git a/client_option_test.go b/client_option_test.go index cf8bf60..9e70f2c 100644 --- a/client_option_test.go +++ b/client_option_test.go @@ -3,6 +3,7 @@ package smobilpay import ( "net/http" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -100,3 +101,33 @@ func TestWithAccessSecret(t *testing.T) { // Assert assert.Equal(t, accessSecret, config.accessSecret) } + +func TestWithCollectSyncVerifyInterval(t *testing.T) { + // Setup + t.Parallel() + + // Arrange + config := defaultClientConfig() + interval := time.Second * 2 + + // Act + WithCollectSyncVerifyInterval(interval).apply(config) + + // Assert + assert.Equal(t, interval, config.collectSyncVerifyInterval) +} + +func TestWithCollectSyncVerifyRetryCount(t *testing.T) { + // Setup + t.Parallel() + + // Arrange + config := defaultClientConfig() + retryCount := uint(1000) + + // Act + WithCollectSyncVerifyRetryCount(retryCount).apply(config) + + // Assert + assert.Equal(t, retryCount, config.collectSyncVerifyRetryCount) +} diff --git a/client_test.go b/client_test.go index 86b6a07..19cdc35 100644 --- a/client_test.go +++ b/client_test.go @@ -189,6 +189,138 @@ func TestClient_Collect(t *testing.T) { server.Close() } +func TestClient_CollectSync(t *testing.T) { + // Setup + t.Parallel() + + // Arrange + server := helpers.MakeTestServerWithMultipleResponses(http.StatusOK, [][]byte{ + stubs.CollectPending(), + stubs.VerifyInProcess(), + stubs.VerifyOk(), + }) + verifyInterval := 100 * time.Millisecond + accessToken := "6B352110-4716-11ED-963F-0800200C9A66" + client := New( + WithBaseURL(server.URL), + WithCollectSyncVerifyInterval(verifyInterval), + WithAccessToken(accessToken), + WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"), + ) + nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995" + params := &CollectParams{ + QuoteID: "15380e55-6227-4a25-8e1f-23c8735ce242", + CustomerPhoneNumber: "697777777", + CustomerEmailAddress: "dev@test.com", + ServiceNumber: "697777777", + ExternalTransactionID: "999992624813740205", + } + + // Act + start := time.Now() + transaction, response, err := client.CollectSync( + context.Background(), + params, + WithRequestNonce(nonce), + ) + + // Assert + assert.Nil(t, err) + assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode) + assert.Equal(t, params.ExternalTransactionID, *transaction.ExternalTransactionID) + assert.False(t, transaction.IsFailed()) + assert.Equal(t, "SUCCESS", transaction.Status) + assert.GreaterOrEqual(t, time.Since(start), 2*verifyInterval) + + // Teardown + server.Close() +} + +func TestClient_CollectSyncWithContextTimeout(t *testing.T) { + // Setup + t.Parallel() + + // Arrange + server := helpers.MakeTestServerWithMultipleResponses(http.StatusOK, [][]byte{ + stubs.CollectPending(), + stubs.VerifyInProcess(), + stubs.VerifyOk(), + }) + + 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" + params := &CollectParams{ + QuoteID: "15380e55-6227-4a25-8e1f-23c8735ce242", + CustomerPhoneNumber: "697777777", + CustomerEmailAddress: "dev@test.com", + ServiceNumber: "697777777", + ExternalTransactionID: "999992624813740205", + } + ctxTimeout := 50 * time.Millisecond + ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout) + start := time.Now() + defer cancel() + + // Act + _, _, err := client.CollectSync( + ctx, + params, + WithRequestNonce(nonce), + ) + + // Assert + assert.NotNil(t, err) + assert.LessOrEqual(t, time.Since(start), 2*ctxTimeout) + + // Teardown + server.Close() +} + +func TestClient_CollectSyncNoRetry(t *testing.T) { + // Setup + t.Parallel() + + // Arrange + server := helpers.MakeTestServer(http.StatusOK, stubs.CollectPending()) + accessToken := "6B352110-4716-11ED-963F-0800200C9A66" + client := New( + WithBaseURL(server.URL), + WithAccessToken(accessToken), + WithCollectSyncVerifyRetryCount(0), + WithAccessSecret("1B875FB0-4717-11ED-963F-0800200C9A66"), + ) + nonce := "95cdf110-4614-4d95-b6c2-f14fe01c4995" + params := &CollectParams{ + QuoteID: "15380e55-6227-4a25-8e1f-23c8735ce242", + CustomerPhoneNumber: "697777777", + CustomerEmailAddress: "dev@test.com", + ServiceNumber: "697777777", + ExternalTransactionID: "999992624813740205", + } + + // Act + transaction, response, err := client.CollectSync( + context.Background(), + params, + WithRequestNonce(nonce), + ) + + // Assert + assert.Nil(t, err) + assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode) + assert.Equal(t, params.ExternalTransactionID, *transaction.ExternalTransactionID) + assert.True(t, transaction.IsPending()) + assert.Equal(t, "PENDING", transaction.Status) + + // Teardown + server.Close() +} + func TestClient_Verify(t *testing.T) { // Setup t.Parallel() diff --git a/constructs.go b/constructs.go index c518ebb..1caaaa6 100644 --- a/constructs.go +++ b/constructs.go @@ -4,6 +4,21 @@ import ( "time" ) +// PayItem represents a payment item +type PayItem struct { + ServiceID string `json:"serviceid"` + Merchant string `json:"merchant"` + PayItemID string `json:"payItemId"` + AmountType string `json:"amountType"` + LocalCurrency string `json:"localCur"` + Name string `json:"name"` + AmountLocalCurrency interface{} `json:"amountLocalCur"` + Description string `json:"description"` + PayItemDescription interface{} `json:"payItemDescr"` + OptionalString interface{} `json:"optStrg"` + OptionalNumber interface{} `json:"optNmb"` +} + // QuoteParams is the input needed to initialize a transaction type QuoteParams struct { PayItemID string `json:"payItemId"` diff --git a/internal/helpers/test_helper.go b/internal/helpers/test_helper.go index 5b63d3b..f96e592 100644 --- a/internal/helpers/test_helper.go +++ b/internal/helpers/test_helper.go @@ -19,6 +19,19 @@ func MakeTestServer(responseCode int, body []byte) *httptest.Server { })) } +// MakeTestServerWithMultipleResponses creates an api server for testing with multiple responses +func MakeTestServerWithMultipleResponses(responseCode int, responses [][]byte) *httptest.Server { + count := 0 + return httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(responseCode) + _, err := res.Write(responses[count]) + if err != nil { + panic(err) + } + count++ + })) +} + // MakeRequestCapturingTestServer creates an api server that captures the request object func MakeRequestCapturingTestServer(responseCode int, response []byte, request *http.Request) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(responseWriter http.ResponseWriter, req *http.Request) { @@ -41,8 +54,3 @@ func MakeRequestCapturingTestServer(responseCode int, response []byte, request * } })) } - -// StringToPointer converts a string to a pointer -func StringToPointer(value string) *string { - return &value -} diff --git a/internal/stubs/cashin_responses.go b/internal/stubs/cashin_responses.go new file mode 100644 index 0000000..90c5275 --- /dev/null +++ b/internal/stubs/cashin_responses.go @@ -0,0 +1,34 @@ +package stubs + +// CashinGetOk is the response when getting the `/cashin` endpoint +func CashinGetOk() []byte { + return []byte(` +[ + { + "serviceid": "30052", + "merchant": "CMORANGEOM", + "payItemId": "S-112-948-CMORANGEOM-30052-2006125104-1", + "amountType": "CUSTOM", + "localCur": "XAF", + "name": "Custom Amount", + "amountLocalCur": null, + "description": "Customer amount", + "payItemDescr": null, + "optStrg": null, + "optNmb": null + } +] +`) +} + +// CashinGetError is the cashout error with an invalid service +func CashinGetError() []byte { + return []byte(` +{ + "devMsg": "Service unknown", + "usrMsg": "Service unknown", + "respCode": 40602, + "link": "http://support.maviance.com/" +} +`) +} diff --git a/internal/stubs/cashout_responses.go b/internal/stubs/cashout_responses.go new file mode 100644 index 0000000..8b5a4f4 --- /dev/null +++ b/internal/stubs/cashout_responses.go @@ -0,0 +1,34 @@ +package stubs + +// CashoutGetOk is the response when getting the `/cashout` endpoint +func CashoutGetOk() []byte { + return []byte(` +[ + { + "serviceid": "20053", + "merchant": "MTNMOMO", + "payItemId": "S-112-949-MTNMOMO-20053-200050001-1", + "amountType": "CUSTOM", + "localCur": "XAF", + "name": "CASH-OUT", + "amountLocalCur": null, + "description": "Cash out a custom amount", + "payItemDescr": null, + "optStrg": null, + "optNmb": null + } +] +`) +} + +// CashoutGetError is the cashout error with an invalid service +func CashoutGetError() []byte { + return []byte(` +{ + "devMsg": "Service unknown", + "usrMsg": "Service unknown", + "respCode": 40602, + "link": "http://support.maviance.com/" +} +`) +} diff --git a/internal/stubs/responses.go b/internal/stubs/responses.go index 73b8347..de4c1e1 100644 --- a/internal/stubs/responses.go +++ b/internal/stubs/responses.go @@ -46,6 +46,28 @@ func CollectOk() []byte { `) } +// CollectPending returns the api response to the `/collectstd` endpoint for a pending transaction +func CollectPending() []byte { + return []byte(` +{ + "ptn":"99999166542651400095315364801168", + "timestamp":"2022-10-10T18:28:34+00:00", + "agentBalance":"247000.00", + "receiptNumber":"999992624813740205", + "veriCode":"f20873", + "priceLocalCur":"1000.00", + "priceSystemCur":"1000.00", + "localCur":"XAF", + "systemCur":"XAF", + "trid": "999992624813740205", + "pin":null, + "status":"PENDING", + "payItemDescr":null, + "payItemId":"S-112-951-CMORANGE-20062-CM_ORANGE_VTU_CUSTOM-1" +} +`) +} + // VerifyOk returns the api response to the `/verifytx` endpoint func VerifyOk() []byte { return []byte(` @@ -70,6 +92,30 @@ func VerifyOk() []byte { `) } +// VerifyInProcess returns the api response to the `/verifytx` endpoint for an in process transaction +func VerifyInProcess() []byte { + return []byte(` +[ + { + "ptn":"99999166542651400095315364801168", + "timestamp":"2022-10-10T18:28:34+00:00", + "agentBalance":"247000.00", + "receiptNumber":"999992624813740205", + "veriCode":"f20873", + "priceLocalCur":"1000.00", + "priceSystemCur":"1000.00", + "localCur":"XAF", + "systemCur":"XAF", + "trid": "999992624813740205", + "pin":null, + "status":"INPROCESS", + "payItemDescr":null, + "payItemId":"S-112-951-CMORANGE-20062-CM_ORANGE_VTU_CUSTOM-1" + } +] +`) +} + // QuoteOk returns the api response to the `/quotestd` endpoint func QuoteOk() []byte { return []byte(` diff --git a/topup.go b/topup.go index 01f10ed..5b38a05 100644 --- a/topup.go +++ b/topup.go @@ -1,16 +1 @@ package smobilpay - -// Topup represents a network where we can buy airtime credit -type Topup struct { - ServiceID string `json:"serviceid"` - Merchant string `json:"merchant"` - PayItemID string `json:"payItemId"` - AmountType string `json:"amountType"` - LocalCurrency string `json:"localCur"` - Name string `json:"name"` - AmountLocalCurrency interface{} `json:"amountLocalCur"` - Description string `json:"description"` - PayItemDescription interface{} `json:"payItemDescr"` - OptionalString interface{} `json:"optStrg"` - OptionalNumber interface{} `json:"optNmb"` -} diff --git a/topup_service.go b/topup_service.go index e0202eb..95f75ed 100644 --- a/topup_service.go +++ b/topup_service.go @@ -7,13 +7,13 @@ import ( "net/http" ) -// topupService is the API client for the `/` endpoint +// topupService is the API client for the `/topup` endpoint type topupService service // GetPackages returns a list of all available topup packages. // // https://apidocs.smobilpay.com/s3papi/API-Reference.2066448558.html -func (service *topupService) GetPackages(ctx context.Context, serviceID string, options ...RequestOption) ([]*Topup, *Response, error) { +func (service *topupService) GetPackages(ctx context.Context, serviceID string, options ...RequestOption) ([]*PayItem, *Response, error) { request, err := service.client.newRequest(ctx, options, http.MethodGet, fmt.Sprintf("/topup?serviceid=%s", serviceID), nil) if err != nil { return nil, nil, err @@ -24,7 +24,7 @@ func (service *topupService) GetPackages(ctx context.Context, serviceID string, return nil, response, err } - var packages []*Topup + var packages []*PayItem if err = json.Unmarshal(*response.Body, &packages); err != nil { return nil, response, err }