Skip to content

Commit cd529f8

Browse files
authored
Merge pull request #2878 from brave-intl/master
Production 2025-07-09_01
2 parents 07c8f74 + b91da03 commit cd529f8

File tree

2 files changed

+287
-56
lines changed

2 files changed

+287
-56
lines changed

services/skus/service.go

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,34 +1484,19 @@ func credChunkFn(interval timeutils.ISODuration) func(time.Time) (time.Time, tim
14841484
}
14851485
}
14861486

1487-
func timeChunking(_ context.Context, issuerID string, tlSecret cryptography.TimeLimitedSecret, ord *model.Order, item *model.OrderItem, duration, interval timeutils.ISODuration, chunkStart time.Time) ([]TimeLimitedCreds, error) {
1488-
// Note, given the user only pays once a year calculating the expiresAt using the orders
1489-
// lastPaidAt won't work. For annual skus we can use the orders ExpiresAt as we know all annual skus
1490-
// will all have an ExpiresAt date. For monthly or legacy skus that may not have an ExpiresAt value we can use the
1491-
// existing method i.e. using the order LastPaidAt until we deprecate time limited creds.
1492-
1493-
expiresAt, err := duration.From(*ord.LastPaidAt)
1487+
func timeChunking(issuerID string, tlSecret cryptography.TimeLimitedSecret, ord *model.Order, item *model.OrderItem, duration, interval timeutils.ISODuration, chunkStart, now time.Time) ([]TimeLimitedCreds, error) {
1488+
issueToWithGrace, err := calculateIssueToWithGrace(ord, item, duration, now)
14941489
if err != nil {
1495-
return nil, fmt.Errorf("unable to compute expiry")
1496-
}
1497-
1498-
if item.IsSearchAnnual() || item.IsTalkAnnual() {
1499-
expiresAt = ord.ExpiresAt
1500-
if expiresAt == nil {
1501-
return nil, model.Error("skus: time chunking: order expires at cannot be nil")
1502-
}
1490+
return nil, err
15031491
}
15041492

1505-
const gracePeriod = 5
1506-
expAtWithGrace := expiresAt.AddDate(0, 0, gracePeriod)
1507-
15081493
chunkingFn := credChunkFn(interval)
15091494
dEnd, _ := chunkingFn(chunkStart)
15101495

15111496
var dStart time.Time
15121497
var creds []TimeLimitedCreds
15131498

1514-
for dEnd.Before(expAtWithGrace) {
1499+
for dEnd.Before(issueToWithGrace) {
15151500
dStart, dEnd = chunkingFn(dEnd)
15161501

15171502
token, err := tlSecret.Derive([]byte(issuerID), dStart, dEnd)
@@ -1531,6 +1516,45 @@ func timeChunking(_ context.Context, issuerID string, tlSecret cryptography.Time
15311516
return creds, nil
15321517
}
15331518

1519+
func calculateIssueToWithGrace(ord *model.Order, item *model.OrderItem, duration timeutils.ISODuration, now time.Time) (time.Time, error) {
1520+
// Note about fix for annual subs. When calculating the issue to date we need to make sure the last paid at date + duration
1521+
// is greater than the current date otherwise the issue to date will be in the past. For example, for annual subs a
1522+
// user only pays once a year so we cannot use the last paid at date and add a duration of 1 month like we
1523+
// do for monthly. We must also make sure we do not have a duration greater than the payment frequency.
1524+
1525+
const gracePeriod = 5
1526+
1527+
switch {
1528+
// We can use the current date as last paid at date and if the issue to date is
1529+
// greater than the order expiry use that instead.
1530+
case item.IsSearchAnnual() || item.IsTalkAnnual():
1531+
issueTo, err := duration.From(now)
1532+
if err != nil {
1533+
return time.Time{}, fmt.Errorf("unable to compute expiry")
1534+
}
1535+
1536+
if ord.ExpiresAt == nil {
1537+
return time.Time{}, model.Error("skus: time chunking: order expires at cannot be nil")
1538+
}
1539+
1540+
if issueTo.After(*ord.ExpiresAt) {
1541+
issueTo = ord.ExpiresAt
1542+
}
1543+
1544+
return issueTo.AddDate(0, 0, gracePeriod), nil
1545+
1546+
default:
1547+
// Leaving original code to calculate monthly. This works because
1548+
// the payment frequency and duration are both the same (1 month).
1549+
issueTo, err := duration.From(*ord.LastPaidAt)
1550+
if err != nil {
1551+
return time.Time{}, fmt.Errorf("unable to compute expiry")
1552+
}
1553+
1554+
return issueTo.AddDate(0, 0, gracePeriod), nil
1555+
}
1556+
}
1557+
15341558
// GetTimeLimitedCreds returns get an order's time limited creds.
15351559
func (s *Service) GetTimeLimitedCreds(ctx context.Context, order *Order, itemID, _ uuid.UUID) ([]TimeLimitedCreds, int, error) {
15361560
if !order.IsPaid() || order.LastPaidAt == nil {
@@ -1579,7 +1603,9 @@ func (s *Service) GetTimeLimitedCreds(ctx context.Context, order *Order, itemID,
15791603
return nil, http.StatusInternalServerError, fmt.Errorf("error encoding issuer: %w", err)
15801604
}
15811605

1582-
credentials, err := timeChunking(ctx, issuerID, timeLimitedSecret, order, item, *duration, *interval, time.Now())
1606+
now := time.Now()
1607+
1608+
credentials, err := timeChunking(issuerID, timeLimitedSecret, order, item, *duration, *interval, now, now)
15831609
if err != nil {
15841610
return nil, http.StatusInternalServerError, fmt.Errorf("failed to derive credential chunking: %w", err)
15851611
}

0 commit comments

Comments
 (0)