Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/card token #7

Merged
merged 16 commits into from
Feb 9, 2024
1 change: 1 addition & 0 deletions .testcoverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ exclude:
paths:
- ^pkg/internal
- ^pkg/config
- ^pkg/cardtoken/mock.go
43 changes: 43 additions & 0 deletions examples/apis/cardtoken/create/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package create

import (
"context"
"fmt"

"github.com/mercadopago/sdk-go/pkg/cardtoken"
"github.com/mercadopago/sdk-go/pkg/config"
)

func main() {
accessToken := "{{ACCESS_TOKEN}}"

cfg, err := config.New(accessToken)
if err != nil {
fmt.Println(err)
return
}

client := cardtoken.NewClient(cfg)

var req = cardtoken.Request{
SiteID: "{{SITE_ID}}",
CardNumber: "{{CARD_NUMBER}}",
ExpirationMonth: "11",
ExpirationYear: "2025",
SecurityCode: "123",
Cardholder: &cardtoken.Cardholder{
Identification: &cardtoken.Identification{
Type: "CPF",
Number: "{{CPF_NUMBER}}",
},
Name: "{{PAYMENT_METHOD}}",
},
}

result, err := client.Create(context.Background(), req)
if err != nil {
return
}

fmt.Println(result)
}
33 changes: 33 additions & 0 deletions pkg/cardtoken/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cardtoken

import (
"context"

"github.com/mercadopago/sdk-go/pkg/config"
"github.com/mercadopago/sdk-go/pkg/internal/httpclient"
)

const url = "https://api.mercadopago.com/v1/card_tokens"

// Client contains the method to interact with the card token API.
type Client interface {
meliguilhermefernandes marked this conversation as resolved.
Show resolved Hide resolved
// Create create a card token.
// It is a post request to the endpoint: https://api.mercadopago.com/v1/card_tokens
Create(ctx context.Context, request Request) (*Response, error)
meliguilhermefernandes marked this conversation as resolved.
Show resolved Hide resolved
}

type client struct {
cfg *config.Config
}

func NewClient(c *config.Config) Client {
return &client{cfg: c}
}

func (c *client) Create(ctx context.Context, request Request) (*Response, error) {
res, err := httpclient.Post[Response](ctx, c.cfg, url, request)
if err != nil {
return nil, err
}
return res, nil
}
95 changes: 95 additions & 0 deletions pkg/cardtoken/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package cardtoken

import (
"context"
"fmt"
"io"
"net/http"
"os"
"reflect"
"strings"
"testing"

"github.com/mercadopago/sdk-go/pkg/config"
"github.com/mercadopago/sdk-go/pkg/internal/httpclient"
)

var (
cardTokenResponseJSON, _ = os.Open("../../resources/mocks/cardtoken/response.json")
cardTokenResponse, _ = io.ReadAll(cardTokenResponseJSON)
)

func TestCreate(t *testing.T) {
type fields struct {
cfg *config.Config
}
type args struct {
ctx context.Context
request Request
}
tests := []struct {
name string
fields fields
args args
want *Response
wantErr string
}{
{
name: "should_create_card_token",
fields: fields{
cfg: &config.Config{
Requester: &httpclient.Mock{
DoMock: func(req *http.Request) (*http.Response, error) {
stringReader := strings.NewReader(string(cardTokenResponse))
stringReadCloser := io.NopCloser(stringReader)
return &http.Response{
Body: stringReadCloser,
}, nil
},
},
},
},
args: args{
ctx: context.Background(),
},
want: mockCardToken(),
wantErr: "",
},
{
name: "should_fail_create_card_token",
fields: fields{
cfg: &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",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &client{
cfg: tt.fields.cfg,
}
got, err := c.Create(tt.args.ctx, tt.args.request)
gotErr := ""
if err != nil {
gotErr = err.Error()
}

if gotErr != tt.wantErr {
t.Errorf("card token client.Create() error = %v, wantErr %v", err, tt.wantErr)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("card token client.Create() = %v, want %v", got, tt.want)
}
})
}
}
52 changes: 52 additions & 0 deletions pkg/cardtoken/mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package cardtoken

import (
"time"
)

func mockCardToken() *Response {
return &Response{
ID: "3d40b34eb41a6d0923e5bc545927c2e9",
FirstSixDigits: "503143",
ExpirationMonth: 11,
ExpirationYear: 2025,
LastFourDigits: "6351",
Cardholder: CardholderResponse{
Identification: IdentificationResponse{
Number: "70383868084",
Type: "CPF",
},
Name: "MASTER TEST",
},
Status: "active",
DateCreated: parseDate("2024-02-08T09:05:42.725-04:00"),
DateLastUpdated: parseDate("2024-02-08T09:05:42.725-04:00"),
DateDue: parseDate("2024-02-16T09:05:42.725-04:00"),
LuhnValidation: true,
LiveMode: false,
CardNumberLength: 16,
SecurityCodeLength: 3,
}
}

func MockCardTokenRequest() Request {
return Request{
SiteID: "Teste",
CardNumber: "5031433215406351",
ExpirationMonth: "11",
ExpirationYear: "2025",
SecurityCode: "123",
Cardholder: &Cardholder{
Identification: &Identification{
Type: "CPF",
Number: "70383868084",
},
Name: "MASTER TEST",
},
}
}

func parseDate(s string) *time.Time {
d, _ := time.Parse(time.RFC3339, s)
return &d
}
20 changes: 20 additions & 0 deletions pkg/cardtoken/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cardtoken

type Request struct {
SiteID string `json:"site_id,omitempty"`
CardNumber string `json:"card_number,omitempty"`
ExpirationYear string `json:"expiration_year,omitempty"`
ExpirationMonth string `json:"expiration_month,omitempty"`
SecurityCode string `json:"security_code,omitempty"`
Cardholder *Cardholder `json:"cardholder,omitempty"`
}

type Cardholder struct {
Identification *Identification `json:"identification,omitempty"`
Name string `json:"name,omitempty"`
}

type Identification struct {
Number string `json:"number,omitempty"`
Type string `json:"type,omitempty"`
}
31 changes: 31 additions & 0 deletions pkg/cardtoken/response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cardtoken

import "time"

type Response struct {
ID string `json:"id"`
FirstSixDigits string `json:"first_six_digits"`
LastFourDigits string `json:"last_four_digits"`
Status string `json:"status"`
LuhnValidation bool `json:"luhn_validation"`
LiveMode bool `json:"live_mode"`
RequireEsc bool `json:"require_esc"`
ExpirationMonth int `json:"expiration_month"`
ExpirationYear int `json:"expiration_year"`
CardNumberLength int `json:"card_number_length"`
SecurityCodeLength int `json:"security_code_length"`
DateCreated *time.Time `json:"date_created"`
DateLastUpdated *time.Time `json:"date_last_updated"`
DateDue *time.Time `json:"date_due"`
Cardholder CardholderResponse `json:"cardholder"`
}

type CardholderResponse struct {
Identification IdentificationResponse `json:"identification,omitempty"`
Name string `json:"name"`
}

type IdentificationResponse struct {
Number string `json:"number"`
Type string `json:"type"`
}
23 changes: 23 additions & 0 deletions resources/mocks/cardtoken/response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"id": "3d40b34eb41a6d0923e5bc545927c2e9",
"first_six_digits": "503143",
"expiration_month": 11,
"expiration_year": 2025,
"last_four_digits": "6351",
"cardholder": {
"identification": {
"number": "70383868084",
"type": "CPF"
},
"name": "MASTER TEST"
},
"status": "active",
"date_created": "2024-02-08T09:05:42.725-04:00",
"date_last_updated": "2024-02-08T09:05:42.725-04:00",
"date_due": "2024-02-16T09:05:42.725-04:00",
"luhn_validation": true,
"live_mode": false,
"require_esc": false,
"card_number_length": 16,
"security_code_length": 3
}
29 changes: 29 additions & 0 deletions test/integration/cardtoken/cardtoken_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cardtoken

import (
"context"
"os"
"testing"

"github.com/mercadopago/sdk-go/pkg/cardtoken"
"github.com/mercadopago/sdk-go/pkg/config"
)

func TestCardToken(t *testing.T) {
t.Run("should_create_card_token", func(t *testing.T) {
cfg, err := config.New(os.Getenv("ACCESS_TOKEN"))
if err != nil {
t.Fatal(err)
}

client := cardtoken.NewClient(cfg)
res, err := client.Create(context.Background(), cardtoken.MockCardTokenRequest())

if res == nil {
t.Error("res can't be nil")
}
if err != nil {
t.Errorf(err.Error())
}
})
}
Loading