Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate Order expires_at Using valid_for_iso #1865

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions libs/time/duration.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ func (i *ISODuration) From(t time.Time) (*time.Time, error) {
return &tt, nil
}

// Duration returns time.Duration.
func (i *ISODuration) Duration() (time.Duration, error) {
return i.base(time.Time{})
}

const (
// HoursPerDay is the number of hours per day according to Google
HoursPerDay = 24.0
Expand All @@ -80,24 +85,25 @@ var (
invalidStrings = []string{"", "P", "PT"}
)

// base - given a base, produce a time.Duration from base for the ISODuration
// base retuns a time.Duration from base for the ISODuration.
//
// TODO: refactor to not require t. It's not used.
// TODO: consider relying only on FindStringSubmatch to avoid running regexp twice.
// A nil result from FindStringSubmatch means no match.
func (i *ISODuration) base(t time.Time) (time.Duration, error) {
if i == nil {
return 0, nil
}
s := i.String()

var (
match []string
prefix string
)
s := i.String()

if pattern.MatchString(s) {
match = pattern.FindStringSubmatch(s)
} else {
if !pattern.MatchString(s) {
return 0, ErrUnsupportedFormat
}

match := pattern.FindStringSubmatch(s)

var prefix string
if strings.HasPrefix(s, "-") {
prefix = "-"
}
Expand Down
20 changes: 20 additions & 0 deletions services/skus/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -1434,11 +1434,31 @@ func (pg *Postgres) recordOrderPayment(ctx context.Context, dbi sqlx.ExecerConte
return pg.orderRepo.SetLastPaidAt(ctx, dbi, id, when)
}

// updateOrderExpiresAt updates order's expires_at based on last_paid_at and a validity period.
//
// Fixed: it now uses the first item's valid_for_iso instead of the order's valid_for.
// There are currently no orders with more than one item at the storage.
func (pg *Postgres) updateOrderExpiresAt(ctx context.Context, dbi sqlx.ExtContext, orderID uuid.UUID) error {
orderTimeBounds, err := pg.orderRepo.GetTimeBounds(ctx, dbi, orderID)
if err != nil {
return fmt.Errorf("unable to get order time bounds: %w", err)
}

items, err := pg.orderItemRepo.FindByOrderID(ctx, dbi, orderID)
if err != nil {
return err
}

if len(items) == 0 {
return model.ErrInvalidOrderNoItems
}

// Keep it backwards-compatible.
// Only use the order item's valid_for_iso if successfully parsed.
// Otherwise, for now keep relying on the order's valid_for.
if d, err := items[0].ValidForISODuration(); err == nil && d != 0 {
orderTimeBounds.ValidFor = &d
}

return pg.orderRepo.SetExpiresAt(ctx, dbi, orderID, orderTimeBounds.ExpiresAt())
}
26 changes: 26 additions & 0 deletions services/skus/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"github.com/stripe/stripe-go/v72/checkout/session"
"github.com/stripe/stripe-go/v72/customer"

xtime "github.com/brave-intl/bat-go/libs/time"

"github.com/brave-intl/bat-go/libs/datastore"
)

Expand All @@ -26,6 +28,8 @@ const (
ErrNoRowsChangedOrder Error = "model: no rows changed in orders"
ErrNoRowsChangedOrderPayHistory Error = "model: no rows changed in order_payment_history"
ErrExpiredStripeCheckoutSessionIDNotFound Error = "model: expired stripeCheckoutSessionId not found"
ErrInvalidOrderNoItems Error = "model: invalid order: no items"
ErrInvalidOrderItemValidISO Error = "model: invalid order item valid_for_iso period"
)

const (
Expand Down Expand Up @@ -173,6 +177,28 @@ type OrderItem struct {
IssuanceIntervalISO *string `json:"issuanceInterval" db:"issuance_interval"`
}

func (x *OrderItem) ValidForISODuration() (time.Duration, error) {
period, err := x.validForISODuration()
if err != nil {
return 0, err
}

return period.Duration()
}

func (x *OrderItem) validForISODuration() (xtime.ISODuration, error) {
if x.ValidForISO == nil {
return "", ErrInvalidOrderItemValidISO
}

result, err := xtime.ParseDuration(*x.ValidForISO)
if err != nil {
return "", err
}

return *result, nil
}

// Methods represents payment methods.
type Methods []string

Expand Down