diff --git a/README.md b/README.md index 45b7da7..bc03ecc 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ * GET /v1/payments/sale/**ID** * POST /v1/payments/sale/**ID**/refund * GET /v1/payments/refund/**ID** + * GET /v1/payments/orders/**ID** + * POST /v1/payments/orders/**ID**/authorize + * POST /v1/payments/orders/**ID**/capture + * POST /v1/payments/orders/**ID**/do-void #### Create client @@ -23,7 +27,7 @@ import "github.com/logpacker/paypalsdk" ```go // Create a client instance -c, err := paypalsdk.NewClient("clietnid", "secret", paypalsdk.APIBaseSandBox) +c, err := paypalsdk.NewClient("clientID", "secretID", paypalsdk.APIBaseSandBox) c.SetLogFile("/tpm/paypal-debug.log") // Set log file if necessary ``` @@ -136,20 +140,44 @@ auth, err := c.ReauthorizeAuthorization("AUTH-1", &paypalsdk.Amount{Total: "200" #### Get Sale by ID ```go -sale, err := c.GetSale("1") +sale, err := c.GetSale("SALE-1") ``` #### Refund Sale by ID ```go // Full -refund, err := c.RefundSale("1", nil) +refund, err := c.RefundSale("SALE-1", nil) // Partial -refund, err := c.RefundSale("1", &paypalsdk.Amount{Total: "100", Currency: "USD"}) +refund, err := c.RefundSale("SALE-1", &paypalsdk.Amount{Total: "100", Currency: "USD"}) ``` #### Get Refund by ID ```go -refund, err := c.GetRefund("1") +refund, err := c.GetRefund("ORDER-1") +``` + +#### Get Order by ID + +```go +order, err := c.GetOrder("ORDER-1") +``` + +#### Authorize Order + +```go +auth, err := c.AuthorizeOrder("ORDER-1", &paypalsdk.Amount{Total: "100", Currency: "USD"}) +``` + +#### Capture Order + +```go +capture, err := c.CaptureOrder("ORDER-1", &paypalsdk.Amount{Total: "100", Currency: "USD"}, true, nil) +``` + +#### Void Order + +```go +order, err := c.VoidOrder("ORDER-1") ``` diff --git a/auth_test.go b/auth_test.go index 2a853a1..4b25d36 100644 --- a/auth_test.go +++ b/auth_test.go @@ -1,6 +1,7 @@ package paypalsdk import ( + "fmt" "testing" ) @@ -17,6 +18,8 @@ func TestGetAuthorization(t *testing.T) { if err == nil { t.Errorf("Error must be returned for invalid Auth ID") + } else { + fmt.Println(err.Error()) } } @@ -28,6 +31,8 @@ func TestCaptureAuthorization(t *testing.T) { if err == nil { t.Errorf("Error must be returned for invalid Auth ID") + } else { + fmt.Println(err.Error()) } } @@ -39,6 +44,8 @@ func TestVoidAuthorization(t *testing.T) { if err == nil { t.Errorf("Error must be returned for invalid Auth ID") + } else { + fmt.Println(err.Error()) } } @@ -50,5 +57,7 @@ func TestReauthorizeAuthorization(t *testing.T) { if err == nil { t.Errorf("Error must be returned for invalid Auth ID") + } else { + fmt.Println(err.Error()) } } diff --git a/examples/main.go b/examples/main.go index c0e6b15..8b38927 100644 --- a/examples/main.go +++ b/examples/main.go @@ -91,4 +91,36 @@ func main() { fmt.Println("ERROR: " + err.Error()) } fmt.Println("OK") + + order, err := client.GetOrder("1") + if err == nil { + fmt.Println("DEBUG: OrderID=" + order.ID) + } else { + fmt.Println("ERROR: " + err.Error()) + } + fmt.Println("OK") + + auth, err := client.AuthorizeOrder("1", &paypalsdk.Amount{Total: "100", Currency: "USD"}) + if err == nil { + fmt.Println("DEBUG: AuthID=" + auth.ID) + } else { + fmt.Println("ERROR: " + err.Error()) + } + fmt.Println("OK") + + capture, err := client.CaptureOrder("1", &paypalsdk.Amount{Total: "100", Currency: "USD"}, true, nil) + if err == nil { + fmt.Println("DEBUG: CaptureID=" + capture.ID) + } else { + fmt.Println("ERROR: " + err.Error()) + } + fmt.Println("OK") + + order, err = client.VoidOrder("1") + if err == nil { + fmt.Println("DEBUG: OrderID=" + order.ID) + } else { + fmt.Println("ERROR: " + err.Error()) + } + fmt.Println("OK") } diff --git a/order.go b/order.go new file mode 100644 index 0000000..9ed6dfe --- /dev/null +++ b/order.go @@ -0,0 +1,81 @@ +package paypalsdk + +import "fmt" + +// GetOrder retreives order +func (c *Client) GetOrder(orderID string) (*Order, error) { + order := &Order{} + + req, err := c.NewRequest("GET", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/orders/"+orderID), nil) + if err != nil { + return order, err + } + + err = c.SendWithAuth(req, order) + if err != nil { + return order, err + } + + return order, nil +} + +// AuthorizeOrder POST /v1/payments/orders//authorize +func (c *Client) AuthorizeOrder(orderID string, amount *Amount) (*Authorization, error) { + type authRequest struct { + Amount *Amount `json:"amount"` + } + + auth := &Authorization{} + + req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/orders/"+orderID+"/authorize"), authRequest{Amount: amount}) + if err != nil { + return auth, err + } + + err = c.SendWithAuth(req, auth) + if err != nil { + return auth, err + } + + return auth, nil +} + +// CaptureOrder POST /v1/payments/orders//capture +func (c *Client) CaptureOrder(orderID string, amount *Amount, isFinalCapture bool, currency *Currency) (*Capture, error) { + type captureRequest struct { + Amount *Amount `json:"amount"` + IsFinalCapture bool `json:"is_final_capture"` + Currency *Currency `json:"transaction_fee"` + } + + capture := &Capture{} + + req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/orders/"+orderID+"/capture"), captureRequest{Amount: amount, IsFinalCapture: isFinalCapture, Currency: currency}) + if err != nil { + return capture, err + } + + err = c.SendWithAuth(req, capture) + if err != nil { + return capture, err + } + + return capture, nil +} + +// VoidOrder POST /v1/payments/orders//do-void +func (c *Client) VoidOrder(orderID string) (*Order, error) { + order := &Order{} + + req, err := c.NewRequest("POST", fmt.Sprintf("%s%s", c.APIBase, "/v1/payments/orders/"+orderID+"/do-void"), nil) + if err != nil { + return order, err + } + + err = c.SendWithAuth(req, order) + if err != nil { + return order, err + } + + return order, nil +} diff --git a/order_test.go b/order_test.go new file mode 100644 index 0000000..d00db4d --- /dev/null +++ b/order_test.go @@ -0,0 +1,55 @@ +package paypalsdk + +import ( + "fmt" + "testing" +) + +func TestGetOrder(t *testing.T) { + c, _ := NewClient("clid", "secret", APIBaseSandBox) + c.GetAccessToken() + + _, err := c.GetOrder("1") + + if err == nil { + t.Errorf("GetOrder must be failed") + } else { + fmt.Println(err.Error()) + } +} + +func TestAuthorizeOrder(t *testing.T) { + c, _ := NewClient("clid", "secret", APIBaseSandBox) + c.GetAccessToken() + + _, err := c.AuthorizeOrder("1", &Amount{Total: "100", Currency: "USD"}) + if err == nil { + t.Errorf("AuthorizeOrder must be failed") + } else { + fmt.Println(err.Error()) + } +} + +func TestCaptureOrder(t *testing.T) { + c, _ := NewClient("clid", "secret", APIBaseSandBox) + c.GetAccessToken() + + _, err := c.CaptureOrder("1", &Amount{Total: "100", Currency: "USD"}, true, nil) + if err == nil { + t.Errorf("CaptureOrder must be failed") + } else { + fmt.Println(err.Error()) + } +} + +func TestVoidOrder(t *testing.T) { + c, _ := NewClient("clid", "secret", APIBaseSandBox) + c.GetAccessToken() + + _, err := c.VoidOrder("1") + if err == nil { + t.Errorf("VoidOrder must be failed") + } else { + fmt.Println(err.Error()) + } +} diff --git a/payment_test.go b/payment_test.go index d1f38e6..9a46086 100644 --- a/payment_test.go +++ b/payment_test.go @@ -1,6 +1,7 @@ package paypalsdk import ( + "fmt" "testing" ) @@ -19,6 +20,8 @@ func TestCreateDirectPaypalPayment(t *testing.T) { if err == nil { t.Errorf("Error must be returned for invalid token") + } else { + fmt.Println(err.Error()) } } @@ -32,6 +35,8 @@ func TestGetPayment(t *testing.T) { if err == nil { t.Errorf("Error must be returned for invalid ID") + } else { + fmt.Println(err.Error()) } } @@ -58,5 +63,7 @@ func TestExecuteApprovedPayment(t *testing.T) { if err == nil { t.Errorf("Error must be returned for invalid token") + } else { + fmt.Println(err.Error()) } } diff --git a/sale_test.go b/sale_test.go index 6d7c7fb..d654b20 100644 --- a/sale_test.go +++ b/sale_test.go @@ -1,6 +1,9 @@ package paypalsdk -import "testing" +import ( + "fmt" + "testing" +) func TestGetSale(t *testing.T) { c, _ := NewClient("clid", "secret", APIBaseSandBox) @@ -9,6 +12,8 @@ func TestGetSale(t *testing.T) { _, err := c.GetSale("1") if err == nil { t.Errorf("GetSale must be failed") + } else { + fmt.Println(err.Error()) } } @@ -19,11 +24,15 @@ func TestRefundSale(t *testing.T) { _, err := c.RefundSale("1", nil) if err == nil { t.Errorf("RefundSale must be failed") + } else { + fmt.Println(err.Error()) } _, err = c.RefundSale("1", &Amount{Total: "100", Currency: "USD"}) if err == nil { t.Errorf("RefundSale must be failed") + } else { + fmt.Println(err.Error()) } } @@ -34,5 +43,7 @@ func TestGetRefund(t *testing.T) { _, err := c.GetRefund("1") if err == nil { t.Errorf("GetRefund must be failed") + } else { + fmt.Println(err.Error()) } } diff --git a/types.go b/types.go index d66d06c..2614a31 100644 --- a/types.go +++ b/types.go @@ -15,17 +15,24 @@ const ( ) type ( - // Client represents a Paypal REST API Client - Client struct { - client *http.Client - ClientID string - Secret string - APIBase string - LogFile string // If user set log file name all requests will be logged there - Token *TokenResponse + // Address struct + Address struct { + Line1 string `json:"line1"` + Line2 string `json:"line2,omitempty"` + City string `json:"city"` + CountryCode string `json:"country_code"` + PostalCode string `json:"postal_code,omitempty"` + State string `json:"state,omitempty"` + Phone string `json:"phone,omitempty"` } - // Authorization maps to the authorization object + // Amount to pay + Amount struct { + Currency string `json:"currency"` + Total string `json:"total"` + } + + // Authorization represetns PayPal authorization Authorization struct { Amount *Amount `json:"amount,omitempty"` CreateTime *time.Time `json:"create_time,omitempty"` @@ -40,7 +47,7 @@ type ( ProtectionEligibilityType string `json:"protection_eligibility_type,omitempty"` } - // Capture maps to the capture object + // Capture struct Capture struct { Amount *Amount `json:"amount,omitempty"` IsFinalCapture bool `json:"is_final_capture"` @@ -52,18 +59,17 @@ type ( Links []Links `json:"links,omitempty"` } - // Address maps to address object - Address struct { - Line1 string `json:"line1"` - Line2 string `json:"line2,omitempty"` - City string `json:"city"` - CountryCode string `json:"country_code"` - PostalCode string `json:"postal_code,omitempty"` - State string `json:"state,omitempty"` - Phone string `json:"phone,omitempty"` + // Client represents a Paypal REST API Client + Client struct { + client *http.Client + ClientID string + Secret string + APIBase string + LogFile string // If user set log file name all requests will be logged there + Token *TokenResponse } - // CreditCard maps to credit_card object + // CreditCard struct CreditCard struct { ID string `json:"id,omitempty"` PayerID string `json:"payer_id,omitempty"` @@ -79,7 +85,7 @@ type ( ValidUntil string `json:"valid_until,omitempty"` } - // CreditCardToken maps to credit_card_token object + // CreditCardToken struct CreditCardToken struct { CreditCardID string `json:"credit_card_id"` PayerID string `json:"payer_id,omitempty"` @@ -88,17 +94,10 @@ type ( ExpireMonth string `json:"expire_month,omitempty"` } - // FundingInstrument maps to funding_instrument object - FundingInstrument struct { - CreditCard *CreditCard `json:"credit_card,omitempty"` - CreditCardToken *CreditCardToken `json:"credit_card_token,omitempty"` - } - - // TokenResponse maps to the API response for the /oauth2/token endpoint - TokenResponse struct { - RefreshToken string `json:"refresh_token"` - Token string `json:"access_token"` - Type string `json:"token_type"` + // Currency https://developer.paypal.com/webapps/developer/docs/api/#currency-object + Currency struct { + Currency string `json:"currency,omitempty"` + Value string `json:"value,omitempty"` } // ErrorResponse is used when a response has errors @@ -117,13 +116,57 @@ type ( Issue string `json:"issue"` } - // PaymentResponse structure - PaymentResponse struct { + // ExecuteResponse structure + ExecuteResponse struct { ID string `json:"id"` Links []PaymentLink `json:"links"` + State string `json:"state"` } - // Payer maps to payer object + // FundingInstrument struct + FundingInstrument struct { + CreditCard *CreditCard `json:"credit_card,omitempty"` + CreditCardToken *CreditCardToken `json:"credit_card_token,omitempty"` + } + + // Item struct + Item struct { + Quantity int `json:"quantity"` + Name string `json:"name"` + Price string `json:"price"` + Currency string `json:"currency"` + SKU string `json:"sku,omitempty"` + Description string `json:"description,omitempty"` + Tax string `json:"tax,omitempty"` + } + + // ItemList struct + ItemList struct { + Items []Item `json:"items,omitempty"` + ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"` + } + + // Links struct + Links struct { + Href string `json:"href"` + Rel string `json:"rel"` + Method string `json:"method"` + Enctype string `json:"enctype"` + } + + // Order https://developer.paypal.com/webapps/developer/docs/api/#order-object + Order struct { + ID string `json:"id,omitempty"` + CreateTime *time.Time `json:"create_time,omitempty"` + UpdateTime *time.Time `json:"update_time,omitempty"` + State string `json:"state,omitempty"` + Amount *Amount `json:"amount,omitempty"` + PendingReason string `json:"pending_reason,omitempty"` + ParentPayment string `json:"parent_payment,omitempty"` + Links []Links `json:"links,omitempty"` + } + + // Payer struct Payer struct { PaymentMethod string `json:"payment_method"` FundingInstruments []FundingInstrument `json:"funding_instruments,omitempty"` @@ -131,7 +174,7 @@ type ( Status string `json:"payer_status,omitempty"` } - // PayerInfo maps to payer_info object + // PayerInfo struct PayerInfo struct { Email string `json:"email,omitempty"` FirstName string `json:"first_name,omitempty"` @@ -143,34 +186,7 @@ type ( TaxID string `json:"tax_id,omitempty"` } - // ShippingAddress maps to shipping_address object - ShippingAddress struct { - RecipientName string `json:"recipient_name,omitempty"` - Type string `json:"type,omitempty"` - Line1 string `json:"line1"` - Line2 string `json:"line2,omitempty"` - City string `json:"city"` - CountryCode string `json:"country_code"` - PostalCode string `json:"postal_code,omitempty"` - State string `json:"state,omitempty"` - Phone string `json:"phone,omitempty"` - } - - // RedirectURLs maps to redirect_urls object - RedirectURLs struct { - ReturnURL string `json:"return_url,omitempty"` - CancelURL string `json:"cancel_url,omitempty"` - } - - // Links maps to links object - Links struct { - Href string `json:"href"` - Rel string `json:"rel"` - Method string `json:"method"` - Enctype string `json:"enctype"` - } - - // Payment maps to payment object + // Payment struct Payment struct { Intent string `json:"intent"` Payer *Payer `json:"payer"` @@ -183,50 +199,33 @@ type ( ExperienceProfileID string `json:"experience_profile_id,omitempty"` } - // Item maps to item object - Item struct { - Quantity int `json:"quantity"` - Name string `json:"name"` - Price string `json:"price"` - Currency string `json:"currency"` - SKU string `json:"sku,omitempty"` - Description string `json:"description,omitempty"` - Tax string `json:"tax,omitempty"` - } - - // ItemList maps to item_list object - ItemList struct { - Items []Item `json:"items,omitempty"` - ShippingAddress *ShippingAddress `json:"shipping_address,omitempty"` - } - - // Transaction maps to transaction object - Transaction struct { - Amount *Amount `json:"amount"` - Description string `json:"description,omitempty"` - ItemList *ItemList `json:"item_list,omitempty"` - InvoiceNumber string `json:"invoice_number,omitempty"` - Custom string `json:"custom,omitempty"` - SoftDescriptor string `json:"soft_descriptor,omitempty"` - } - // PaymentLink structure PaymentLink struct { Href string `json:"href"` Rel string `json:"rel"` } - // Amount to pay - Amount struct { - Currency string `json:"currency"` - Total string `json:"total"` - } - - // ExecuteResponse structure - ExecuteResponse struct { + // PaymentResponse structure + PaymentResponse struct { ID string `json:"id"` Links []PaymentLink `json:"links"` - State string `json:"state"` + } + + // RedirectURLs for redirect_urls + RedirectURLs struct { + ReturnURL string `json:"return_url,omitempty"` + CancelURL string `json:"cancel_url,omitempty"` + } + + // Refund will be returned by RefundSale + Refund struct { + ID string `json:"id,omitempty"` + Amount *Amount `json:"amount,omitempty"` + CreateTime *time.Time `json:"create_time,omitempty"` + State string `json:"state,omitempty"` + CaptureID string `json:"capture_id,omitempty"` + ParentPayment string `json:"parent_payment,omitempty"` + UpdateTime *time.Time `json:"update_time,omitempty"` } // Sale will be returned by GetSale @@ -247,15 +246,34 @@ type ( Links []Links `json:"links,omitempty"` } - // Refund will be returned by RefundSale - Refund struct { - ID string `json:"id,omitempty"` - Amount *Amount `json:"amount,omitempty"` - CreateTime *time.Time `json:"create_time,omitempty"` - State string `json:"state,omitempty"` - CaptureID string `json:"capture_id,omitempty"` - ParentPayment string `json:"parent_payment,omitempty"` - UpdateTime *time.Time `json:"update_time,omitempty"` + // ShippingAddress for shipping_address + ShippingAddress struct { + RecipientName string `json:"recipient_name,omitempty"` + Type string `json:"type,omitempty"` + Line1 string `json:"line1"` + Line2 string `json:"line2,omitempty"` + City string `json:"city"` + CountryCode string `json:"country_code"` + PostalCode string `json:"postal_code,omitempty"` + State string `json:"state,omitempty"` + Phone string `json:"phone,omitempty"` + } + + // TokenResponse is for API response for the /oauth2/token endpoint + TokenResponse struct { + RefreshToken string `json:"refresh_token"` + Token string `json:"access_token"` + Type string `json:"token_type"` + } + + // Transaction is for transaction object + Transaction struct { + Amount *Amount `json:"amount"` + Description string `json:"description,omitempty"` + ItemList *ItemList `json:"item_list,omitempty"` + InvoiceNumber string `json:"invoice_number,omitempty"` + Custom string `json:"custom,omitempty"` + SoftDescriptor string `json:"soft_descriptor,omitempty"` } )