From 21b349dbdc59a4553bfa7cfd788a0ad008b3f82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=BClent=20Rahim=20Kazanc=C4=B1?= <58530683+bulentkazanci@users.noreply.github.com> Date: Sat, 2 May 2020 17:42:37 +0300 Subject: [PATCH] Add functions of create, get, update, delete and list webhooks (#146) * Add webhook creation function * Add webhook list function * Add webhook delete function * Add webhook get function * Add webhook update function * Update documentation related to webhooks * Fix addressed issues on code review --- README.md | 41 +++++++++++++++++++++++ go.sum | 0 integration_test.go | 82 +++++++++++++++++++++++++++++++++++++++++++++ types.go | 37 ++++++++++++++++++++ webhooks.go | 70 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 230 insertions(+) create mode 100644 go.sum diff --git a/README.md b/README.md index 1b7a836..0b2ee2f 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,11 @@ Currently supports **v2** only, if you want to use **v1**, use **v1.1.4** git ta * PATCH /v2/payments/billing-plans/***ID*** * POST /v2/payments/billing-agreements * POST /v2/payments/billing-agreements/***TOKEN***/agreement-execute + * POST /v1/notifications/webhooks + * GET /v1/notifications/webhooks + * GET /v1/notifications/webhooks/**ID** + * PATCH /v1/notifications/webhooks/**ID** + * DELETE /v1/notifications/webhooks/**ID** * POST /v1/notifications/verify-webhook-signature ### Missing endpoints @@ -281,6 +286,42 @@ c.GetCreditCard("CARD-ID-123") c.GetCreditCards(nil) ``` +### Webhooks +```go +// Create a webhook +c.CreateWebhook(paypal.CreateWebhookRequest{ + URL: "webhook URL", + EventTypes: []paypal.WebhookEventType{ + paypal.WebhookEventType{ + Name: "PAYMENT.AUTHORIZATION.CREATED", + }, + }, +}) + +// Update a registered webhook +c.UpdateWebhook("WebhookID", []paypal.WebhookField{ + paypal.WebhookField{ + Operation: "replace", + Path: "/event_types", + Value: []interface{}{ + map[string]interface{}{ + "name": "PAYMENT.SALE.REFUNDED", + }, + }, + }, +}) + +// Get a registered webhook +c.GetWebhook("WebhookID") + +// Delete a webhook +c.DeleteWebhook("WebhookID") + +// List registered webhooks +c.ListWebhooks(paypal.AncorTypeApplication) + +``` + ### How to Contribute * Fork a repository diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/integration_test.go b/integration_test.go index b8b5a04..8a6f3ea 100644 --- a/integration_test.go +++ b/integration_test.go @@ -132,3 +132,85 @@ func TestPatchCreditCard(t *testing.T) { t.Errorf("Error is expected for empty update info") } } + +// Creates, gets, and deletes single webhook +func TestCreateAndGetWebhook(t *testing.T) { + c, _ := NewClient(testClientID, testSecret, APIBaseSandBox) + c.GetAccessToken() + + payload := &CreateWebhookRequest{ + URL: "https://example.com/paypal_webhooks", + EventTypes: []WebhookEventType{ + WebhookEventType{ + Name: "PAYMENT.AUTHORIZATION.CREATED", + }, + }, + } + + createdWebhook, err := c.CreateWebhook(payload) + if err != nil { + t.Errorf("Webhook couldn't be created, error %v", err) + } + + _, err = c.GetWebhook(createdWebhook.ID) + if err != nil { + t.Errorf("An error occurred while getting webhook, error %v", err) + } + + err = c.DeleteWebhook(createdWebhook.ID) + if err != nil { + t.Errorf("An error occurred while webhooks deletion, error %v", err) + } +} + +// Creates, updates, and deletes single webhook +func TestCreateAndUpdateWebhook(t *testing.T) { + c, _ := NewClient(testClientID, testSecret, APIBaseSandBox) + c.GetAccessToken() + + creationPayload := &CreateWebhookRequest{ + URL: "https://example.com/paypal_webhooks", + EventTypes: []WebhookEventType{ + WebhookEventType{ + Name: "PAYMENT.AUTHORIZATION.CREATED", + }, + }, + } + + createdWebhook, err := c.CreateWebhook(creationPayload) + if err != nil { + t.Errorf("Webhook couldn't be created, error %v", err) + } + + updatePayload := []WebhookField{ + WebhookField{ + Operation: "replace", + Path: "/event_types", + Value: []interface{}{ + map[string]interface{}{ + "name": "PAYMENT.SALE.REFUNDED", + }, + }, + }, + } + + _, err = c.UpdateWebhook(createdWebhook.ID, updatePayload) + if err != nil { + t.Errorf("Couldn't update webhook, error %v", err) + } + + err = c.DeleteWebhook(createdWebhook.ID) + if err != nil { + t.Errorf("An error occurred while webhooks deletion, error %v", err) + } +} + +func TestListWebhooks(t *testing.T) { + c, _ := NewClient(testClientID, testSecret, APIBaseSandBox) + c.GetAccessToken() + + _, err := c.ListWebhooks(AncorTypeApplication) + if err != nil { + t.Errorf("Cannot registered list webhooks, error %v", err) + } +} diff --git a/types.go b/types.go index de50ac2..b12b67d 100644 --- a/types.go +++ b/types.go @@ -124,6 +124,11 @@ const ( LinkRelActionURL string = "action_url" ) +const ( + AncorTypeApplication string = "APPLICATION" + AncorTypeAccount string = "ACCOUNT" +) + type ( // JSONTime overrides MarshalJson method to format in ISO8601 JSONTime time.Time @@ -963,10 +968,20 @@ type ( UserAction string `json:"user_action,omitempty"` } + // VerifyWebhookResponse struct VerifyWebhookResponse struct { VerificationStatus string `json:"verification_status,omitempty"` } + // Webhook strunct + Webhook struct { + ID string `json:"id"` + URL string `json:"url"` + EventTypes []WebhookEventType `json:"event_types"` + Links []Link `json:"links"` + } + + // WebhookEvent struct WebhookEvent struct { ID string `json:"id"` CreateTime time.Time `json:"create_time"` @@ -979,6 +994,28 @@ type ( ResourceVersion string `json:"resource_version,omitempty"` } + // WebhookEventType struct + WebhookEventType struct { + Name string `json:"name"` + Description string `json:"description"` + } + + // CreateWebhookRequest struct + CreateWebhookRequest struct { + URL string `json:"url"` + EventTypes []WebhookEventType `json:"event_types"` + } + + ListWebhookResponse struct { + Webhooks []Webhook `json:"webhooks"` + } + + WebhookField struct { + Operation string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value"` + } + Resource struct { // Payment Resource type ID string `json:"id,omitempty"` diff --git a/webhooks.go b/webhooks.go index 7cd7625..dd4d0eb 100644 --- a/webhooks.go +++ b/webhooks.go @@ -8,6 +8,76 @@ import ( "net/http" ) +// CreateWebhook - Subscribes your webhook listener to events. +// Endpoint: POST /v1/notifications/webhooks +func (c *Client) CreateWebhook(createWebhookRequest *CreateWebhookRequest) (*Webhook, error) { + req, err := c.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), createWebhookRequest) + webhook := &Webhook{} + if err != nil { + return webhook, err + } + + err = c.SendWithAuth(req, webhook) + return webhook, err +} + +// GetWebhook - Shows details for a webhook, by ID. +// Endpoint: GET /v1/notifications/webhooks/ID +func (c *Client) GetWebhook(webhookID string) (*Webhook, error) { + req, err := c.NewRequest(http.MethodGet, fmt.Sprintf("%s%s%s", c.APIBase, "/v1/notifications/webhooks/", webhookID), nil) + webhook := &Webhook{} + if err != nil { + return webhook, err + } + + err = c.SendWithAuth(req, webhook) + return webhook, err +} + +// UpdateWebhook - Updates a webhook to replace webhook fields with new values. +// Endpoint: PATCH /v1/notifications/webhooks/ID +func (c *Client) UpdateWebhook(webhookID string, fields []WebhookField) (*Webhook, error) { + req, err := c.NewRequest(http.MethodPatch, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), fields) + webhook := &Webhook{} + if err != nil { + return webhook, err + } + + err = c.SendWithAuth(req, webhook) + return webhook, err +} + +// ListWebhooks - Lists webhooks for an app. +// Endpoint: GET /v1/notifications/webhooks +func (c *Client) ListWebhooks(anchorType string) (*ListWebhookResponse, error) { + if len(anchorType) == 0 { + anchorType = AncorTypeApplication + } + req, err := c.NewRequest(http.MethodGet, fmt.Sprintf("%s%s", c.APIBase, "/v1/notifications/webhooks"), nil) + q := req.URL.Query() + q.Add("anchor_type", anchorType) + req.URL.RawQuery = q.Encode() + resp := &ListWebhookResponse{} + if err != nil { + return nil, err + } + + err = c.SendWithAuth(req, resp) + return resp, err +} + +// DeleteWebhook - Deletes a webhook, by ID. +// Endpoint: DELETE /v1/notifications/webhooks/ID +func (c *Client) DeleteWebhook(webhookID string) error { + req, err := c.NewRequest(http.MethodDelete, fmt.Sprintf("%s/v1/notifications/webhooks/%s", c.APIBase, webhookID), nil) + if err != nil { + return err + } + + err = c.SendWithAuth(req, nil) + return err +} + // VerifyWebhookSignature - Use this to verify the signature of a webhook recieved from paypal. // Endpoint: POST /v1/notifications/verify-webhook-signature func (c *Client) VerifyWebhookSignature(httpReq *http.Request, webhookID string) (*VerifyWebhookResponse, error) {