diff --git a/services/skus/controllers.go b/services/skus/controllers.go index 9c7901e49..7395451a1 100644 --- a/services/skus/controllers.go +++ b/services/skus/controllers.go @@ -657,12 +657,13 @@ func DeleteOrderCreds(service *Service) handlers.AppHandler { ) } - if err := service.validateOrderMerchantAndCaveats(ctx, *orderID.UUID()); err != nil { + id := *orderID.UUID() + if err := service.validateOrderMerchantAndCaveats(ctx, id); err != nil { return handlers.WrapError(err, "Error validating auth merchant and caveats", http.StatusForbidden) } isSigned := r.URL.Query().Get("isSigned") == "true" - if err := service.DeleteOrderCreds(ctx, *orderID.UUID(), isSigned); err != nil { + if err := service.DeleteOrderCreds(ctx, id, isSigned); err != nil { return handlers.WrapError(err, "Error deleting credentials", http.StatusBadRequest) } diff --git a/services/skus/credentials.go b/services/skus/credentials.go index 70f620bc1..4707f8b9e 100644 --- a/services/skus/credentials.go +++ b/services/skus/credentials.go @@ -33,8 +33,8 @@ const ( ) var ( - ErrOrderUnpaid = errors.New("order not paid") - ErrOrderHasNoItems = errors.New("order has no items") + ErrOrderUnpaid = errors.New("order not paid") + ErrOrderHasNoItems model.Error = "order has no items" errInvalidIssuerResp model.Error = "invalid issuer response" @@ -600,9 +600,10 @@ func (s *SigningOrderResultErrorHandler) Handle(ctx context.Context, message kaf return nil } -// DeleteOrderCreds performs a hard delete of all the order credentials associated with the given OrderID. -// This includes both time limited v2 and single use credentials. -// The isSigned param only applies to single use and will always be false for time limited v2. +// DeleteOrderCreds hard-deletes all the order credentials associated with the given orderID. +// +// This includes both time-limited-v2 and single-use credentials. +// The isSigned param only applies to single use and will always be false for time-limited-v2. // Credentials cannot be deleted when an order is in the process of being signed. func (s *Service) DeleteOrderCreds(ctx context.Context, orderID uuid.UUID, isSigned bool) error { order, err := s.Datastore.GetOrder(orderID) @@ -610,40 +611,58 @@ func (s *Service) DeleteOrderCreds(ctx context.Context, orderID uuid.UUID, isSig return err } - if len(order.Items) == 0 { + nitems := len(order.Items) + if nitems == 0 { return ErrOrderHasNoItems } - if order.Items[0].CredentialType == timeLimited { + // Exit early in this special case. + if nitems == 1 && order.Items[0].CredentialType == timeLimited { return nil } - ctx, tx, rollback, commit, err := datastore.GetTx(ctx, s.Datastore) + tx, err := s.Datastore.RawDB().BeginTxx(ctx, nil) if err != nil { - return fmt.Errorf("error retrieveing txn delete order cred") + return err } - defer rollback() + defer s.Datastore.RollbackTx(tx) - switch order.Items[0].CredentialType { - case singleUse: - err = s.Datastore.DeleteSingleUseOrderCredsByOrderTx(ctx, tx, orderID, isSigned) - if err != nil { - return fmt.Errorf("error deleting single use order creds: %w", err) - } - case timeLimitedV2: - err = s.Datastore.DeleteTimeLimitedV2OrderCredsByOrderTx(ctx, tx, orderID) - if err != nil { - return fmt.Errorf("error deleting time limited v2 order creds: %w", err) + var didSingleUse, didTlv2 bool + + // TODO(pavelb): + // - create repos for credentials; + // - move the corresponding methods there; + // - make those methods work on per-item basis. + for i := range order.Items { + item := order.Items[i] + + switch item.CredentialType { + case timeLimited: + continue + case singleUse: + if !didSingleUse { + if err := s.Datastore.DeleteSingleUseOrderCredsByOrderTx(ctx, tx, orderID, isSigned); err != nil { + return fmt.Errorf("error deleting single use order creds: %w", err) + } + } + + didSingleUse = true + case timeLimitedV2: + if !didTlv2 { + if err := s.Datastore.DeleteTimeLimitedV2OrderCredsByOrderTx(ctx, tx, orderID); err != nil { + return fmt.Errorf("error deleting time limited v2 order creds: %w", err) + } + } + + didTlv2 = true } } - err = s.Datastore.DeleteSigningOrderRequestOutboxByOrderTx(ctx, tx, orderID) - if err != nil { + if err := s.Datastore.DeleteSigningOrderRequestOutboxByOrderTx(ctx, tx, orderID); err != nil { return fmt.Errorf("error deleting order creds signing in progress") } - err = commit() - if err != nil { + if err := tx.Commit(); err != nil { return fmt.Errorf("error commiting delete order creds: %w", err) } diff --git a/services/skus/order.go b/services/skus/order.go index b87862cb8..2b60ede03 100644 --- a/services/skus/order.go +++ b/services/skus/order.go @@ -190,14 +190,11 @@ func getEmailFromCheckoutSession(stripeSession *stripe.CheckoutSession) string { return email } -// RenewOrder updates the orders status to paid and paid at time, inserts record of this order +// RenewOrder updates the order status to paid and records payment history. +// // Status should either be one of pending, paid, fulfilled, or canceled. func (s *Service) RenewOrder(ctx context.Context, orderID uuid.UUID) error { - - // renew order is an update order with paid status - // and an update order expires at with the new expiry time of the order - err := s.Datastore.UpdateOrder(orderID, OrderStatusPaid) // this performs a record order payment - if err != nil { + if err := s.Datastore.UpdateOrder(orderID, OrderStatusPaid); err != nil { return fmt.Errorf("failed to set order status to paid: %w", err) }