From 7e723285e7ed251cdf3d83a60a1b5454f56ce6d6 Mon Sep 17 00:00:00 2001 From: Aliaksandr Pliutau Date: Mon, 19 Dec 2016 12:55:00 +0700 Subject: [PATCH] Fixed #11 --- README.md | 40 +++++++++++++++++++ client.go | 3 +- client_test.go | 1 + examples/main.go | 24 ++++++++++++ payment.go | 2 +- payment_test.go | 6 +-- types.go | 49 +++++++++++++++--------- vault.go | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ vault_test.go | 88 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 290 insertions(+), 22 deletions(-) create mode 100644 vault.go create mode 100644 vault_test.go diff --git a/README.md b/README.md index a039ca2..ba0dc62 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,11 @@ * GET /v1/payment-experience/web-profiles/**ID** * PUT /v1/payment-experience/web-profiles/**ID** * DELETE /v1/payment-experience/web-profiles/**ID** + * POST /v1/vault/credit-cards + * DELETE /v1/vault/credit-cards/**ID** + * PATCH /v1/vault/credit-cards/**ID** + * GET /v1/vault/credit-cards/**ID** + * GET /v1/vault/credit-cards ### Missing endpoints It is possible that some endpoints are missing in this SDK Client, but you can use built-in **paypalsdk** functions to perform a request: **NewClient -> NewRequest -> SendWithAuth** @@ -292,6 +297,41 @@ err := c.SetWebProfile(webprofile) err := c.DeleteWebProfile("XP-CP6S-W9DY-96H8-MVN2") ``` +### Vault + +```go +// https://developer.paypal.com/docs/api/vault/ + +// Store CC +c.StoreCreditCard(paypalsdk.CreditCard{ + Number: "4417119669820331", + Type: "visa", + ExpireMonth: "11", + ExpireYear: "2020", + CVV2: "874", + FirstName: "Foo", + LastName: "Bar", +}) + +// Delete it +c.DeleteCreditCard("CARD-ID-123") + +// Edit it +c.PatchCreditCard("CARD-ID-123", []paypalsdk.CreditCardField{ + paypalsdk.CreditCardField{ + Operation: "replace", + Path: "/billing_address/line1", + Value: "New value", + }, +}) + +// Get it +c.GetCreditCard("CARD-ID-123") + +// get all stored credit cards +c.GetCreditCards(nil) +``` + ### How to Contribute * Fork a repository diff --git a/client.go b/client.go index 7dd8fb0..08c4a29 100644 --- a/client.go +++ b/client.go @@ -135,9 +135,10 @@ func (c *Client) NewRequest(method, url string, payload interface{}) (*http.Requ func (c *Client) log(r *http.Request, resp *http.Response) { if c.Log != nil { reqDump := fmt.Sprintf("%s %s. Data: %s", r.Method, r.URL.String(), r.Form.Encode()) + dump, _ := ioutil.ReadAll(r.Body) + fmt.Println(string(dump)) respDump, _ := httputil.DumpResponse(resp, true) c.Log.Write([]byte("Request: " + reqDump + "\nResponse: " + string(respDump) + "\n\n")) - } } diff --git a/client_test.go b/client_test.go index 04a70b6..706bad0 100644 --- a/client_test.go +++ b/client_test.go @@ -16,6 +16,7 @@ var testSaleID = "4CF18861HF410323U" var testPaymentID = "PAY-5YK922393D847794YKER7MUI" var testPayerID = "CR87QHB7JTRSC" var testUserID = "https://www.paypal.com/webapps/auth/identity/user/WEssgRpQij92sE99_F9MImvQ8FPYgUEjrvCja2qH2H8" +var testCardID = "CARD-54E6956910402550WKGRL6EA" func TestNewClient(t *testing.T) { _, err := NewClient("", "", "") diff --git a/examples/main.go b/examples/main.go index bebccf1..46f4509 100644 --- a/examples/main.go +++ b/examples/main.go @@ -168,4 +168,28 @@ func main() { } else { fmt.Println("ERROR: " + err.Error()) } + + cc := paypalsdk.CreditCard{ + Number: "4417119669820331", + Type: "visa", + ExpireMonth: "11", + ExpireYear: "2020", + CVV2: "874", + FirstName: "Foo", + LastName: "Bar", + } + r1, e1 := c.StoreCreditCard(cc) + fmt.Printf("DEBUG StoreCreditCard: %v, %v\n", r1, e1) + + r2, e2 := c.DeleteCreditCard("123") + fmt.Printf("DEBUG DeleteCreditCard: %v, %v\n", r2, e2) + + r3, e3 := c.PatchCreditCard("123", nil) + fmt.Printf("DEBUG PatchCreditCard: %v, %v\n", r3, e3) + + r4, e4 := c.GetCreditCard("123") + fmt.Printf("DEBUG GetCreditCard: %v, %v\n", r4, e4) + + r5, e5 := c.GetCreditCards(nil) + fmt.Printf("DEBUG GetCreditCards: %v, %v\n", r5, e5) } diff --git a/payment.go b/payment.go index 3a8dbe8..b8d11c8 100644 --- a/payment.go +++ b/payment.go @@ -15,7 +15,7 @@ type ListPaymentsResp struct { // CreatePaymentResp contains Payment Info and Links slice type CreatePaymentResp struct { *Payment - Links []Links `json:"links"` + Links []Link `json:"links"` } // CreateDirectPaypalPayment sends request to create a payment with payment_method=paypal diff --git a/payment_test.go b/payment_test.go index b8007f4..ed088b3 100644 --- a/payment_test.go +++ b/payment_test.go @@ -40,10 +40,10 @@ func TestGetPayments(t *testing.T) { c, _ := NewClient(testClientID, testSecret, APIBaseSandBox) c.GetAccessToken() - payments, _ := c.GetPayments() + _, err := c.GetPayments() - if len(payments) == 0 { - t.Errorf("> 0 payments must be returned for GetPayments. Returned: %d", len(payments)) + if err != nil { + t.Errorf("Nil error expected") } } diff --git a/types.go b/types.go index 392f001..5e1b3ee 100644 --- a/types.go +++ b/types.go @@ -76,7 +76,7 @@ type ( ParentPayment string `json:"parent_payment,omitempty"` ID string `json:"id,omitempty"` ValidUntil *time.Time `json:"valid_until,omitempty"` - Links []Links `json:"links,omitempty"` + Links []Link `json:"links,omitempty"` ClearingTime string `json:"clearing_time,omitempty"` ProtectionEligibility string `json:"protection_eligibility,omitempty"` ProtectionEligibilityType string `json:"protection_eligibility_type,omitempty"` @@ -102,7 +102,7 @@ type ( State string `json:"state,omitempty"` ParentPayment string `json:"parent_payment,omitempty"` ID string `json:"id,omitempty"` - Links []Links `json:"links,omitempty"` + Links []Link `json:"links,omitempty"` } // Client represents a Paypal REST API Client @@ -131,6 +131,14 @@ type ( ValidUntil string `json:"valid_until,omitempty"` } + // CreditCards GET /v1/vault/credit-cards + CreditCards struct { + Items []CreditCard `json:"items"` + Links []Link `json:"links"` + TotalItems int `json:"total_items"` + TotalPages int `json:"total_pages"` + } + // CreditCardToken struct CreditCardToken struct { CreditCardID string `json:"credit_card_id"` @@ -140,6 +148,19 @@ type ( ExpireMonth string `json:"expire_month,omitempty"` } + // CreditCardsFilter struct + CreditCardsFilter struct { + PageSize int + Page int + } + + // CreditCardField PATCH /v1/vault/credit-cards/credit_card_id + CreditCardField struct { + Operation string `json:"op"` + Path string `json:"path"` + Value string `json:"value"` + } + // Currency struct Currency struct { Currency string `json:"currency,omitempty"` @@ -159,7 +180,7 @@ type ( // ExecuteResponse struct ExecuteResponse struct { ID string `json:"id"` - Links []PaymentLink `json:"links"` + Links []Link `json:"links"` State string `json:"state"` Transactions []Transaction `json:"transactions,omitempty"` } @@ -187,8 +208,8 @@ type ( ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"` } - // Links struct - Links struct { + // Link struct + Link struct { Href string `json:"href"` Rel string `json:"rel,omitempty"` Method string `json:"method,omitempty"` @@ -204,7 +225,7 @@ type ( Amount *Amount `json:"amount,omitempty"` PendingReason string `json:"pending_reason,omitempty"` ParentPayment string `json:"parent_payment,omitempty"` - Links []Links `json:"links,omitempty"` + Links []Link `json:"links,omitempty"` } // Payer struct @@ -240,16 +261,10 @@ type ( ExperienceProfileID string `json:"experience_profile_id,omitempty"` } - // PaymentLink struct - PaymentLink struct { - Href string `json:"href"` - Rel string `json:"rel"` - } - // PaymentResponse structure PaymentResponse struct { - ID string `json:"id"` - Links []PaymentLink `json:"links"` + ID string `json:"id"` + Links []Link `json:"links"` } // Payout struct @@ -276,14 +291,14 @@ type ( PayoutItemFee *AmountPayout `json:"payout_item_fee,omitempty"` PayoutItem *PayoutItem `json:"payout_item"` TimeProcessed *time.Time `json:"time_processed,omitempty"` - Links []Links `json:"links"` + Links []Link `json:"links"` } // PayoutResponse struct PayoutResponse struct { BatchHeader *BatchHeader `json:"batch_header"` Items []PayoutItemResponse `json:"items"` - Links []Links `json:"links"` + Links []Link `json:"links"` } // RedirectURLs struct @@ -327,7 +342,7 @@ type ( ClearingTime string `json:"clearing_time,omitempty"` ProtectionEligibility string `json:"protection_eligibility,omitempty"` ProtectionEligibilityType string `json:"protection_eligibility_type,omitempty"` - Links []Links `json:"links,omitempty"` + Links []Link `json:"links,omitempty"` } // SenderBatchHeader struct diff --git a/vault.go b/vault.go new file mode 100644 index 0000000..c7a924e --- /dev/null +++ b/vault.go @@ -0,0 +1,99 @@ +package paypalsdk + +import ( + "fmt" +) + +// StoreCreditCard func +// Endpoint: POST /v1/vault/credit-cards +func (c *Client) StoreCreditCard(cc CreditCard) (*CreditCard, error) { + req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/vault/credit-cards"), cc) + if err != nil { + return nil, err + } + + response := CreditCard{} + err = c.SendWithAuth(req, &response) + if err != nil { + return nil, err + } + + return &response, nil +} + +// DeleteCreditCard func +// Endpoint: DELETE /v1/vault/credit-cards/credit_card_id +func (c *Client) DeleteCreditCard(id string) (*CreditCard, error) { + req, err := c.NewRequest("DELETE", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil) + if err != nil { + return nil, err + } + + response := CreditCard{} + err = c.SendWithAuth(req, &response) + if err != nil { + return nil, err + } + + return &response, nil +} + +// GetCreditCard func +// Endpoint: GET /v1/vault/credit-cards/credit_card_id +func (c *Client) GetCreditCard(id string) (*CreditCard, error) { + req, err := c.NewRequest("GET", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), nil) + if err != nil { + return nil, err + } + + response := CreditCard{} + err = c.SendWithAuth(req, &response) + if err != nil { + return nil, err + } + + return &response, nil +} + +// GetCreditCards func +// Endpoint: GET /v1/vault/credit-cards +func (c *Client) GetCreditCards(ccf *CreditCardsFilter) (*CreditCards, error) { + page := 1 + if ccf != nil && ccf.Page > 0 { + page = ccf.Page + } + pageSize := 10 + if ccf != nil && ccf.PageSize > 0 { + pageSize = ccf.PageSize + } + + req, err := c.NewRequest("GET", fmt.Sprintf("%s/v1/vault/credit-cards?page=%d&page_size=%d", c.APIBase, page, pageSize), nil) + if err != nil { + return nil, err + } + + response := CreditCards{} + err = c.SendWithAuth(req, &response) + if err != nil { + return nil, err + } + + return &response, nil +} + +// PatchCreditCard func +// Endpoint: PATCH /v1/vault/credit-cards/credit_card_id +func (c *Client) PatchCreditCard(id string, ccf []CreditCardField) (*CreditCard, error) { + req, err := c.NewRequest("PATCH", fmt.Sprintf("%s/v1/vault/credit-cards/%s", c.APIBase, id), ccf) + if err != nil { + return nil, err + } + + response := CreditCard{} + err = c.SendWithAuth(req, &response) + if err != nil { + return nil, err + } + + return &response, nil +} diff --git a/vault_test.go b/vault_test.go new file mode 100644 index 0000000..4bfd8d4 --- /dev/null +++ b/vault_test.go @@ -0,0 +1,88 @@ +package paypalsdk + +import ( + "testing" +) + +func TestStoreCreditCard(t *testing.T) { + c, _ := NewClient(testClientID, testSecret, APIBaseSandBox) + c.GetAccessToken() + + r1, e1 := c.StoreCreditCard(CreditCard{}) + if e1 == nil || r1 != nil { + t.Errorf("Error is expected for invalid CC") + } + + r2, e2 := c.StoreCreditCard(CreditCard{ + Number: "4417119669820331", + Type: "visa", + ExpireMonth: "11", + ExpireYear: "2020", + CVV2: "874", + FirstName: "Foo", + LastName: "Bar", + }) + if e2 != nil || r2 == nil { + t.Errorf("200 code expected for valid CC card. Error: %v", e2) + } +} + +func TestDeleteCreditCard(t *testing.T) { + c, _ := NewClient(testClientID, testSecret, APIBaseSandBox) + c.GetAccessToken() + + r1, e1 := c.DeleteCreditCard("") + if e1 == nil || r1 != nil { + t.Errorf("Error is expected for invalid CC ID") + } +} + +func TestGetCreditCard(t *testing.T) { + c, _ := NewClient(testClientID, testSecret, APIBaseSandBox) + c.GetAccessToken() + + r1, e1 := c.GetCreditCard("BBGGG") + if e1 == nil || r1 != nil { + t.Errorf("Error is expected for invalid CC, got CC %v", r1) + } +} + +func TestGetCreditCards(t *testing.T) { + c, _ := NewClient(testClientID, testSecret, APIBaseSandBox) + c.GetAccessToken() + + r1, e1 := c.GetCreditCards(nil) + if e1 != nil || r1 == nil { + t.Errorf("200 code expected. Error: %v", e1) + } + if r1.TotalItems < 1 { + t.Errorf("Expected >0 CCs, got %d", r1.TotalItems) + } + if r1.TotalPages < 1 { + t.Errorf("Expected >0 CCs page") + } + + r2, e2 := c.GetCreditCards(&CreditCardsFilter{ + Page: 2, + PageSize: 7, + }) + if e2 != nil || r2 == nil { + t.Errorf("200 code expected. Error: %v", e2) + } + if r2.TotalItems < 1 { + t.Errorf("Expected >0 CCs, got %d", r2.TotalItems) + } + if r2.TotalPages < 1 { + t.Errorf("Expected >0 CCs page") + } +} + +func TestPatchCreditCard(t *testing.T) { + c, _ := NewClient(testClientID, testSecret, APIBaseSandBox) + c.GetAccessToken() + + r1, e1 := c.PatchCreditCard(testCardID, nil) + if e1 == nil || r1 != nil { + t.Errorf("Error is expected for empty update info") + } +}