Skip to content

Commit a413d73

Browse files
committed
implement ValidateAccountHolderStatus and GetBasicUserinfo
1 parent 97f3ea8 commit a413d73

File tree

7 files changed

+228
-7
lines changed

7 files changed

+228
-7
lines changed

collection.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
11
package mtnmomo
22

3+
import "strings"
4+
5+
type AccountHolderIDType string
6+
7+
const (
8+
// AccountHolderIDTypeMSISDN is the account holder ID type for a mobile number
9+
AccountHolderIDTypeMSISDN AccountHolderIDType = "msisdn"
10+
11+
// AccountHolderIDTypeEMAIL is the account holder ID type for an email address
12+
AccountHolderIDTypeEMAIL AccountHolderIDType = "email"
13+
)
14+
15+
// AccountHolderStatus is the status of an account holder
16+
type AccountHolderStatus struct {
17+
IsActive bool `json:"result"`
18+
}
19+
20+
// BasicUserInfo contains personal information of an account holder
21+
type BasicUserInfo struct {
22+
GivenName string `json:"given_name"`
23+
FamilyName string `json:"family_name"`
24+
Sub string `json:"sub"`
25+
}
26+
27+
// FullName returns the full name of the account holder
28+
func (info *BasicUserInfo) FullName() string {
29+
return strings.TrimSpace(info.FamilyName + " " + info.GivenName)
30+
}
31+
332
// RequestToPayParams is the set of parameters used when creating a payment request
433
type RequestToPayParams struct {
534
Amount string `json:"amount"`

collection_service.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package mtnmomo
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
"net/http"
78
"time"
89
)
@@ -129,6 +130,68 @@ func (service *collectionService) GetAccountBalance(ctx context.Context) (*Accou
129130
return balance, response, err
130131
}
131132

133+
// ValidateAccountHolderStatus is used to check if an account holder is registered and active in the system.
134+
//
135+
// API Docs: https://momodeveloper.mtn.com/api-details#api=collection&operation=ValidateAccountHolderStatus
136+
func (service *collectionService) ValidateAccountHolderStatus(ctx context.Context, IDType AccountHolderIDType, ID string) (*AccountHolderStatus, *Response, error) {
137+
err := service.refreshToken(ctx)
138+
if err != nil {
139+
return nil, nil, err
140+
}
141+
142+
uri := fmt.Sprintf("/collection/v1_0/accountholder/%s/%s/active", IDType, ID)
143+
request, err := service.client.newRequest(ctx, http.MethodGet, uri, nil)
144+
if err != nil {
145+
return nil, nil, err
146+
}
147+
148+
service.client.addAccessToken(request)
149+
service.client.addTargetEnvironment(request)
150+
151+
response, err := service.client.do(request)
152+
if err != nil {
153+
return nil, response, err
154+
}
155+
156+
status := new(AccountHolderStatus)
157+
if err = json.Unmarshal(*response.Body, status); err != nil {
158+
return nil, response, err
159+
}
160+
161+
return status, response, err
162+
}
163+
164+
// GetBasicUserinfo returns personal information of the account holder.
165+
//
166+
// API Docs: https://momodeveloper.mtn.com/api-details#api=collection&operation=GetBasicUserinfo
167+
func (service *collectionService) GetBasicUserinfo(ctx context.Context, IDType AccountHolderIDType, ID string) (*BasicUserInfo, *Response, error) {
168+
err := service.refreshToken(ctx)
169+
if err != nil {
170+
return nil, nil, err
171+
}
172+
173+
uri := fmt.Sprintf("/collection/v1_0/accountholder/%s/%s/basicuserinfo", IDType, ID)
174+
request, err := service.client.newRequest(ctx, http.MethodGet, uri, nil)
175+
if err != nil {
176+
return nil, nil, err
177+
}
178+
179+
service.client.addAccessToken(request)
180+
service.client.addTargetEnvironment(request)
181+
182+
response, err := service.client.do(request)
183+
if err != nil {
184+
return nil, response, err
185+
}
186+
187+
status := new(BasicUserInfo)
188+
if err = json.Unmarshal(*response.Body, status); err != nil {
189+
return nil, response, err
190+
}
191+
192+
return status, response, err
193+
}
194+
132195
func (service *collectionService) tokenIsValid() bool {
133196
return time.Now().UTC().Unix() < service.client.accessTokenExpiresAt
134197
}

collection_service_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,107 @@ func TestCollectionService_GetAccountBalance(t *testing.T) {
174174
// Teardown
175175
server.Close()
176176
}
177+
178+
func TestCollectionService_ValidateAccountHolderStatus(t *testing.T) {
179+
// Setup
180+
t.Parallel()
181+
182+
// Arrange
183+
requests := make([]*http.Request, 0)
184+
responses := [][]byte{stubs.CollectionToken(), stubs.CollectionValidateAccountHolderStatus()}
185+
server := helpers.MakeRequestCapturingTestServer(http.StatusOK, responses, &requests)
186+
client := New(
187+
WithBaseURL(server.URL),
188+
WithSubscriptionKey(testSubscriptionKey),
189+
WithAPIUser(testAPIUser),
190+
WithAPIKey(testAPIKey),
191+
)
192+
193+
// Act
194+
status, response, err := client.Collection.ValidateAccountHolderStatus(context.Background(), AccountHolderIDTypeMSISDN, "23777777777")
195+
196+
// Assert
197+
assert.Nil(t, err)
198+
199+
assert.GreaterOrEqual(t, len(requests), 1)
200+
request := requests[len(requests)-1]
201+
assert.Equal(t, "/collection/v1_0/accountholder/msisdn/23777777777/active", request.URL.Path)
202+
assert.True(t, strings.HasPrefix(request.Header.Get("Authorization"), "Bearer"))
203+
assert.Equal(t, testSubscriptionKey, request.Header.Get(headerKeySubscriptionKey))
204+
assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
205+
206+
assert.True(t, status.IsActive)
207+
208+
// Teardown
209+
server.Close()
210+
}
211+
212+
func TestCollectionService_ValidateAccountHolderStatusWithInternalServerError(t *testing.T) {
213+
// Setup
214+
t.Parallel()
215+
216+
// Arrange
217+
server := helpers.MakeTestServer(http.StatusInternalServerError, nil)
218+
client := New(WithBaseURL(server.URL))
219+
220+
// Act
221+
_, _, err := client.Collection.ValidateAccountHolderStatus(context.Background(), AccountHolderIDTypeMSISDN, "23777777777")
222+
223+
// Assert
224+
assert.NotNil(t, err)
225+
226+
// Teardown
227+
server.Close()
228+
}
229+
230+
func TestCollectionService_GetBasicUserinfo(t *testing.T) {
231+
// Setup
232+
t.Parallel()
233+
234+
// Arrange
235+
requests := make([]*http.Request, 0)
236+
responses := [][]byte{stubs.CollectionToken(), stubs.CollectionGetBasicUserinfo()}
237+
server := helpers.MakeRequestCapturingTestServer(http.StatusOK, responses, &requests)
238+
client := New(
239+
WithBaseURL(server.URL),
240+
WithSubscriptionKey(testSubscriptionKey),
241+
WithAPIUser(testAPIUser),
242+
WithAPIKey(testAPIKey),
243+
)
244+
245+
// Act
246+
userInfo, response, err := client.Collection.GetBasicUserinfo(context.Background(), AccountHolderIDTypeMSISDN, "23777777777")
247+
248+
// Assert
249+
assert.Nil(t, err)
250+
251+
assert.GreaterOrEqual(t, len(requests), 1)
252+
request := requests[len(requests)-1]
253+
assert.Equal(t, "/collection/v1_0/accountholder/msisdn/23777777777/basicuserinfo", request.URL.Path)
254+
assert.True(t, strings.HasPrefix(request.Header.Get("Authorization"), "Bearer"))
255+
assert.Equal(t, testSubscriptionKey, request.Header.Get(headerKeySubscriptionKey))
256+
assert.Equal(t, http.StatusOK, response.HTTPResponse.StatusCode)
257+
258+
assert.Equal(t, "JOHN DOE", userInfo.FullName())
259+
260+
// Teardown
261+
server.Close()
262+
}
263+
264+
func TestCollectionService_GetBasicUserinfoWithInternalServerError(t *testing.T) {
265+
// Setup
266+
t.Parallel()
267+
268+
// Arrange
269+
server := helpers.MakeTestServer(http.StatusInternalServerError, nil)
270+
client := New(WithBaseURL(server.URL))
271+
272+
// Act
273+
_, _, err := client.Collection.GetBasicUserinfo(context.Background(), AccountHolderIDTypeMSISDN, "23777777777")
274+
275+
// Assert
276+
assert.NotNil(t, err)
277+
278+
// Teardown
279+
server.Close()
280+
}

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
module github.com/NdoleStudio/mtnmomo-go
22

3-
go 1.17
3+
go 1.18
44

55
require (
66
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2
77
github.com/davecgh/go-spew v1.1.1
88
github.com/dustin/go-humanize v1.0.1
9-
github.com/fatih/color v1.17.0
9+
github.com/fatih/color v1.18.0
1010
github.com/google/uuid v1.3.0
1111
github.com/stretchr/testify v1.8.1
1212
)
1313

1414
require (
1515
github.com/mattn/go-colorable v0.1.13 // indirect
1616
github.com/mattn/go-isatty v0.0.20 // indirect
17-
golang.org/x/sys v0.24.0 // indirect
17+
golang.org/x/sys v0.26.0 // indirect
1818
)
1919

2020
require (

go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
1010
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
1111
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
1212
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
13+
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
14+
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
1315
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
1416
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
1517
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@@ -34,8 +36,11 @@ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
3436
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
3537
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
3638
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
39+
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
40+
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
3741
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
3842
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3943
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
4044
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
45+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
4146
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

internal/helpers/test_helper.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package helpers
33
import (
44
"bytes"
55
"context"
6-
"io/ioutil"
6+
"io"
77
"net/http"
88
"net/http/httptest"
99
)
@@ -26,12 +26,12 @@ func MakeRequestCapturingTestServer(responseCode int, responses [][]byte, reques
2626
clonedRequest := request.Clone(context.Background())
2727

2828
// clone body
29-
body, err := ioutil.ReadAll(request.Body)
29+
body, err := io.ReadAll(request.Body)
3030
if err != nil {
3131
panic(err)
3232
}
33-
request.Body = ioutil.NopCloser(bytes.NewReader(body))
34-
clonedRequest.Body = ioutil.NopCloser(bytes.NewReader(body))
33+
request.Body = io.NopCloser(bytes.NewReader(body))
34+
clonedRequest.Body = io.NopCloser(bytes.NewReader(body))
3535

3636
*requests = append(*requests, clonedRequest)
3737

internal/stubs/collection.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,23 @@ func CollectionAccountBalance() []byte {
3737
}
3838
`)
3939
}
40+
41+
// CollectionValidateAccountHolderStatus is a dummy json response for the `/collection/v1_0/accountholder/{accountHolderIdType}/{accountHolderId}/active` endpoint
42+
func CollectionValidateAccountHolderStatus() []byte {
43+
return []byte(`
44+
{
45+
"result": true
46+
}
47+
`)
48+
}
49+
50+
// CollectionGetBasicUserinfo is a dummy json response for the `/collection/v1_0/accountholder/{accountHolderIdType}/{accountHolderId}/basicuserinfo` endpoint
51+
func CollectionGetBasicUserinfo() []byte {
52+
return []byte(`
53+
{
54+
"family_name": "JOHN",
55+
"given_name": "DOE",
56+
"sub": "1111111"
57+
}
58+
`)
59+
}

0 commit comments

Comments
 (0)