diff --git a/billing/checkout/service.go b/billing/checkout/service.go index ac05b7a6ec..3ab5ca5b1d 100644 --- a/billing/checkout/service.go +++ b/billing/checkout/service.go @@ -13,6 +13,7 @@ import ( "github.com/stripe/stripe-go/v79" "github.com/raystack/frontier/billing" + billingerrors "github.com/raystack/frontier/billing/errors" "github.com/raystack/frontier/internal/metrics" "github.com/raystack/frontier/pkg/metadata" @@ -350,7 +351,7 @@ func (s *Service) Create(ctx context.Context, ch Checkout) (Checkout, error) { PaymentMethodCollection: stripe.String(string(stripe.PaymentLinkPaymentMethodCollectionIfRequired)), }) if err != nil { - return Checkout{}, fmt.Errorf("failed to create subscription at billing provider: %w", err) + return Checkout{}, fmt.Errorf("failed to create subscription at billing provider: %w", billingerrors.TranslateStripeError(err)) } return s.repository.Create(ctx, Checkout{ @@ -481,7 +482,7 @@ func (s *Service) Create(ctx context.Context, ch Checkout) (Checkout, error) { }, }) if err != nil { - return Checkout{}, fmt.Errorf("failed to buy product at billing provider: %w", err) + return Checkout{}, fmt.Errorf("failed to buy product at billing provider: %w", billingerrors.TranslateStripeError(err)) } return s.repository.Create(ctx, Checkout{ @@ -559,7 +560,7 @@ func (s *Service) SyncWithProvider(ctx context.Context, customerID string) error }, }) if err != nil { - errs = append(errs, fmt.Errorf("failed to get checkout session from billing provider: %w", err)) + errs = append(errs, fmt.Errorf("failed to get checkout session from billing provider: %w", billingerrors.TranslateStripeError(err))) continue } if ch.PaymentStatus != string(checkoutSession.PaymentStatus) { @@ -735,7 +736,7 @@ func (s *Service) ensureSubscription(ctx context.Context, ch Checkout) (string, }, }) if err != nil { - return "", fmt.Errorf("failed to get subscription from billing provider: %w", err) + return "", fmt.Errorf("failed to get subscription from billing provider: %w", billingerrors.TranslateStripeError(err)) } // create subscription @@ -802,7 +803,7 @@ func (s *Service) CreateSessionForPaymentMethod(ctx context.Context, ch Checkout }, }) if err != nil { - return Checkout{}, fmt.Errorf("failed to create checkout at billing provider: %w", err) + return Checkout{}, fmt.Errorf("failed to create checkout at billing provider: %w", billingerrors.TranslateStripeError(err)) } return s.repository.Create(ctx, Checkout{ @@ -844,7 +845,7 @@ func (s *Service) CreateSessionForCustomerPortal(ctx context.Context, ch Checkou session, err := s.stripeClient.BillingPortalSessions.New(sessionParams) if err != nil { - return Checkout{}, fmt.Errorf("failed to create session for customer portal: %w", err) + return Checkout{}, fmt.Errorf("failed to create session for customer portal: %w", billingerrors.TranslateStripeError(err)) } return Checkout{ @@ -989,7 +990,7 @@ func (s *Service) Apply(ctx context.Context, ch Checkout) (*subscription.Subscri Coupon: couponID, }) if err != nil { - return nil, nil, fmt.Errorf("failed to create subscription at billing provider: %w", err) + return nil, nil, fmt.Errorf("failed to create subscription at billing provider: %w", billingerrors.TranslateStripeError(err)) } // register subscription in frontier diff --git a/billing/customer/service.go b/billing/customer/service.go index 143c26a7a3..133af7db81 100644 --- a/billing/customer/service.go +++ b/billing/customer/service.go @@ -2,6 +2,7 @@ package customer import ( "context" + "errors" "fmt" "log/slog" "math/rand" @@ -14,6 +15,7 @@ import ( "github.com/stripe/stripe-go/v79" "github.com/raystack/frontier/billing" + billingerrors "github.com/raystack/frontier/billing/errors" "github.com/raystack/frontier/internal/metrics" "slices" @@ -134,14 +136,11 @@ func (s *Service) RegisterToProvider(ctx context.Context, customer Customer) (*s TestClock: customer.StripeTestClockID, }) if err != nil { - if stripeErr, ok := err.(*stripe.Error); ok { - switch stripeErr.Code { - case stripe.ErrorCodeParameterMissing: - // stripe error - return nil, fmt.Errorf("missing parameter while registering to biller: %s", stripeErr.Error()) - } + var stripeErr *stripe.Error + if errors.As(err, &stripeErr) && stripeErr.Code == stripe.ErrorCodeParameterMissing { + return nil, fmt.Errorf("missing parameter while registering to biller: %s: %w", stripeErr.Msg, err) } - return nil, fmt.Errorf("failed to register in billing provider: %w", err) + return nil, fmt.Errorf("failed to register in billing provider: %w", billingerrors.TranslateStripeError(err)) } return stripeCustomer, nil @@ -194,14 +193,11 @@ func (s *Service) Update(ctx context.Context, customer Customer) (Customer, erro }, }) if err != nil { - if stripeErr, ok := err.(*stripe.Error); ok { - switch stripeErr.Code { - case stripe.ErrorCodeParameterMissing: - // stripe error - return Customer{}, fmt.Errorf("missing parameter while registering to biller: %s", stripeErr.Error()) - } + var stripeErr *stripe.Error + if errors.As(err, &stripeErr) && stripeErr.Code == stripe.ErrorCodeParameterMissing { + return Customer{}, fmt.Errorf("missing parameter while registering to biller: %s: %w", stripeErr.Msg, err) } - return Customer{}, fmt.Errorf("failed to register in billing provider: %w", err) + return Customer{}, fmt.Errorf("failed to register in billing provider: %w", billingerrors.TranslateStripeError(err)) } customer.ProviderID = stripeCustomer.ID return s.repository.UpdateByID(ctx, customer) @@ -286,17 +282,9 @@ func (s *Service) Delete(ctx context.Context, id string) error { Context: ctx, }, }); err != nil { - var throw = true - // Try to safely cast a generic error to a stripe.Error so that we can get at - // some additional Stripe-specific information about what went wrong. - if stripeErr, ok := err.(*stripe.Error); ok { - // The Code field will contain a basic identifier for the failure. - if stripeErr.Code == stripe.ErrorCodeResourceMissing { - // it's ok if the customer is already deleted - throw = false - } - } - if throw { + err = billingerrors.TranslateStripeError(err) + // it's ok if the customer is already deleted + if !errors.Is(err, billingerrors.ErrProviderResourceMissing) { return fmt.Errorf("failed to delete customer from billing provider: %w", err) } } @@ -352,6 +340,9 @@ func (s *Service) ListPaymentMethods(ctx context.Context, id string) ([]PaymentM paymentMethods = append(paymentMethods, pm) } + if err := stripePaymentMethodItr.Err(); err != nil { + return nil, fmt.Errorf("failed to list payment methods from billing provider: %w", billingerrors.TranslateStripeError(err)) + } return paymentMethods, nil } @@ -432,7 +423,7 @@ func (s *Service) SyncWithProvider(ctx context.Context, customr Customer) error }, }) if err != nil { - return fmt.Errorf("failed to get customer from billing provider: %w", err) + return fmt.Errorf("failed to get customer from billing provider: %w", billingerrors.TranslateStripeError(err)) } var shouldUpdate bool diff --git a/billing/invoice/service.go b/billing/invoice/service.go index 341fe92979..14c72b08a9 100644 --- a/billing/invoice/service.go +++ b/billing/invoice/service.go @@ -20,6 +20,7 @@ import ( "github.com/stripe/stripe-go/v79" "github.com/raystack/frontier/billing" + billingerrors "github.com/raystack/frontier/billing/errors" "github.com/raystack/frontier/internal/metrics" "github.com/raystack/frontier/billing/customer" @@ -287,7 +288,7 @@ func (s *Service) SyncWithProvider(ctx context.Context, customr customer.Custome return errors.Join(errs...) } if err := stripeInvoices.Err(); err != nil { - return fmt.Errorf("failed to list invoices: %w", err) + return fmt.Errorf("failed to list invoices: %w", billingerrors.TranslateStripeError(err)) } return nil } @@ -361,7 +362,7 @@ func (s *Service) GetUpcoming(ctx context.Context, customerID string) (Invoice, s.log.DebugContext(ctx, "no upcoming invoice", "error", stripeErr) return Invoice{}, nil } - return Invoice{}, fmt.Errorf("failed to get upcoming invoice: %w", err) + return Invoice{}, fmt.Errorf("failed to get upcoming invoice: %w", billingerrors.TranslateStripeError(err)) } return stripeInvoiceToInvoice(customerID, stripeInvoice), nil @@ -693,7 +694,7 @@ func (s *Service) CreateInProvider(ctx context.Context, custmr customer.Customer }, }) if err != nil { - return nil, fmt.Errorf("failed to create invoice: %w", err) + return nil, fmt.Errorf("failed to create invoice: %w", billingerrors.TranslateStripeError(err)) } // create line item for the invoice @@ -726,12 +727,12 @@ func (s *Service) CreateInProvider(ctx context.Context, custmr customer.Customer Period: itemPeriod, }) if err != nil { - return nil, fmt.Errorf("failed to create invoice item: %w", err) + return nil, fmt.Errorf("failed to create invoice item: %w", billingerrors.TranslateStripeError(err)) } } // fetch updated stripe invoice - return s.stripeClient.Invoices.Get(stripeInvoice.ID, &stripe.InvoiceParams{ + updatedInvoice, err := s.stripeClient.Invoices.Get(stripeInvoice.ID, &stripe.InvoiceParams{ Params: stripe.Params{ Context: ctx, }, @@ -739,6 +740,10 @@ func (s *Service) CreateInProvider(ctx context.Context, custmr customer.Customer new("lines"), }, }) + if err != nil { + return nil, fmt.Errorf("failed to get invoice from billing provider: %w", billingerrors.TranslateStripeError(err)) + } + return updatedInvoice, nil } // Reconcile checks all paid invoices and reconciles them with the system. @@ -835,7 +840,7 @@ func (s *Service) reconcileCreditInvoice(ctx context.Context, inv Invoice) error func (s *Service) TriggerSyncByProviderID(ctx context.Context, id string) error { stripeInvoice, err := s.stripeClient.Invoices.Get(id, &stripe.InvoiceParams{}) if err != nil { - return err + return fmt.Errorf("failed to get invoice from billing provider: %w", billingerrors.TranslateStripeError(err)) } customrs, err := s.customerService.List(ctx, customer.Filter{ diff --git a/billing/product/service.go b/billing/product/service.go index 439fe92d4e..cd1d4ba8c6 100644 --- a/billing/product/service.go +++ b/billing/product/service.go @@ -12,6 +12,7 @@ import ( "slices" "github.com/google/uuid" + billingerrors "github.com/raystack/frontier/billing/errors" "github.com/raystack/frontier/pkg/utils" "github.com/stripe/stripe-go/v79/client" ) @@ -85,7 +86,7 @@ func (s *Service) Create(ctx context.Context, product Product) (Product, error) }, }) if err != nil { - return Product{}, err + return Product{}, fmt.Errorf("failed to create product at billing provider: %w", billingerrors.TranslateStripeError(err)) } productOb, err := s.productRepository.Create(ctx, product) @@ -213,7 +214,7 @@ func (s *Service) Update(ctx context.Context, product Product) (Product, error) }, }) if err != nil { - return Product{}, err + return Product{}, fmt.Errorf("failed to update product at billing provider: %w", billingerrors.TranslateStripeError(err)) } // check feature updates in product @@ -405,7 +406,7 @@ func (s *Service) setPriceActive(ctx context.Context, price Price, active bool) Params: stripe.Params{Context: ctx}, Active: new(active), }); err != nil { - return err + return fmt.Errorf("failed to update price at billing provider: %w", billingerrors.TranslateStripeError(err)) } } if active { @@ -481,7 +482,7 @@ func (s *Service) CreatePrice(ctx context.Context, price Price) (Price, error) { } stripePrice, err := s.stripeClient.Prices.New(providerParams) if err != nil { - return Price{}, err + return Price{}, fmt.Errorf("failed to create price at billing provider: %w", billingerrors.TranslateStripeError(err)) } price.ProviderID = stripePrice.ID @@ -533,7 +534,7 @@ func (s *Service) UpdatePrice(ctx context.Context, price Price) (Price, error) { }, }) if err != nil { - return Price{}, err + return Price{}, fmt.Errorf("failed to update price at billing provider: %w", billingerrors.TranslateStripeError(err)) } return s.priceRepository.UpdateByID(ctx, existingPrice) diff --git a/billing/subscription/service.go b/billing/subscription/service.go index 6c9a872d8f..0b83a5824c 100644 --- a/billing/subscription/service.go +++ b/billing/subscription/service.go @@ -20,6 +20,7 @@ import ( "github.com/raystack/frontier/billing/credit" "github.com/raystack/frontier/billing" + billingerrors "github.com/raystack/frontier/billing/errors" "github.com/raystack/frontier/billing/product" "github.com/raystack/frontier/pkg/utils" @@ -330,7 +331,7 @@ func (s *Service) Cancel(ctx context.Context, id string, immediate bool) (Subscr Prorate: new(true), }) if err != nil { - return Subscription{}, fmt.Errorf("failed to cancel subscription at billing provider: %w", err) + return Subscription{}, fmt.Errorf("failed to cancel subscription at billing provider: %w", billingerrors.TranslateStripeError(err)) } sub.State = string(stripeSubscription.Status) if stripeSubscription.CanceledAt > 0 { @@ -359,7 +360,7 @@ func (s *Service) Cancel(ctx context.Context, id string, immediate bool) (Subscr EndBehavior: stripe.String(string(stripe.SubscriptionScheduleEndBehaviorCancel)), }) if err != nil { - return sub, fmt.Errorf("failed to cancel subscription schedule at billing provider: %w", err) + return sub, fmt.Errorf("failed to cancel subscription schedule at billing provider: %w", billingerrors.TranslateStripeError(err)) } sub.Phase.PlanID = "" sub.Phase.Reason = SubscriptionCancel.String() @@ -381,8 +382,8 @@ func (s *Service) createOrGetSchedule(ctx context.Context, sub Subscription) (*s }, }) if err != nil { - // check if it's a subscription not found err - if stripeErr, ok := err.(*stripe.Error); ok && stripeErr.Code == stripe.ErrorCodeResourceMissing { + err = billingerrors.TranslateStripeError(err) + if errors.Is(err, billingerrors.ErrProviderResourceMissing) { return nil, nil, ErrSubscriptionOnProviderNotFound } return nil, nil, fmt.Errorf("failed to get subscription from billing provider: %w", err) @@ -398,7 +399,7 @@ func (s *Service) createOrGetSchedule(ctx context.Context, sub Subscription) (*s }, }) if err != nil { - return nil, nil, fmt.Errorf("failed to get subscription schedule from billing provider: %w", err) + return nil, nil, fmt.Errorf("failed to get subscription schedule from billing provider: %w", billingerrors.TranslateStripeError(err)) } stripeSubscription.Schedule = schedule } @@ -421,7 +422,7 @@ func (s *Service) createOrGetSchedule(ctx context.Context, sub Subscription) (*s }, }) if err != nil { - return nil, nil, fmt.Errorf("failed to create subscription schedule at billing provider: %w", err) + return nil, nil, fmt.Errorf("failed to create subscription schedule at billing provider: %w", billingerrors.TranslateStripeError(err)) } } return stripeSubscription, stripeSubscription.Schedule, nil @@ -483,7 +484,7 @@ func (s *Service) UpdateProductQuantity(ctx context.Context, orgID string, curre PendingInvoiceItemInterval: getPendingInvoiceItemInterval(currentPlan), }) if err != nil { - return fmt.Errorf("failed to update subscription quantity at billing provider: %w", err) + return fmt.Errorf("failed to update subscription quantity at billing provider: %w", billingerrors.TranslateStripeError(err)) } } } @@ -561,7 +562,7 @@ func (s *Service) UpdateProductQuantity(ctx context.Context, orgID string, curre Phases: updatedPhases, }) if err != nil { - return fmt.Errorf("failed to update subscription schedule at billing provider: %w", err) + return fmt.Errorf("failed to update subscription schedule at billing provider: %w", billingerrors.TranslateStripeError(err)) } } @@ -773,7 +774,7 @@ func (s *Service) ChangePlan(ctx context.Context, id string, changeRequest Chang }, }) if err != nil { - return change, fmt.Errorf("failed to update subscription schedule at billing provider: %w", err) + return change, fmt.Errorf("failed to update subscription schedule at billing provider: %w", billingerrors.TranslateStripeError(err)) } // update subscription with new phase @@ -968,7 +969,7 @@ func (s *Service) CancelUpcomingPhase(ctx context.Context, sub Subscription) err }, }) if err != nil { - return fmt.Errorf("failed to update subscription schedule at billing provider: %w", err) + return fmt.Errorf("failed to update subscription schedule at billing provider: %w", billingerrors.TranslateStripeError(err)) } sub.Phase.Reason = ""