From 9db3baa2696b14448afaca121f977da147d603d0 Mon Sep 17 00:00:00 2001 From: Pavel Brm <5097196+pavelbrm@users.noreply.github.com> Date: Wed, 17 Jul 2024 20:56:36 +1200 Subject: [PATCH 1/2] feat: add leeway for expiry time on mobile webhooks (#2603) --- services/skus/service.go | 4 ++-- services/skus/service_nonint_test.go | 33 ++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/services/skus/service.go b/services/skus/service.go index 095bdc8b9..2e3fe891b 100644 --- a/services/skus/service.go +++ b/services/skus/service.go @@ -1689,7 +1689,7 @@ func (s *Service) processAppStoreNotificationTx(ctx context.Context, dbi sqlx.Ex switch { case ntf.shouldRenew(): - expt := time.UnixMilli(txn.ExpiresDate).UTC() + expt := time.UnixMilli(txn.ExpiresDate).UTC().Add(24 * time.Hour) paidt := time.Now() return s.renewOrderWithExpPaidTime(ctx, dbi, ord.ID, expt, paidt) @@ -1748,7 +1748,7 @@ func (s *Service) processPlayStoreNotificationTx(ctx context.Context, dbi sqlx.E return err } - expt := time.UnixMilli(sub.ExpiryTimeMillis).UTC() + expt := time.UnixMilli(sub.ExpiryTimeMillis).UTC().Add(24 * time.Hour) paidt := time.Now() return s.renewOrderWithExpPaidTime(ctx, dbi, ord.ID, expt, paidt) diff --git a/services/skus/service_nonint_test.go b/services/skus/service_nonint_test.go index e821ca705..b44fa0108 100644 --- a/services/skus/service_nonint_test.go +++ b/services/skus/service_nonint_test.go @@ -424,9 +424,26 @@ func TestService_processPlayStoreNotificationTx(t *testing.T) { SubID: "nightly.bravevpn.monthly", }, }, - orepo: &repository.MockOrder{}, + orepo: &repository.MockOrder{ + FnSetExpiresAt: func(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, when time.Time) error { + if when.Equal(time.Date(2024, time.July, 2, 0, 0, 0, 0, time.UTC)) { + return nil + } + + return model.Error("unexpected") + }, + }, prepo: &repository.MockOrderPayHistory{}, - pscl: &mockPSClient{}, + pscl: &mockPSClient{ + fnVerifySubscription: func(ctx context.Context, pkgName, subID, token string) (*androidpublisher.SubscriptionPurchase, error) { + result := &androidpublisher.SubscriptionPurchase{ + PaymentState: ptrTo[int64](1), + ExpiryTimeMillis: time.Date(2024, time.July, 1, 0, 0, 0, 0, time.UTC).UnixMilli(), + } + + return result, nil + }, + }, }, }, @@ -558,10 +575,18 @@ func TestService_processAppStoreNotificationTx(t *testing.T) { }, txn: &appstore.JWSTransactionDecodedPayload{ OriginalTransactionId: "123456789000001", - ExpiresDate: 1704067201000, + ExpiresDate: 1704067200000, }, - orepo: &repository.MockOrder{}, + orepo: &repository.MockOrder{ + FnSetExpiresAt: func(ctx context.Context, dbi sqlx.ExecerContext, id uuid.UUID, when time.Time) error { + if when.Equal(time.Date(2024, time.January, 2, 0, 0, 0, 0, time.UTC)) { + return nil + } + + return model.Error("unexpected") + }, + }, prepo: &repository.MockOrderPayHistory{}, }, }, From 1eda1a0973c2eb0a218503f9dc54b8dfcc8791b7 Mon Sep 17 00:00:00 2001 From: clD11 <23483715+clD11@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:48:51 +0100 Subject: [PATCH 2/2] fix: io read timeout on create item creds endpoint (#2599) Use io.ReadAll instead of requestutils.ReadJSON and increase the read timeout for grants server. --- services/grant/cmd/grant.go | 2 +- services/skus/controllers.go | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/services/grant/cmd/grant.go b/services/grant/cmd/grant.go index 9abd8e77d..759b56b48 100644 --- a/services/grant/cmd/grant.go +++ b/services/grant/cmd/grant.go @@ -703,7 +703,7 @@ func GrantServer( srv := http.Server{ Addr: ":3333", Handler: chi.ServerBaseContext(ctx, r), - ReadTimeout: 3 * time.Second, + ReadTimeout: 10 * time.Second, WriteTimeout: 20 * time.Second, } err = srv.ListenAndServe() diff --git a/services/skus/controllers.go b/services/skus/controllers.go index e8d728b14..d66b20fc5 100644 --- a/services/skus/controllers.go +++ b/services/skus/controllers.go @@ -117,7 +117,7 @@ func Router( // For now, this endpoint is placed directly under /credentials. // It would make sense to put it under /items/item_id, had the caller known the item id. - // However, the caller of this endpoit does not posses that knowledge, and it would have to call the order endpoint to get it. + // However, the caller of this endpoint does not possess that knowledge, and it would have to call the order endpoint to get it. // This extra round-trip currently does not make sense. // So until Bundles came along we can benefit from the fact that there is one item per order. // By the time Bundles arrive, the caller would either have to fetch order anyway, or this can be communicated in another way. @@ -623,15 +623,19 @@ type createItemCredsRequest struct { // createItemCreds handles requests for creating credentials for an item. func createItemCreds(svc *Service) handlers.AppHandler { return func(w http.ResponseWriter, r *http.Request) *handlers.AppError { - ctx := r.Context() - lg := logging.Logger(ctx, "skus.createItemCreds") + b, err := io.ReadAll(io.LimitReader(r.Body, reqBodyLimit10MB)) + if err != nil { + return handlers.WrapError(err, "error reading body", http.StatusBadRequest) + } req := &createItemCredsRequest{} - if err := requestutils.ReadJSON(ctx, r.Body, req); err != nil { - lg.Error().Err(err).Msg("failed to read body payload") - return handlers.WrapError(err, "Error in request body", http.StatusBadRequest) + if err := json.Unmarshal(b, req); err != nil { + return handlers.WrapError(err, "error decoding body", http.StatusBadRequest) } + ctx := r.Context() + lg := logging.Logger(ctx, "skus.createItemCreds") + if _, err := govalidator.ValidateStruct(req); err != nil { lg.Error().Err(err).Msg("failed to validate struct") return handlers.WrapValidationError(err)