-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add implementation, unit tests and example * adjust mock * add integration tests that require refactoring after implementing the preapproval_plan client * Update examples/apis/invoice/get/main.go Co-authored-by: gdeandradero <[email protected]> * Update examples/apis/invoice/search/main.go Co-authored-by: gdeandradero <[email protected]> * adjustments requested in the review * Update pkg/invoice/search_request.go Co-authored-by: gdeandradero <[email protected]> * adjustments requested - organizing types * add unit test to search_request file * adjusts requested to maintain standard * test adjustment so ID is received by parameter * adjust searchrequest test * adjust struct name - PagingResponse * remove return --------- Co-authored-by: gdeandradero <[email protected]>
- Loading branch information
1 parent
fd76586
commit 17c7b45
Showing
10 changed files
with
549 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/mercadopago/sdk-go/pkg/config" | ||
"github.com/mercadopago/sdk-go/pkg/invoice" | ||
) | ||
|
||
func main() { | ||
cfg, err := config.New("{{ACCESS_TOKEN}}") | ||
if err != nil { | ||
fmt.Println(err) | ||
return | ||
} | ||
|
||
client := invoice.NewClient(cfg) | ||
|
||
invoiceID := "123" | ||
|
||
result, err := client.Get(context.Background(), invoiceID) | ||
if err != nil { | ||
fmt.Println(err) | ||
return | ||
} | ||
|
||
fmt.Println(result) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/mercadopago/sdk-go/pkg/config" | ||
"github.com/mercadopago/sdk-go/pkg/invoice" | ||
) | ||
|
||
func main() { | ||
cfg, err := config.New("{{ACCESS_TOKEN}}") | ||
if err != nil { | ||
fmt.Println(err) | ||
return | ||
} | ||
|
||
client := invoice.NewClient(cfg) | ||
|
||
req := invoice.SearchRequest{ | ||
Limit: "10", | ||
Offset: "10", | ||
Filters: map[string]string{ | ||
"preapproval_id": "preapproval_id", | ||
}, | ||
} | ||
|
||
result, err := client.Search(context.Background(), req) | ||
if err != nil { | ||
fmt.Println(err) | ||
return | ||
} | ||
|
||
for _, inv := range result.Results { | ||
fmt.Println(inv) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package invoice | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/mercadopago/sdk-go/pkg/config" | ||
"github.com/mercadopago/sdk-go/pkg/internal/baseclient" | ||
"net/url" | ||
) | ||
|
||
const ( | ||
urlBase = "https://api.mercadopago.com/authorized_payments" | ||
urlSearch = urlBase + "/search" | ||
urlWithID = urlBase + "/:id" | ||
) | ||
|
||
// Client contains the methods to interact with the Invoice API. | ||
type Client interface { | ||
// Get finds an invoice by ID. | ||
// It is a get request to the endpoint: https://api.mercadopago.com/authorized_payments/{id} | ||
// Reference: https://www.mercadopago.com/developers/en/reference/subscriptions/_authorized_payments_id/get | ||
Get(ctx context.Context, id string) (*Response, error) | ||
|
||
// Search the invoices for a subscriptions by different parameters. | ||
// It is a get request to the endpoint: https://api.mercadopago.com/authorized_payments/search | ||
// Reference: https://www.mercadopago.com/developers/en/reference/subscriptions/_authorized_payments_search/get | ||
Search(ctx context.Context, request SearchRequest) (*SearchResponse, error) | ||
} | ||
|
||
// client is the implementation of Client. | ||
type client struct { | ||
cfg *config.Config | ||
} | ||
|
||
// NewClient returns a new Invoice API Client. | ||
func NewClient(c *config.Config) Client { | ||
return &client{ | ||
cfg: c, | ||
} | ||
} | ||
|
||
func (c *client) Get(ctx context.Context, id string) (*Response, error) { | ||
params := map[string]string{ | ||
"id": id, | ||
} | ||
|
||
res, err := baseclient.Get[*Response](ctx, c.cfg, urlWithID, baseclient.WithPathParams(params)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return res, nil | ||
} | ||
|
||
func (c *client) Search(ctx context.Context, request SearchRequest) (*SearchResponse, error) { | ||
params := request.Parameters() | ||
|
||
parsedURL, err := url.Parse(urlSearch) | ||
if err != nil { | ||
return nil, fmt.Errorf("error parsing parseUrl: %w", err) | ||
} | ||
parsedURL.RawQuery = params | ||
|
||
res, err := baseclient.Get[*SearchResponse](ctx, c.cfg, parsedURL.String()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return res, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
package invoice | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/mercadopago/sdk-go/pkg/config" | ||
"github.com/mercadopago/sdk-go/pkg/internal/httpclient" | ||
"io" | ||
"net/http" | ||
"os" | ||
"reflect" | ||
"strings" | ||
"testing" | ||
"time" | ||
) | ||
|
||
var ( | ||
getResponseJSON, _ = os.Open("../../resources/mocks/invoice/get_response.json") | ||
getResponse, _ = io.ReadAll(getResponseJSON) | ||
searchResponseJSON, _ = os.Open("../../resources/mocks/invoice/search_response.json") | ||
searchResponse, _ = io.ReadAll(searchResponseJSON) | ||
) | ||
|
||
func TestGet(t *testing.T) { | ||
type fields struct { | ||
config *config.Config | ||
} | ||
type args struct { | ||
ctx context.Context | ||
id string | ||
} | ||
tests := []struct { | ||
name string | ||
fields fields | ||
args args | ||
want *Response | ||
wantErr string | ||
}{ | ||
{ | ||
name: "should_return_error_when_send_request", | ||
fields: fields{ | ||
config: &config.Config{ | ||
Requester: &httpclient.Mock{ | ||
DoMock: func(req *http.Request) (*http.Response, error) { | ||
return nil, fmt.Errorf("some error") | ||
}, | ||
}, | ||
}, | ||
}, | ||
args: args{ | ||
ctx: context.Background(), | ||
}, | ||
want: nil, | ||
wantErr: "transport level error: some error", | ||
}, | ||
{ | ||
name: "should_return_response", | ||
fields: fields{ | ||
config: &config.Config{ | ||
Requester: &httpclient.Mock{ | ||
DoMock: func(req *http.Request) (*http.Response, error) { | ||
stringReader := strings.NewReader(string(getResponse)) | ||
stringReadCloser := io.NopCloser(stringReader) | ||
return &http.Response{ | ||
Body: stringReadCloser, | ||
}, nil | ||
}, | ||
}, | ||
}, | ||
}, | ||
args: args{ | ||
ctx: context.Background(), | ||
id: "3950169598", | ||
}, | ||
want: &Response{ | ||
PreapprovalID: "202caa5d4084417b8e2a394121bf172b", | ||
ID: 3950169598, | ||
Type: "recurring", | ||
Status: "processed", | ||
DateCreated: parseDate("2024-02-27T17:42:04.835-04:00"), | ||
LastModified: parseDate("2024-02-27T17:45:06.462-04:00"), | ||
TransactionAmount: 5.00, | ||
CurrencyID: "BRL", | ||
Reason: "Yoga classes", | ||
Payment: PaymentResponse{ | ||
ID: 3950169598, | ||
Status: "approved", | ||
StatusDetail: "accredited", | ||
}, | ||
RetryAttempt: 1, | ||
NextRetryDate: parseDate("2024-02-28T17:40:33.000-04:00"), | ||
DebitDate: parseDate("2024-02-27T17:40:32.000-04:00"), | ||
PaymentMethodID: "account_money", | ||
}, | ||
wantErr: "", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
c := &client{ | ||
cfg: tt.fields.config, | ||
} | ||
got, err := c.Get(tt.args.ctx, tt.args.id) | ||
gotErr := "" | ||
if err != nil { | ||
gotErr = err.Error() | ||
} | ||
|
||
if gotErr != tt.wantErr { | ||
t.Errorf("client.Get() error = %v, wantErr %v", err, tt.wantErr) | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("client.Get() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestSearch(t *testing.T) { | ||
type fields struct { | ||
config *config.Config | ||
} | ||
type args struct { | ||
ctx context.Context | ||
request SearchRequest | ||
} | ||
tests := []struct { | ||
name string | ||
fields fields | ||
args args | ||
want *SearchResponse | ||
wantErr string | ||
}{ | ||
{ | ||
name: "should_return_error_when_send_request", | ||
fields: fields{ | ||
config: &config.Config{ | ||
Requester: &httpclient.Mock{ | ||
DoMock: func(req *http.Request) (*http.Response, error) { | ||
return nil, fmt.Errorf("some error") | ||
}, | ||
}, | ||
}, | ||
}, | ||
args: args{ | ||
ctx: context.Background(), | ||
}, | ||
want: nil, | ||
wantErr: "transport level error: some error", | ||
}, | ||
{ | ||
name: "should_return_response", | ||
fields: fields{ | ||
config: &config.Config{ | ||
Requester: &httpclient.Mock{ | ||
DoMock: func(req *http.Request) (*http.Response, error) { | ||
stringReader := strings.NewReader(string(searchResponse)) | ||
stringReadCloser := io.NopCloser(stringReader) | ||
return &http.Response{ | ||
Body: stringReadCloser, | ||
}, nil | ||
}, | ||
}, | ||
}, | ||
}, | ||
args: args{ | ||
ctx: context.Background(), | ||
request: SearchRequest{ | ||
Limit: "12", | ||
}, | ||
}, | ||
want: &SearchResponse{ | ||
Results: []Response{ | ||
{ | ||
PreapprovalID: "202caa5d4084417b8e2a394121bf172b", | ||
ID: 3950169598, | ||
Type: "recurring", | ||
Status: "processed", | ||
DateCreated: parseDate("2024-02-27T17:42:04.835-04:00"), | ||
LastModified: parseDate("2024-02-27T17:45:06.462-04:00"), | ||
TransactionAmount: 5.00, | ||
CurrencyID: "BRL", | ||
Reason: "Yoga classes", | ||
Payment: PaymentResponse{ | ||
ID: 3950169598, | ||
Status: "approved", | ||
StatusDetail: "accredited", | ||
}, | ||
RetryAttempt: 1, | ||
NextRetryDate: parseDate("2024-02-28T17:40:33.000-04:00"), | ||
DebitDate: parseDate("2024-02-27T17:40:32.000-04:00"), | ||
PaymentMethodID: "account_money", | ||
}, | ||
}, | ||
Paging: PagingResponse{ | ||
Offset: 0, | ||
Limit: 12, | ||
Total: 1, | ||
}, | ||
}, | ||
wantErr: "", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
c := &client{ | ||
cfg: tt.fields.config, | ||
} | ||
got, err := c.Search(tt.args.ctx, tt.args.request) | ||
gotErr := "" | ||
if err != nil { | ||
gotErr = err.Error() | ||
} | ||
|
||
if gotErr != tt.wantErr { | ||
t.Errorf("client.Search() error = %v, wantErr %v", err, tt.wantErr) | ||
} | ||
if !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("client.Search() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func parseDate(s string) *time.Time { | ||
d, _ := time.Parse(time.RFC3339, s) | ||
return &d | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package invoice | ||
|
||
import "time" | ||
|
||
// Response is the response from the Invoice API. | ||
type Response struct { | ||
DateCreated *time.Time `json:"date_created"` | ||
DebitDate *time.Time `json:"debit_date"` | ||
LastModified *time.Time `json:"last_modified"` | ||
NextRetryDate *time.Time `json:"next_retry_date"` | ||
Payment PaymentResponse `json:"payment"` | ||
|
||
CurrencyID string `json:"currency_id"` | ||
ExternalReference string `json:"external_reference"` | ||
PaymentMethodID string `json:"payment_method_id"` | ||
PreapprovalID string `json:"preapproval_id"` | ||
Reason string `json:"reason"` | ||
Status string `json:"status"` | ||
Summarized string `json:"summarized"` | ||
Type string `json:"type"` | ||
ID int `json:"id"` | ||
RetryAttempt int `json:"retry_attempt"` | ||
TransactionAmount float64 `json:"transaction_amount"` | ||
} | ||
|
||
// PaymentResponse contains information about payment. | ||
type PaymentResponse struct { | ||
Status string `json:"status"` | ||
StatusDetail string `json:"status_detail"` | ||
ID int `json:"id"` | ||
} |
Oops, something went wrong.