@@ -1484,34 +1484,19 @@ func credChunkFn(interval timeutils.ISODuration) func(time.Time) (time.Time, tim
1484
1484
}
1485
1485
}
1486
1486
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 )
1494
1489
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
1503
1491
}
1504
1492
1505
- const gracePeriod = 5
1506
- expAtWithGrace := expiresAt .AddDate (0 , 0 , gracePeriod )
1507
-
1508
1493
chunkingFn := credChunkFn (interval )
1509
1494
dEnd , _ := chunkingFn (chunkStart )
1510
1495
1511
1496
var dStart time.Time
1512
1497
var creds []TimeLimitedCreds
1513
1498
1514
- for dEnd .Before (expAtWithGrace ) {
1499
+ for dEnd .Before (issueToWithGrace ) {
1515
1500
dStart , dEnd = chunkingFn (dEnd )
1516
1501
1517
1502
token , err := tlSecret .Derive ([]byte (issuerID ), dStart , dEnd )
@@ -1531,6 +1516,45 @@ func timeChunking(_ context.Context, issuerID string, tlSecret cryptography.Time
1531
1516
return creds , nil
1532
1517
}
1533
1518
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
+
1534
1558
// GetTimeLimitedCreds returns get an order's time limited creds.
1535
1559
func (s * Service ) GetTimeLimitedCreds (ctx context.Context , order * Order , itemID , _ uuid.UUID ) ([]TimeLimitedCreds , int , error ) {
1536
1560
if ! order .IsPaid () || order .LastPaidAt == nil {
@@ -1579,7 +1603,9 @@ func (s *Service) GetTimeLimitedCreds(ctx context.Context, order *Order, itemID,
1579
1603
return nil , http .StatusInternalServerError , fmt .Errorf ("error encoding issuer: %w" , err )
1580
1604
}
1581
1605
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 )
1583
1609
if err != nil {
1584
1610
return nil , http .StatusInternalServerError , fmt .Errorf ("failed to derive credential chunking: %w" , err )
1585
1611
}
0 commit comments