Skip to content

Commit 4291d15

Browse files
authored
Merge pull request #282 from moovfinancial/ledg-3847-update-webhooks-moov-api-and-moov-go
webhooks: add additional methods and tweak existing models
2 parents d5d6582 + 8abda56 commit 4291d15

File tree

5 files changed

+98
-12
lines changed

5 files changed

+98
-12
lines changed

pkg/moov/invoice_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func Test_Invoice_CreateUpdateGet(t *testing.T) {
3131
},
3232
Quantity: 1,
3333
Options: []moov.CreateInvoiceLineItemOption{
34-
moov.CreateInvoiceLineItemOption{
34+
{
3535
Name: "big TIP",
3636
Quantity: 1,
3737
PriceModifier: &moov.AmountDecimal{

pkg/moov/paths.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,11 @@ const (
4747
pathWalletTransactions = "/accounts/%s/wallets/%s/transactions"
4848
pathWalletTransaction = "/accounts/%s/wallets/%s/transactions/%s"
4949

50-
pathWebhooks = "/webhooks"
51-
pathWebhook = "/webhooks/%s"
50+
pathWebhooks = "/webhooks"
51+
pathWebhook = "/webhooks/%s"
52+
pathWebhookPing = "/webhooks/%s/ping"
53+
pathWebhookSecret = "/webhooks/%s/secret" // #nosec G101 - false positive, this is a URL path not a credential
54+
pathEventTypes = "/event-types"
5255

5356
pathSweepConfigs = "/accounts/%s/sweep-configs"
5457
pathSweepConfig = "/accounts/%s/sweep-configs/%s"

pkg/moov/webhook.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"net/http"
66
)
77

8-
func (c *Client) CreateWebhook(ctx context.Context, webhook UpsertWebhook) (*Webhook, error) {
8+
func (c *Client) CreateWebhook(ctx context.Context, webhook CreateWebhook) (*Webhook, error) {
99
resp, err := c.CallHttp(ctx,
1010
Endpoint(http.MethodPost, pathWebhooks),
1111
AcceptJson(),
@@ -39,7 +39,7 @@ func (c *Client) GetWebhook(ctx context.Context, webhookID string) (*Webhook, er
3939
return CompletedObjectOrError[Webhook](resp)
4040
}
4141

42-
func (c *Client) UpdateWebhook(ctx context.Context, webhookID string, webhook UpsertWebhook) (*Webhook, error) {
42+
func (c *Client) UpdateWebhook(ctx context.Context, webhookID string, webhook UpdateWebhook) (*Webhook, error) {
4343
resp, err := c.CallHttp(ctx,
4444
Endpoint(http.MethodPut, pathWebhook, webhookID),
4545
AcceptJson(),
@@ -61,3 +61,41 @@ func (c *Client) DeleteWebhook(ctx context.Context, webhookID string) error {
6161

6262
return CompletedNilOrError(resp)
6363
}
64+
65+
// PingWebhook sends a test event to the webhook URL to verify it is working.
66+
// The ping will send an event with type `event.test` and a null data payload.
67+
func (c *Client) PingWebhook(ctx context.Context, webhookID string) (*WebhookPing, error) {
68+
resp, err := c.CallHttp(ctx,
69+
Endpoint(http.MethodPost, pathWebhookPing, webhookID),
70+
AcceptJson())
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
return CompletedObjectOrError[WebhookPing](resp)
76+
}
77+
78+
// GetWebhookSecret retrieves the signing secret for a webhook.
79+
// Use this secret to verify the signature of incoming webhook payloads.
80+
func (c *Client) GetWebhookSecret(ctx context.Context, webhookID string) (*WebhookSecret, error) {
81+
resp, err := c.CallHttp(ctx,
82+
Endpoint(http.MethodGet, pathWebhookSecret, webhookID),
83+
AcceptJson())
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
return CompletedObjectOrError[WebhookSecret](resp)
89+
}
90+
91+
// ListWebhookEventTypes returns all available webhook event types that can be subscribed to.
92+
func (c *Client) ListWebhookEventTypes(ctx context.Context) ([]WebhookEventType, error) {
93+
resp, err := c.CallHttp(ctx,
94+
Endpoint(http.MethodGet, pathEventTypes),
95+
AcceptJson())
96+
if err != nil {
97+
return nil, err
98+
}
99+
100+
return CompletedListOrError[WebhookEventType](resp)
101+
}

pkg/moov/webhook_models.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,28 @@ const (
5252

5353
type WebhookStatus string
5454

55-
type UpsertWebhook struct {
56-
URL string `json:"url"`
57-
Description string `json:"description"`
58-
Status WebhookStatus `json:"status"`
59-
EventTypes []EventType `json:"eventTypes"`
55+
// CreateWebhook is the payload for creating a new webhook.
56+
type CreateWebhook struct {
57+
// URL is the destination for webhook events. Must be a valid URL.
58+
URL string `json:"url"`
59+
// Description is an optional description for the webhook.
60+
Description string `json:"description"`
61+
// Status is the webhook status (enabled or disabled).
62+
Status WebhookStatus `json:"status"`
63+
// EventTypes is the list of event types to subscribe to.
64+
EventTypes []EventType `json:"eventTypes"`
65+
}
66+
67+
// UpdateWebhook is the payload for updating an existing webhook.
68+
type UpdateWebhook struct {
69+
// URL is the destination for webhook events. Must be a valid URL.
70+
URL string `json:"url"`
71+
// Description is an optional description for the webhook.
72+
Description string `json:"description"`
73+
// Status is the webhook status (enabled or disabled).
74+
Status WebhookStatus `json:"status"`
75+
// EventTypes is the list of event types to subscribe to.
76+
EventTypes []EventType `json:"eventTypes"`
6077
}
6178

6279
type Webhook struct {
@@ -69,3 +86,25 @@ type Webhook struct {
6986
CreatedOn time.Time `json:"createdOn"`
7087
UpdatedOn time.Time `json:"updatedOn"`
7188
}
89+
90+
// WebhookSecret contains the signing secret for verifying webhook payloads.
91+
type WebhookSecret struct {
92+
Secret string `json:"secret"`
93+
}
94+
95+
// WebhookPing is returned when pinging a webhook endpoint.
96+
type WebhookPing struct {
97+
// The webhook that was pinged.
98+
Webhook Webhook `json:"webhook"`
99+
// The request body sent to the target URL. It will contain an event type of `event.test` and an empty (null) data payload.
100+
RequestBodySent map[string]any `json:"requestBodySent"`
101+
// The response status code after sending a ping event to the URL.
102+
ResponseStatusCode int32 `json:"responseStatusCode"`
103+
}
104+
105+
// WebhookEventType describes a webhook event type that can be subscribed to.
106+
type WebhookEventType struct {
107+
EventTypeID string `json:"eventTypeID"`
108+
Type EventType `json:"type"`
109+
Description string `json:"description"`
110+
}

pkg/moov/webhook_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
func TestCreateWebhook(t *testing.T) {
1414
mc := NewTestClient(t)
1515

16-
createWebhook := moov.UpsertWebhook{
16+
createWebhook := moov.CreateWebhook{
1717
URL: "https://example.com/webhook",
1818
Description: "Test webhook",
1919
Status: moov.WebhookStatusEnabled,
@@ -56,8 +56,14 @@ func TestCreateWebhook(t *testing.T) {
5656
require.Equal(t, createdWebhook, webhook)
5757
})
5858

59+
t.Run("get webhook secret", func(t *testing.T) {
60+
secret, err := mc.GetWebhookSecret(context.Background(), createdWebhook.WebhookID)
61+
require.NoError(t, err)
62+
require.NotEmpty(t, secret.Secret)
63+
})
64+
5965
t.Run("update webhook", func(t *testing.T) {
60-
webhook, err := mc.UpdateWebhook(context.Background(), createdWebhook.WebhookID, moov.UpsertWebhook{
66+
webhook, err := mc.UpdateWebhook(context.Background(), createdWebhook.WebhookID, moov.UpdateWebhook{
6167
URL: "https://example.com/webhook-new",
6268
Description: "Test webhook new",
6369
Status: moov.WebhookStatusEnabled,

0 commit comments

Comments
 (0)