Skip to content

Commit

Permalink
feat: handle radom webhook events
Browse files Browse the repository at this point in the history
  • Loading branch information
clD11 committed Aug 10, 2024
1 parent 1a2af79 commit f4256b7
Show file tree
Hide file tree
Showing 6 changed files with 1,299 additions and 0 deletions.
42 changes: 42 additions & 0 deletions services/skus/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"strconv"

"github.com/asaskevich/govalidator"
"github.com/brave-intl/bat-go/services/skus/radom"
"github.com/go-chi/chi"
"github.com/go-chi/cors"
"github.com/go-playground/validator/v10"
Expand Down Expand Up @@ -1193,6 +1194,47 @@ func handleRadomWebhook(_ *Service) handlers.AppHandler {
}
}

func handleRadomWebhookH(w http.ResponseWriter, r *http.Request, svc *Service) *handlers.AppError {
ctx := r.Context()

l := logging.Logger(ctx, "skus").With().Str("func", "handleRadomWebhookH").Logger()

if err := svc.radomAuth.Authenticate(ctx, r.Header.Get("radom-verification-key")); err != nil {
l.Error().Err(err).Msg("invalid request")

return handlers.WrapError(err, "invalid request", http.StatusUnauthorized)
}

b, err := io.ReadAll(io.LimitReader(r.Body, reqBodyLimit10MB))
if err != nil {
l.Err(err).Msg("failed to read payload")

return handlers.RenderContent(ctx, struct{}{}, w, http.StatusOK)
}

event, err := radom.ParseEvent(b)
if err != nil {
l.Err(err).Msg("failed to parse radom event")

return handlers.WrapError(err, "failed to parse radom event", http.StatusBadRequest)
}

if err := svc.processRadomEvent(ctx, event); err != nil {
l.Err(err).Msg("failed to process radom event")

return handlers.WrapError(model.ErrSomethingWentWrong, "something went wrong", http.StatusInternalServerError)
}

msg := "skipped radom notification"
if event.ShouldProcess() {
msg = "processed radom notification"
}

l.Info().Str("ntf_type", event.EventType).Str("ntf_effect", event.Effect()).Msg(msg)

return nil
}

func handleStripeWebhook(svc *Service) handlers.AppHandler {
return func(w http.ResponseWriter, r *http.Request) *handlers.AppError {
ctx := r.Context()
Expand Down
177 changes: 177 additions & 0 deletions services/skus/radom/event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package radom

import (
"context"
"encoding/json"

"github.com/brave-intl/bat-go/services/skus/model"
uuid "github.com/satori/go.uuid"
)

const (
ErrUnsupportedEvent = model.Error("radom: unsupported event type for brave order id")
ErrBraveOrderIDNotFound = model.Error("radom: brave order id not found")
ErrSubscriptionIDNotFound = model.Error("radom: subscription id not found")
)

type Event struct {
EventType string `json:"eventType"`
EventData EventData `json:"eventData"`
RadomData RadData `json:"radomData"`
}

func (e *Event) OrderID() (uuid.UUID, error) {
if e.EventData.NewSubscription == nil {
return uuid.Nil, ErrUnsupportedEvent
}

mdata := e.RadomData.CheckoutSession.Metadata

for i := range mdata {
d := mdata[i]
if d.Key == "brave_order_id" {
return uuid.FromString(d.Value)
}
}

return uuid.Nil, ErrBraveOrderIDNotFound
}

func (e *Event) SubID() (uuid.UUID, error) {
var subID uuid.UUID

switch {
case e.EventData.NewSubscription != nil:
subID = e.EventData.NewSubscription.SubscriptionID

case e.EventData.SubscriptionPayment != nil:
subID = e.EventData.SubscriptionPayment.RadomData.Subscription.SubscriptionID

case e.EventData.SubscriptionCancelled != nil:
subID = e.EventData.SubscriptionCancelled.SubscriptionID

case e.EventData.SubscriptionExpired != nil:
subID = e.EventData.SubscriptionExpired.SubscriptionID
}

if uuid.Equal(subID, uuid.Nil) {
return uuid.Nil, ErrSubscriptionIDNotFound
}

return subID, nil
}

func (e *Event) IsNewSub() bool {
return e.EventData.NewSubscription != nil
}

func (e *Event) ShouldRenew() bool {
return e.EventData.SubscriptionPayment != nil
}

func (e *Event) ShouldCancel() bool {
return e.EventData.SubscriptionCancelled != nil || e.EventData.SubscriptionExpired != nil
}

func (e *Event) ShouldProcess() bool {
return e.IsNewSub() || e.ShouldRenew() || e.ShouldCancel()
}

func (e *Event) Effect() string {
switch {
case e.IsNewSub():
return "new"

case e.ShouldRenew():
return "renew"

case e.ShouldCancel():
return "cancel"

default:
return "skip"
}
}

type EventData struct {
NewSubscription *NewSubscription `json:"newSubscription"`
SubscriptionPayment *SubscriptionPayment `json:"subscriptionPayment"`
SubscriptionCancelled *SubscriptionCancelled `json:"subscriptionCancelled"`
SubscriptionExpired *SubscriptionExpired `json:"subscriptionExpired"`
}

type NewSubscription struct {
SubscriptionID uuid.UUID `json:"subscriptionId"`
}

type SubscriptionPayment struct {
RadomData RadData `json:"radomData"`
}

type SubscriptionCancelled struct {
SubscriptionID uuid.UUID `json:"subscriptionId"`
}

type SubscriptionExpired struct {
SubscriptionID uuid.UUID `json:"subscriptionId"`
}

type RadData struct {
CheckoutSession CheckoutSession `json:"checkoutSession"`
Subscription Subscription `json:"subscription"`
}

type CheckoutSession struct {
CheckoutSessionID string `json:"checkoutSessionId"`
Metadata []Metadata `json:"metadata"`
}

type Subscription struct {
SubscriptionID uuid.UUID `json:"subscriptionId"`
}

func ParseEvent(b []byte) (Event, error) {
var event Event
if err := json.Unmarshal(b, &event); err != nil {
return Event{}, err
}

return event, nil
}

type MessageAuthConfig struct {
token string
enabled bool
}

type MessageAuthenticator struct {
cfg MessageAuthConfig
}

func NewMessageAuthenticator(cfg MessageAuthConfig) *MessageAuthenticator {
return &MessageAuthenticator{
cfg: cfg,
}
}

const (
ErrDisabled = model.Error("radom: radom disabled")
ErrVerificationKeyEmpty = model.Error("radom: verification key is empty")
ErrVerificationKeyInvalid = model.Error("radom: verification key is invalid")
)

func (r *MessageAuthenticator) Authenticate(_ context.Context, token string) error {
if !r.cfg.enabled {
return ErrDisabled
}

if token == "" {
return ErrVerificationKeyEmpty
}

if token != r.cfg.token {
return ErrVerificationKeyInvalid
}

return nil
}
Loading

0 comments on commit f4256b7

Please sign in to comment.