diff --git a/examples/apis/preapprovalplan/create/main.go b/examples/apis/preapprovalplan/create/main.go new file mode 100644 index 00000000..b9965589 --- /dev/null +++ b/examples/apis/preapprovalplan/create/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "context" + "fmt" + "github.com/mercadopago/sdk-go/pkg/config" + "github.com/mercadopago/sdk-go/pkg/preapprovalplan" +) + +func main() { + cfg, err := config.New("{{ACCESS_TOKEN}}") + if err != nil { + fmt.Println(err) + return + } + + req := preapprovalplan.Request{ + AutoRecurring: &preapprovalplan.AutoRecurringRequest{ + Frequency: 1, + FrequencyType: "days", + TransactionAmount: 5, + CurrencyID: "BRL", + }, + BackURL: "https://www.yoursite.com", + PaymentMethodsAllowed: &preapprovalplan.PaymentMethodsAllowedRequest{ + PaymentTypes: []preapprovalplan.PaymentTypeRequest{ + { + ID: "credit_card", + }, + }, + PaymentMethods: []preapprovalplan.PaymentMethodRequest{ + { + ID: "bolbradesco", + }, + }, + }, + Reason: "Yoga classes", + } + + client := preapprovalplan.NewClient(cfg) + result, err := client.Create(context.Background(), req) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(result) +} diff --git a/examples/apis/preapprovalplan/get/main.go b/examples/apis/preapprovalplan/get/main.go new file mode 100644 index 00000000..039ba0db --- /dev/null +++ b/examples/apis/preapprovalplan/get/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "context" + "fmt" + "github.com/mercadopago/sdk-go/pkg/preapprovalplan" + + "github.com/mercadopago/sdk-go/pkg/config" +) + +func main() { + cfg, err := config.New("{{ACCESS_TOKEN}}") + if err != nil { + fmt.Println(err) + return + } + + client := preapprovalplan.NewClient(cfg) + + preApprovalPlanID := "123" + + result, err := client.Get(context.Background(), preApprovalPlanID) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(result) +} diff --git a/examples/apis/preapprovalplan/search/main.go b/examples/apis/preapprovalplan/search/main.go new file mode 100644 index 00000000..46103f8f --- /dev/null +++ b/examples/apis/preapprovalplan/search/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "context" + "fmt" + "github.com/mercadopago/sdk-go/pkg/preapprovalplan" + + "github.com/mercadopago/sdk-go/pkg/config" +) + +func main() { + cfg, err := config.New("{{ACCESS_TOKEN}}") + if err != nil { + fmt.Println(err) + return + } + + client := preapprovalplan.NewClient(cfg) + + filters := preapprovalplan.SearchRequest{ + Limit: "10", + Offset: "10", + Filters: map[string]string{ + "status": "active", + }, + } + + result, err := client.Search(context.Background(), filters) + if err != nil { + fmt.Println(err) + return + } + + for _, plan := range result.Results { + fmt.Println(plan) + } +} diff --git a/examples/apis/preapprovalplan/update/main.go b/examples/apis/preapprovalplan/update/main.go new file mode 100644 index 00000000..c20b3f2e --- /dev/null +++ b/examples/apis/preapprovalplan/update/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "context" + "fmt" + "github.com/mercadopago/sdk-go/pkg/preapprovalplan" + + "github.com/mercadopago/sdk-go/pkg/config" +) + +func main() { + cfg, err := config.New("{{ACCESS_TOKEN}}") + if err != nil { + fmt.Println(err) + return + } + + req := preapprovalplan.Request{ + AutoRecurring: &preapprovalplan.AutoRecurringRequest{ + Frequency: 1, + FrequencyType: "days", + TransactionAmount: 5, + CurrencyID: "BRL", + }, + BackURL: "https://www.yoursite.com", + PaymentMethodsAllowed: &preapprovalplan.PaymentMethodsAllowedRequest{ + PaymentTypes: []preapprovalplan.PaymentTypeRequest{ + { + ID: "credit_card", + }, + }, + PaymentMethods: []preapprovalplan.PaymentMethodRequest{ + { + ID: "bolbradesco", + }, + }, + }, + Reason: "Yoga classes", + } + + client := preapprovalplan.NewClient(cfg) + + result, err := client.Create(context.Background(), req) + if err != nil { + fmt.Println(err) + return + } + + req = preapprovalplan.Request{ + AutoRecurring: &preapprovalplan.AutoRecurringRequest{ + Frequency: 1, + FrequencyType: "months", + TransactionAmount: 10, + BillingDay: 10, + Repetitions: 12, + CurrencyID: "BRL", + }, + } + + result, err = client.Update(context.Background(), req, result.ID) + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(result) +} diff --git a/pkg/preapprovalplan/client.go b/pkg/preapprovalplan/client.go new file mode 100644 index 00000000..8ee65d68 --- /dev/null +++ b/pkg/preapprovalplan/client.go @@ -0,0 +1,95 @@ +package preapprovalplan + +import ( + "context" + "fmt" + "github.com/mercadopago/sdk-go/pkg/config" + "github.com/mercadopago/sdk-go/pkg/internal/baseclient" + "net/url" + "strings" +) + +const ( + urlBase = "https://api.mercadopago.com/preapproval_plan" + urlWithID = urlBase + "/{id}" + urlSearch = urlBase + "/search" +) + +// Client contains the methods to interact with the Pre Approval Plan API. +type Client interface { + // Create creates a new pre-approval plan. + // It is a post request to the endpoint: https://api.mercadopago.com/preapproval_plan + // Reference: https://www.mercadopago.com/developers/en/reference/subscriptions/_preapproval_plan/post/ + Create(ctx context.Context, request Request) (*Response, error) + + // Get finds a pre-approval plan by ID. + // It is a get request to the endpoint: https://api.mercadopago.com/preapproval_plan/{id} + // Reference: https://www.mercadopago.com/developers/en/reference/subscriptions/_preapproval_plan_id/get + Get(ctx context.Context, id string) (*Response, error) + + // Update updates details a pre-approval plan by ID. + // It is a put request to the endpoint: https://api.mercadopago.com/preapproval_plan/{id} + // Reference: https://www.mercadopago.com/developers/en/reference/subscriptions/_preapproval_plan_id/put + Update(ctx context.Context, request Request, id string) (*Response, error) + + // Search finds all pre-approval plan information generated through specific filters. + // It is a get request to the endpoint: https://api.mercadopago.com/preapproval_plan/search + // Reference: https://www.mercadopago.com/developers/en/reference/subscriptions/_preapproval_plan_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 Pre Approval Plan API Client. +func NewClient(c *config.Config) Client { + return &client{ + cfg: c, + } +} + +func (c *client) Create(ctx context.Context, request Request) (*Response, error) { + result, err := baseclient.Post[*Response](ctx, c.cfg, urlBase, request) + if err != nil { + return nil, err + } + + return result, nil +} + +func (c *client) Get(ctx context.Context, id string) (*Response, error) { + result, err := baseclient.Get[*Response](ctx, c.cfg, strings.Replace(urlWithID, "{id}", id, 1)) + if err != nil { + return nil, err + } + + return result, nil +} + +func (c *client) Update(ctx context.Context, request Request, id string) (*Response, error) { + result, err := baseclient.Put[*Response](ctx, c.cfg, strings.Replace(urlWithID, "{id}", id, 1), request) + if err != nil { + return nil, err + } + + return result, 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 url: %w", err) + } + parsedURL.RawQuery = params + + result, err := baseclient.Get[*SearchResponse](ctx, c.cfg, parsedURL.String()) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/pkg/preapprovalplan/client_test.go b/pkg/preapprovalplan/client_test.go new file mode 100644 index 00000000..e6851060 --- /dev/null +++ b/pkg/preapprovalplan/client_test.go @@ -0,0 +1,468 @@ +package preapprovalplan + +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 ( + createResponseJSON, _ = os.Open("../../resources/mocks/preapproval_plan/create_response.json") + createResponse, _ = io.ReadAll(createResponseJSON) + getResponseJSON, _ = os.Open("../../resources/mocks/preapproval_plan/get_response.json") + getResponse, _ = io.ReadAll(getResponseJSON) + updateResponseJSON, _ = os.Open("../../resources/mocks/preapproval_plan/update_response.json") + updateResponse, _ = io.ReadAll(updateResponseJSON) + searchResponseJSON, _ = os.Open("../../resources/mocks/preapproval_plan/search_response.json") + searchResponse, _ = io.ReadAll(searchResponseJSON) +) + +func TestCreate(t *testing.T) { + type fields struct { + config *config.Config + } + type args struct { + ctx context.Context + request Request + } + tests := []struct { + name string + fields fields + args args + want *Response + wantErr string + }{ + { + name: "should_fail_to_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(createResponse)) + stringReadCloser := io.NopCloser(stringReader) + return &http.Response{ + Body: stringReadCloser, + }, nil + }, + }, + }, + }, + args: args{ + ctx: context.Background(), + }, + want: &Response{ + ID: "2c938084726fca480172750000000000", + CollectorID: 100200300, + ApplicationID: 1234567812345678, + Status: "active", + DateCreated: parseDate("2024-02-27T17:37:06.459-04:00"), + LastModified: parseDate("2024-02-27T17:37:06.459-04:00"), + InitPoint: "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_plan_id=2c938084726fca480172750000000000", + AutoRecurring: AutoRecurringResponse{ + Frequency: 1, + FrequencyType: "days", + TransactionAmount: 5, + CurrencyID: "BRL", + }, + BackURL: "https://www.yoursite.com", + PaymentMethodsAllowed: PaymentMethodsAllowedResponse{ + PaymentTypes: []PaymentTypeResponse{ + { + ID: "credit_card", + }, + }, + PaymentMethods: []PaymentMethodResponse{ + { + ID: "bolbradesco", + }, + }, + }, + Reason: "Yoga classes", + }, + wantErr: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &client{ + cfg: tt.fields.config, + } + + got, err := c.Create(tt.args.ctx, tt.args.request) + gotErr := "" + if err != nil { + gotErr = err.Error() + } + + if gotErr != tt.wantErr { + t.Errorf("client.Create() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("client.Create() got = %v, want %v", got, tt.want) + } + }) + } +} + +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: "2c938084726fca480172750000000000", + }, + want: &Response{ + ID: "2c938084726fca480172750000000000", + CollectorID: 100200300, + ApplicationID: 1234567812345678, + Status: "active", + DateCreated: parseDate("2024-02-27T17:37:06.459-04:00"), + LastModified: parseDate("2024-02-27T17:37:06.711-04:00"), + InitPoint: "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_plan_id=2c938084726fca480172750000000000", + AutoRecurring: AutoRecurringResponse{ + Frequency: 1, + FrequencyType: "days", + TransactionAmount: 5, + CurrencyID: "BRL", + }, + BackURL: "https://www.yoursite.com", + PaymentMethodsAllowed: PaymentMethodsAllowedResponse{ + PaymentTypes: []PaymentTypeResponse{ + { + ID: "credit_card", + }, + }, + PaymentMethods: []PaymentMethodResponse{ + { + ID: "bolbradesco", + }, + }, + }, + Reason: "Yoga classes", + }, + 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 TestUpdate(t *testing.T) { + type fields struct { + config *config.Config + } + type args struct { + ctx context.Context + id string + request Request + } + 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(updateResponse)) + stringReadCloser := io.NopCloser(stringReader) + return &http.Response{ + Body: stringReadCloser, + }, nil + }, + }, + }, + }, + args: args{ + ctx: context.Background(), + id: "2c938084726fca480172750000000000", + }, + want: &Response{ + ID: "2c938084726fca480172750000000000", + CollectorID: 100200300, + ApplicationID: 1234567812345678, + Status: "active", + DateCreated: parseDate("2024-02-27T17:37:06.459-04:00"), + LastModified: parseDate("2024-03-02T19:30:37.530-04:00"), + InitPoint: "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_plan_id=2c938084726fca480172750000000000", + AutoRecurring: AutoRecurringResponse{ + Frequency: 1, + FrequencyType: "months", + TransactionAmount: 10, + CurrencyID: "BRL", + Repetitions: 12, + FreeTrial: FreeTrialResponse{ + Frequency: 1, + FrequencyType: "months", + FirstInvoiceOffset: 30, + }, + BillingDay: 10, + BillingDayProportional: false, + }, + BackURL: "https://www.yoursite.com", + PaymentMethodsAllowed: PaymentMethodsAllowedResponse{ + PaymentTypes: []PaymentTypeResponse{ + { + ID: "credit_card", + }, + }, + PaymentMethods: []PaymentMethodResponse{ + { + ID: "bolbradesco", + }, + }, + }, + Reason: "Yoga classes", + }, + wantErr: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &client{ + cfg: tt.fields.config, + } + + got, err := c.Update(tt.args.ctx, tt.args.request, tt.args.id) + gotErr := "" + if err != nil { + gotErr = err.Error() + } + + if gotErr != tt.wantErr { + t.Errorf("client.Create() error = %v, wantErr %v", err, tt.wantErr) + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("client.Create() got = %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: "10", + }, + }, + want: &SearchResponse{ + Paging: PagingResponse{ + Offset: 0, + Total: 10, + Limit: 10, + }, + Results: []Response{ + { + ID: "2c938084726fca480172750000000000", + CollectorID: 100200300, + ApplicationID: 1234567812345678, + Status: "active", + DateCreated: parseDate("2024-02-27T17:37:06.459-04:00"), + LastModified: parseDate("2024-02-27T17:37:06.459-04:00"), + InitPoint: "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_plan_id=2c938084726fca480172750000000000", + AutoRecurring: AutoRecurringResponse{ + Frequency: 1, + FrequencyType: "days", + TransactionAmount: 5, + CurrencyID: "BRL", + }, + BackURL: "https://www.yoursite.com", + PaymentMethodsAllowed: PaymentMethodsAllowedResponse{ + PaymentTypes: []PaymentTypeResponse{ + { + ID: "credit_card", + }, + }, + PaymentMethods: []PaymentMethodResponse{ + { + ID: "bolbradesco", + }, + }, + }, + Reason: "Yoga classes", + }, + }, + }, + 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 +} diff --git a/pkg/preapprovalplan/request.go b/pkg/preapprovalplan/request.go new file mode 100644 index 00000000..1e0240d5 --- /dev/null +++ b/pkg/preapprovalplan/request.go @@ -0,0 +1,46 @@ +package preapprovalplan + +// Request represents a request for creating a pre approval plan. +type Request struct { + AutoRecurring *AutoRecurringRequest `json:"auto_recurring,omitempty"` + PaymentMethodsAllowed *PaymentMethodsAllowedRequest `json:"payment_methods_allowed,omitempty"` + + BackURL string `json:"back_url,omitempty"` + Reason string `json:"reason,omitempty"` +} + +// AutoRecurringRequest represents the recurrence settings. +type AutoRecurringRequest struct { + FreeTrial *FreeTrialRequest `json:"free_trial,omitempty"` + + CurrencyID string `json:"currency_id,omitempty"` + FrequencyType string `json:"frequency_type,omitempty"` + TransactionAmount float64 `json:"transaction_amount,omitempty"` + Frequency int `json:"frequency,omitempty"` + Repetitions int `json:"repetitions,omitempty"` + BillingDay int `json:"billing_day,omitempty"` + BillingDayProportional bool `json:"billing_day_proportional,omitempty"` +} + +// FreeTrialRequest represents the free trial settings. +type FreeTrialRequest struct { + FrequencyType string `json:"frequency_type,omitempty"` + Frequency int `json:"frequency,omitempty"` + FirstInvoiceOffset int `json:"first_invoice_offset,omitempty"` +} + +// PaymentMethodsAllowedRequest represents the Payment Methods enabled at checkout. +type PaymentMethodsAllowedRequest struct { + PaymentTypes []PaymentTypeRequest `json:"payment_types,omitempty"` + PaymentMethods []PaymentMethodRequest `json:"payment_methods,omitempty"` +} + +// PaymentTypeRequest represents the Payment Types allowed in the payment flow. +type PaymentTypeRequest struct { + ID string `json:"id,omitempty"` +} + +// PaymentMethodRequest represents the Payment Methods allowed in the payment flow. +type PaymentMethodRequest struct { + ID string `json:"id,omitempty"` +} diff --git a/pkg/preapprovalplan/response.go b/pkg/preapprovalplan/response.go new file mode 100644 index 00000000..fc9a8987 --- /dev/null +++ b/pkg/preapprovalplan/response.go @@ -0,0 +1,56 @@ +package preapprovalplan + +import "time" + +// Response represents the response from the pre-approval plan endpoint. +type Response struct { + DateCreated *time.Time `json:"date_created"` + LastModified *time.Time `json:"last_modified"` + AutoRecurring AutoRecurringResponse `json:"auto_recurring"` + PaymentMethodsAllowed PaymentMethodsAllowedResponse `json:"payment_methods_allowed"` + + ID string `json:"id"` + BackURL string `json:"back_url"` + AutoReturn string `json:"auto_return"` + Reason string `json:"reason"` + Status string `json:"status"` + InitPoint string `json:"init_point"` + CollectorID int `json:"collector_id"` + ApplicationID int `json:"application_id"` +} + +// AutoRecurringResponse represents the recurrence settings. +type AutoRecurringResponse struct { + FreeTrial FreeTrialResponse `json:"free_trial"` + + CurrencyID string `json:"currency_id"` + FrequencyType string `json:"frequency_type"` + Frequency int `json:"frequency"` + Repetitions int `json:"repetitions"` + BillingDay int `json:"billing_day"` + TransactionAmount float64 `json:"transaction_amount"` + BillingDayProportional bool `json:"billing_day_proportional"` +} + +// FreeTrialResponse represents the free trial settings. +type FreeTrialResponse struct { + FrequencyType string `json:"frequency_type"` + Frequency int `json:"frequency"` + FirstInvoiceOffset int `json:"first_invoice_offset"` +} + +// PaymentMethodsAllowedResponse represents the Payment Methods enabled at checkout. +type PaymentMethodsAllowedResponse struct { + PaymentTypes []PaymentTypeResponse `json:"payment_types"` + PaymentMethods []PaymentMethodResponse `json:"payment_methods"` +} + +// PaymentTypeResponse represents the Payment Types allowed in the payment flow. +type PaymentTypeResponse struct { + ID string `json:"id"` +} + +// PaymentMethodResponse represents the Payment Methods allowed in the payment flow. +type PaymentMethodResponse struct { + ID string `json:"id"` +} diff --git a/pkg/preapprovalplan/search_request.go b/pkg/preapprovalplan/search_request.go new file mode 100644 index 00000000..e8134d60 --- /dev/null +++ b/pkg/preapprovalplan/search_request.go @@ -0,0 +1,40 @@ +package preapprovalplan + +import "net/url" + +// SearchRequest contains filters accepted in search. +// Filters field can receive a lot of parameters. For details, see: +// https://www.mercadopago.com/developers/en/reference/subscriptions/_preapproval_plan_id/get +type SearchRequest struct { + Filters map[string]string + + Limit string + Offset string +} + +// Parameters transforms SearchRequest into url params. +func (s SearchRequest) Parameters() string { + params := url.Values{} + + for k, v := range s.Filters { + params.Add(k, v) + } + + if _, ok := s.Filters["limit"]; !ok { + limit := "30" + if s.Limit != "" { + limit = s.Limit + } + params.Add("limit", limit) + } + + if _, ok := s.Filters["offset"]; !ok { + offset := "0" + if s.Offset != "" { + offset = s.Offset + } + params.Add("offset", offset) + } + + return params.Encode() +} diff --git a/pkg/preapprovalplan/search_response.go b/pkg/preapprovalplan/search_response.go new file mode 100644 index 00000000..b459f5e7 --- /dev/null +++ b/pkg/preapprovalplan/search_response.go @@ -0,0 +1,14 @@ +package preapprovalplan + +// SearchResponse represents the response from the search endpoint. +type SearchResponse struct { + Results []Response `json:"results"` + Paging PagingResponse `json:"paging"` +} + +// PagingResponse represents the paging information within SearchResponse. +type PagingResponse struct { + Total int `json:"total"` + Limit int `json:"limit"` + Offset int `json:"offset"` +} diff --git a/resources/mocks/preapproval_plan/create_response.json b/resources/mocks/preapproval_plan/create_response.json new file mode 100644 index 00000000..0fe4970b --- /dev/null +++ b/resources/mocks/preapproval_plan/create_response.json @@ -0,0 +1,29 @@ +{ + "id": "2c938084726fca480172750000000000", + "back_url": "https://www.yoursite.com", + "collector_id": 100200300, + "application_id": 1234567812345678, + "reason": "Yoga classes", + "status": "active", + "date_created": "2024-02-27T17:37:06.459-04:00", + "last_modified": "2024-02-27T17:37:06.459-04:00", + "init_point": "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_plan_id=2c938084726fca480172750000000000", + "auto_recurring": { + "frequency": 1, + "frequency_type": "days", + "transaction_amount": 5, + "currency_id": "BRL" + }, + "payment_methods_allowed": { + "payment_types": [ + { + "id": "credit_card" + } + ], + "payment_methods": [ + { + "id": "bolbradesco" + } + ] + } +} \ No newline at end of file diff --git a/resources/mocks/preapproval_plan/get_response.json b/resources/mocks/preapproval_plan/get_response.json new file mode 100644 index 00000000..7b1e90ce --- /dev/null +++ b/resources/mocks/preapproval_plan/get_response.json @@ -0,0 +1,29 @@ +{ + "id": "2c938084726fca480172750000000000", + "back_url": "https://www.yoursite.com", + "collector_id": 100200300, + "application_id": 1234567812345678, + "reason": "Yoga classes", + "status": "active", + "date_created": "2024-02-27T17:37:06.459-04:00", + "last_modified": "2024-02-27T17:37:06.711-04:00", + "init_point": "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_plan_id=2c938084726fca480172750000000000", + "auto_recurring": { + "frequency": 1, + "frequency_type": "days", + "transaction_amount": 5.00, + "currency_id": "BRL" + }, + "payment_methods_allowed": { + "payment_types": [ + { + "id": "credit_card" + } + ], + "payment_methods": [ + { + "id": "bolbradesco" + } + ] + } +} \ No newline at end of file diff --git a/resources/mocks/preapproval_plan/search_response.json b/resources/mocks/preapproval_plan/search_response.json new file mode 100644 index 00000000..f6cc64b4 --- /dev/null +++ b/resources/mocks/preapproval_plan/search_response.json @@ -0,0 +1,38 @@ +{ + "paging": { + "offset": 0, + "limit": 10, + "total": 10 + }, + "results": [ + { + "id": "2c938084726fca480172750000000000", + "back_url": "https://www.yoursite.com", + "collector_id": 100200300, + "application_id": 1234567812345678, + "reason": "Yoga classes", + "status": "active", + "date_created": "2024-02-27T17:37:06.459-04:00", + "last_modified": "2024-02-27T17:37:06.459-04:00", + "init_point": "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_plan_id=2c938084726fca480172750000000000", + "auto_recurring": { + "frequency": 1, + "frequency_type": "days", + "transaction_amount": 5, + "currency_id": "BRL" + }, + "payment_methods_allowed": { + "payment_types": [ + { + "id": "credit_card" + } + ], + "payment_methods": [ + { + "id": "bolbradesco" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/resources/mocks/preapproval_plan/update_response.json b/resources/mocks/preapproval_plan/update_response.json new file mode 100644 index 00000000..07ee9ed5 --- /dev/null +++ b/resources/mocks/preapproval_plan/update_response.json @@ -0,0 +1,37 @@ +{ + "id": "2c938084726fca480172750000000000", + "back_url": "https://www.yoursite.com", + "collector_id": 100200300, + "application_id": 1234567812345678, + "reason": "Yoga classes", + "status": "active", + "date_created": "2024-02-27T17:37:06.459-04:00", + "last_modified": "2024-03-02T19:30:37.530-04:00", + "init_point": "https://www.mercadopago.com.br/subscriptions/checkout?preapproval_plan_id=2c938084726fca480172750000000000", + "auto_recurring": { + "frequency": 1, + "frequency_type": "months", + "transaction_amount": 10, + "currency_id": "BRL", + "repetitions": 12, + "free_trial": { + "frequency": 1, + "frequency_type": "months", + "first_invoice_offset": 30 + }, + "billing_day": 10, + "billing_day_proportional": false + }, + "payment_methods_allowed": { + "payment_types": [ + { + "id": "credit_card" + } + ], + "payment_methods": [ + { + "id": "bolbradesco" + } + ] + } +} \ No newline at end of file diff --git a/test/integration/preapproval_plan/preapproval_plan_test.go b/test/integration/preapproval_plan/preapproval_plan_test.go new file mode 100644 index 00000000..a7a6792b --- /dev/null +++ b/test/integration/preapproval_plan/preapproval_plan_test.go @@ -0,0 +1,182 @@ +package integration + +import ( + "context" + "github.com/mercadopago/sdk-go/pkg/config" + "github.com/mercadopago/sdk-go/pkg/preapprovalplan" + "os" + "testing" +) + +func TestPreApprovalPlan(t *testing.T) { + t.Run("should_create_preapproval_plan", func(t *testing.T) { + cfg, err := config.New(os.Getenv("ACCESS_TOKEN")) + if err != nil { + t.Fatal(err) + } + + client := preapprovalplan.NewClient(cfg) + + req := preapprovalplan.Request{ + AutoRecurring: &preapprovalplan.AutoRecurringRequest{ + Frequency: 1, + FrequencyType: "days", + TransactionAmount: 5, + CurrencyID: "BRL", + }, + BackURL: "https://www.yoursite.com", + PaymentMethodsAllowed: &preapprovalplan.PaymentMethodsAllowedRequest{ + PaymentTypes: []preapprovalplan.PaymentTypeRequest{ + { + ID: "credit_card", + }, + }, + PaymentMethods: []preapprovalplan.PaymentMethodRequest{ + { + ID: "bolbradesco", + }, + }, + }, + Reason: "Yoga classes", + } + + result, err := client.Create(context.Background(), req) + if result == nil || result.ID == "" { + t.Error("preapproval_plan can't be nil") + } + if err != nil { + t.Errorf(err.Error()) + } + }) + + t.Run("should_get_preapproval_plan", func(t *testing.T) { + cfg, err := config.New(os.Getenv("ACCESS_TOKEN")) + if err != nil { + t.Fatal(err) + } + + client := preapprovalplan.NewClient(cfg) + + req := preapprovalplan.Request{ + AutoRecurring: &preapprovalplan.AutoRecurringRequest{ + Frequency: 1, + FrequencyType: "days", + TransactionAmount: 5, + CurrencyID: "BRL", + }, + BackURL: "https://www.yoursite.com", + PaymentMethodsAllowed: &preapprovalplan.PaymentMethodsAllowedRequest{ + PaymentTypes: []preapprovalplan.PaymentTypeRequest{ + { + ID: "credit_card", + }, + }, + PaymentMethods: []preapprovalplan.PaymentMethodRequest{ + { + ID: "bolbradesco", + }, + }, + }, + Reason: "Yoga classes", + } + + result, err := client.Create(context.Background(), req) + if result == nil { + t.Error("preapproval_plan can't be nil") + } + if err != nil { + t.Errorf(err.Error()) + return + } + + result, err = client.Get(context.Background(), result.ID) + if result == nil || result.ID == "" { + t.Error("preapproval_plan can't be nil") + } + if err != nil { + t.Errorf(err.Error()) + } + }) + + t.Run("should_update_preapproval_plan", func(t *testing.T) { + cfg, err := config.New(os.Getenv("ACCESS_TOKEN")) + if err != nil { + t.Fatal(err) + } + + client := preapprovalplan.NewClient(cfg) + + req := preapprovalplan.Request{ + AutoRecurring: &preapprovalplan.AutoRecurringRequest{ + Frequency: 1, + FrequencyType: "days", + TransactionAmount: 5, + CurrencyID: "BRL", + }, + BackURL: "https://www.yoursite.com", + PaymentMethodsAllowed: &preapprovalplan.PaymentMethodsAllowedRequest{ + PaymentTypes: []preapprovalplan.PaymentTypeRequest{ + { + ID: "credit_card", + }, + }, + PaymentMethods: []preapprovalplan.PaymentMethodRequest{ + { + ID: "bolbradesco", + }, + }, + }, + Reason: "Yoga classes", + } + + result, err := client.Create(context.Background(), req) + if result == nil { + t.Error("preapproval_plan can't be nil") + } + if err != nil { + t.Errorf(err.Error()) + return + } + + req = preapprovalplan.Request{ + AutoRecurring: &preapprovalplan.AutoRecurringRequest{ + Frequency: 1, + FrequencyType: "months", + TransactionAmount: 10, + BillingDay: 10, + Repetitions: 12, + CurrencyID: "BRL", + }, + } + + result, err = client.Update(context.Background(), req, result.ID) + if result == nil { + t.Error("preapproval_plan can't be nil") + } + if err != nil { + t.Errorf(err.Error()) + } + }) + + t.Run("should_search_preapproval_plan", func(t *testing.T) { + cfg, err := config.New(os.Getenv("ACCESS_TOKEN")) + if err != nil { + t.Fatal(err) + } + + filters := preapprovalplan.SearchRequest{ + Limit: "10", + Offset: "10", + } + + client := preapprovalplan.NewClient(cfg) + result, err := client.Search(context.Background(), filters) + + if result == nil || result.Results[0].ID == "" { + t.Error("preapproval_plan can't be nil") + } + if err != nil { + t.Errorf(err.Error()) + } + }) +}