Skip to content

Commit

Permalink
feat(app): reuse app error codes in stripe app (#2144)
Browse files Browse the repository at this point in the history
  • Loading branch information
hekike authored Jan 23, 2025
1 parent 548a91f commit f702679
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 128 deletions.
9 changes: 4 additions & 5 deletions openmeter/app/stripe/adapter/customer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/openmeterio/openmeter/openmeter/app"
appentitybase "github.com/openmeterio/openmeter/openmeter/app/entity/base"
appstripe "github.com/openmeterio/openmeter/openmeter/app/stripe"
stripeclient "github.com/openmeterio/openmeter/openmeter/app/stripe/client"
appstripeentity "github.com/openmeterio/openmeter/openmeter/app/stripe/entity"
entdb "github.com/openmeterio/openmeter/openmeter/ent/db"
Expand All @@ -17,7 +16,7 @@ import (
// GetStripeCustomerData gets stripe customer data
func (a adapter) GetStripeCustomerData(ctx context.Context, input appstripeentity.GetStripeCustomerDataInput) (appstripeentity.CustomerData, error) {
if err := input.Validate(); err != nil {
return appstripeentity.CustomerData{}, appstripe.ValidationError{
return appstripeentity.CustomerData{}, app.ValidationError{
Err: fmt.Errorf("error getting stripe customer data: %w", err),
}
}
Expand Down Expand Up @@ -56,7 +55,7 @@ func (a adapter) GetStripeCustomerData(ctx context.Context, input appstripeentit
// UpsertStripeCustomerData upserts stripe customer data
func (a adapter) UpsertStripeCustomerData(ctx context.Context, input appstripeentity.UpsertStripeCustomerDataInput) error {
if err := input.Validate(); err != nil {
return appstripe.ValidationError{
return app.ValidationError{
Err: fmt.Errorf("error upsert stripe customer data: %w", err),
}
}
Expand Down Expand Up @@ -106,7 +105,7 @@ func (a adapter) UpsertStripeCustomerData(ctx context.Context, input appstripeen
// DeleteStripeCustomerData deletes stripe customer data
func (a adapter) DeleteStripeCustomerData(ctx context.Context, input appstripeentity.DeleteStripeCustomerDataInput) error {
if err := input.Validate(); err != nil {
return appstripe.ValidationError{
return app.ValidationError{
Err: fmt.Errorf("error delete stripe customer data: %w", err),
}
}
Expand All @@ -123,7 +122,7 @@ func (a adapter) DeleteStripeCustomerData(ctx context.Context, input appstripeen
}

if namespace == "" {
return appstripe.ValidationError{
return app.ValidationError{
Err: fmt.Errorf("error delete stripe customer data: namespace is empty"),
}
}
Expand Down
39 changes: 21 additions & 18 deletions openmeter/app/stripe/adapter/stripe.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (a adapter) GetStripeAppClientFactory() stripeclient.StripeAppClientFactory
// CreateApp creates a new app
func (a adapter) CreateStripeApp(ctx context.Context, input appstripeentity.CreateAppStripeInput) (appstripeentity.AppBase, error) {
if err := input.Validate(); err != nil {
return appstripeentity.AppBase{}, appstripe.ValidationError{
return appstripeentity.AppBase{}, app.ValidationError{
Err: fmt.Errorf("error create stripe app: %w", err),
}
}
Expand Down Expand Up @@ -86,7 +86,7 @@ func (a adapter) CreateStripeApp(ctx context.Context, input appstripeentity.Crea
func (a adapter) UpdateAPIKey(ctx context.Context, input appstripeentity.UpdateAPIKeyInput) error {
// Validate the input
if err := input.Validate(); err != nil {
return appstripe.ValidationError{
return app.ValidationError{
Err: fmt.Errorf("error replace api key: %w", err),
}
}
Expand Down Expand Up @@ -120,7 +120,7 @@ func (a adapter) UpdateAPIKey(ctx context.Context, input appstripeentity.UpdateA
err = errors.New("new stripe api key is in test mode but the app is in live mode")
}

return appstripe.ValidationError{
return app.ValidationError{
Err: err,
}
}
Expand All @@ -133,7 +133,7 @@ func (a adapter) UpdateAPIKey(ctx context.Context, input appstripeentity.UpdateA

// Check if the stripe account id matches with the stored one
if stripeAccount.StripeAccountID != appData.StripeAccountID {
return appstripe.ValidationError{
return app.ValidationError{
Err: fmt.Errorf("stripe account id mismatch: %s != %s", stripeAccount.StripeAccountID, appData.StripeAccountID),
}
}
Expand Down Expand Up @@ -166,7 +166,7 @@ func (a adapter) UpdateAPIKey(ctx context.Context, input appstripeentity.UpdateA
// GetStripeAppData gets stripe customer data
func (a adapter) GetStripeAppData(ctx context.Context, input appstripeentity.GetStripeAppDataInput) (appstripeentity.AppData, error) {
if err := input.Validate(); err != nil {
return appstripeentity.AppData{}, appstripe.ValidationError{
return appstripeentity.AppData{}, app.ValidationError{
Err: fmt.Errorf("error getting stripe customer data: %w", err),
}
}
Expand Down Expand Up @@ -200,7 +200,7 @@ func (a adapter) GetStripeAppData(ctx context.Context, input appstripeentity.Get
// DeleteStripeAppData deletes the stripe app data
func (a adapter) DeleteStripeAppData(ctx context.Context, input appstripeentity.DeleteStripeAppDataInput) error {
if err := input.Validate(); err != nil {
return appstripe.ValidationError{
return app.ValidationError{
Err: fmt.Errorf("error delete stripe app: %w", err),
}
}
Expand Down Expand Up @@ -229,7 +229,7 @@ func (a adapter) DeleteStripeAppData(ctx context.Context, input appstripeentity.
// GetWebhookSecret gets the webhook secret
func (a adapter) GetWebhookSecret(ctx context.Context, input appstripeentity.GetWebhookSecretInput) (appstripeentity.GetWebhookSecretOutput, error) {
if err := input.Validate(); err != nil {
return secretentity.Secret{}, appstripe.ValidationError{
return secretentity.Secret{}, app.ValidationError{
Err: fmt.Errorf("error get webhook secret: %w", err),
}
}
Expand All @@ -243,8 +243,11 @@ func (a adapter) GetWebhookSecret(ctx context.Context, input appstripeentity.Get
Only(ctx)
if err != nil {
if entdb.IsNotFound(err) {
return secretentity.Secret{}, appstripe.WebhookAppNotFoundError{
AppID: input.AppID,
// We don't know the namespace from the app id for webhook requests
return secretentity.Secret{}, app.AppNotFoundError{
AppID: appentitybase.AppID{
ID: input.AppID,
},
}
}

Expand All @@ -269,7 +272,7 @@ func (a adapter) GetWebhookSecret(ctx context.Context, input appstripeentity.Get
// SetCustomerDefaultPaymentMethod sets the default payment method for a customer
func (a adapter) SetCustomerDefaultPaymentMethod(ctx context.Context, input appstripeentity.SetCustomerDefaultPaymentMethodInput) (appstripeentity.SetCustomerDefaultPaymentMethodOutput, error) {
if err := input.Validate(); err != nil {
return appstripeentity.SetCustomerDefaultPaymentMethodOutput{}, appstripe.ValidationError{
return appstripeentity.SetCustomerDefaultPaymentMethodOutput{}, app.ValidationError{
Err: fmt.Errorf("error set customer default payment method: %w", err),
}
}
Expand All @@ -286,10 +289,10 @@ func (a adapter) SetCustomerDefaultPaymentMethod(ctx context.Context, input apps
Only(ctx)
if err != nil {
if entdb.IsNotFound(err) {
return appstripeentity.SetCustomerDefaultPaymentMethodOutput{}, appstripe.StripeCustomerPreConditionError{
AppID: input.AppID,
StripeCustomerID: input.StripeCustomerID,
Condition: "stripe customer has no data for stripe app",
return appstripeentity.SetCustomerDefaultPaymentMethodOutput{}, app.AppCustomerPreConditionError{
AppID: input.AppID,
AppType: appentitybase.AppTypeStripe,
Condition: fmt.Sprintf("stripe customer has no data for stripe app: %s", input.StripeCustomerID),
}
}
}
Expand Down Expand Up @@ -330,7 +333,7 @@ func (a adapter) SetCustomerDefaultPaymentMethod(ctx context.Context, input apps
// CreateCheckoutSession creates a new checkout session
func (a adapter) CreateCheckoutSession(ctx context.Context, input appstripeentity.CreateCheckoutSessionInput) (appstripeentity.CreateCheckoutSessionOutput, error) {
if err := input.Validate(); err != nil {
return appstripeentity.CreateCheckoutSessionOutput{}, appstripe.ValidationError{
return appstripeentity.CreateCheckoutSessionOutput{}, app.ValidationError{
Err: fmt.Errorf("error create checkout session: %w", err),
}
}
Expand Down Expand Up @@ -361,7 +364,7 @@ func (a adapter) CreateCheckoutSession(ctx context.Context, input appstripeentit
Only(ctx)
if err != nil {
if entdb.IsNotFound(err) {
return appstripeentity.CreateCheckoutSessionOutput{}, appstripe.AppNotFoundError{
return appstripeentity.CreateCheckoutSessionOutput{}, app.AppNotFoundError{
AppID: appID,
}
}
Expand Down Expand Up @@ -499,7 +502,7 @@ func (a adapter) CreateCheckoutSession(ctx context.Context, input appstripeentit
func (a adapter) GetSupplierContact(ctx context.Context, input appstripeentity.GetSupplierContactInput) (billing.SupplierContact, error) {
// Validate input
if err := input.Validate(); err != nil {
return billing.SupplierContact{}, appstripe.ValidationError{
return billing.SupplierContact{}, app.ValidationError{
Err: fmt.Errorf("error validate input: %w", err),
}
}
Expand Down Expand Up @@ -561,7 +564,7 @@ func (a adapter) GetSupplierContact(ctx context.Context, input appstripeentity.G
func (a adapter) GetMaskedSecretAPIKey(secretAPIKeyID secretentity.SecretID) (string, error) {
// Validate input
if err := secretAPIKeyID.Validate(); err != nil {
return "", appstripe.ValidationError{
return "", app.ValidationError{
Err: fmt.Errorf("error validate input: %w", err),
}
}
Expand Down
86 changes: 0 additions & 86 deletions openmeter/app/stripe/errors.go

This file was deleted.

4 changes: 2 additions & 2 deletions openmeter/app/stripe/httpdriver/checkout_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"github.com/stripe/stripe-go/v80"

"github.com/openmeterio/openmeter/api"
"github.com/openmeterio/openmeter/openmeter/app"
appentitybase "github.com/openmeterio/openmeter/openmeter/app/entity/base"
appstripe "github.com/openmeterio/openmeter/openmeter/app/stripe"
stripeclient "github.com/openmeterio/openmeter/openmeter/app/stripe/client"
appstripeentity "github.com/openmeterio/openmeter/openmeter/app/stripe/entity"
customerentity "github.com/openmeterio/openmeter/openmeter/customer/entity"
Expand Down Expand Up @@ -53,7 +53,7 @@ func (h *handler) CreateAppStripeCheckoutSession() CreateAppStripeCheckoutSessio
// If err try to parse customer field as customer input
customerCreate, err := body.Customer.AsCustomerCreate()
if err != nil {
return CreateAppStripeCheckoutSessionRequest{}, appstripe.ValidationError{
return CreateAppStripeCheckoutSessionRequest{}, app.ValidationError{
Err: fmt.Errorf("failed to decode customer: %w", err),
}
}
Expand Down
14 changes: 9 additions & 5 deletions openmeter/app/stripe/httpdriver/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"net/http"

"github.com/openmeterio/openmeter/openmeter/app"
appstripe "github.com/openmeterio/openmeter/openmeter/app/stripe"
customerentity "github.com/openmeterio/openmeter/openmeter/customer/entity"
"github.com/openmeterio/openmeter/pkg/framework/commonhttp"
"github.com/openmeterio/openmeter/pkg/framework/transport/httptransport"
Expand All @@ -14,10 +13,15 @@ import (

func errorEncoder() httptransport.ErrorEncoder {
return func(ctx context.Context, err error, w http.ResponseWriter, r *http.Request) bool {
return commonhttp.HandleErrorIfTypeMatches[appstripe.AppNotFoundError](ctx, http.StatusNotFound, err, w) ||
commonhttp.HandleErrorIfTypeMatches[appstripe.WebhookAppNotFoundError](ctx, http.StatusNotFound, err, w) ||
commonhttp.HandleErrorIfTypeMatches[appstripe.ValidationError](ctx, http.StatusBadRequest, err, w) ||
commonhttp.HandleErrorIfTypeMatches[appstripe.StripeCustomerPreConditionError](ctx, http.StatusPreconditionFailed, err, w) ||
// TODO (pmarton): We need to find a better way to handle cross package errors instead of registering all of them here
return commonhttp.HandleErrorIfTypeMatches[app.AppNotFoundError](ctx, http.StatusNotFound, err, w) ||
commonhttp.HandleErrorIfTypeMatches[app.AppDefaultNotFoundError](ctx, http.StatusNotFound, err, w) ||
commonhttp.HandleErrorIfTypeMatches[app.AppProviderError](ctx, http.StatusFailedDependency, err, w) ||
commonhttp.HandleErrorIfTypeMatches[app.AppProviderAuthenticationError](ctx, http.StatusUnauthorized, err, w) ||
commonhttp.HandleErrorIfTypeMatches[app.AppProviderPreConditionError](ctx, http.StatusPreconditionFailed, err, w) ||
commonhttp.HandleErrorIfTypeMatches[app.AppCustomerPreConditionError](ctx, http.StatusPreconditionFailed, err, w) ||
commonhttp.HandleErrorIfTypeMatches[app.ValidationError](ctx, http.StatusBadRequest, err, w) ||

// TODO (pmarton): We need to add this customer error because Stripe Checkout Session creation can create a customer and fail with this error
// On the long term all errors should implement a common interface that controls the HTTP status code.
commonhttp.HandleErrorIfTypeMatches[customerentity.SubjectKeyConflictError](ctx, http.StatusPreconditionFailed, err, w) ||
Expand Down
16 changes: 8 additions & 8 deletions openmeter/app/stripe/httpdriver/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"github.com/stripe/stripe-go/v80/webhook"

"github.com/openmeterio/openmeter/api"
"github.com/openmeterio/openmeter/openmeter/app"
appentitybase "github.com/openmeterio/openmeter/openmeter/app/entity/base"
appstripe "github.com/openmeterio/openmeter/openmeter/app/stripe"
stripeclient "github.com/openmeterio/openmeter/openmeter/app/stripe/client"
appstripeentity "github.com/openmeterio/openmeter/openmeter/app/stripe/entity"
"github.com/openmeterio/openmeter/pkg/framework/commonhttp"
Expand Down Expand Up @@ -53,7 +53,7 @@ func (h *handler) AppStripeWebhook() AppStripeWebhookHandler {
// Validate the webhook event
event, err := webhook.ConstructEventWithTolerance(params.Payload, r.Header.Get("Stripe-Signature"), secret.Value, time.Hour*10000)
if err != nil {
return AppStripeWebhookRequest{}, appstripe.ValidationError{
return AppStripeWebhookRequest{}, app.ValidationError{
Err: fmt.Errorf("failed to construct webhook event: %w", err),
}
}
Expand All @@ -79,7 +79,7 @@ func (h *handler) AppStripeWebhook() AppStripeWebhookHandler {

err := json.Unmarshal(request.Event.Data.Raw, &paymentIntent)
if err != nil {
return AppStripeWebhookResponse{}, appstripe.ValidationError{
return AppStripeWebhookResponse{}, app.ValidationError{
Err: fmt.Errorf("failed to unmarshal payment intent: %w", err),
}
}
Expand All @@ -88,7 +88,7 @@ func (h *handler) AppStripeWebhook() AppStripeWebhookHandler {
if metadataAppId, ok := paymentIntent.Metadata[stripeclient.SetupIntentDataMetadataAppID]; !ok {
// When the app id is set, it must match the app id in the API path
if metadataAppId != "" && metadataAppId != request.AppID.ID {
return AppStripeWebhookResponse{}, appstripe.ValidationError{
return AppStripeWebhookResponse{}, app.ValidationError{
Err: fmt.Errorf("appid mismatch: in request %s, in payment intent metadata %s", request.AppID.ID, metadataAppId),
}
}
Expand All @@ -100,20 +100,20 @@ func (h *handler) AppStripeWebhook() AppStripeWebhookHandler {

// This is an extra consistency check that should never fail as we skip manually created payment intents above
if metadataNamespace, ok := paymentIntent.Metadata[stripeclient.SetupIntentDataMetadataNamespace]; !ok || metadataNamespace != request.AppID.Namespace {
return AppStripeWebhookResponse{}, appstripe.ValidationError{
return AppStripeWebhookResponse{}, app.ValidationError{
Err: fmt.Errorf("namespace mismatch: in request %s, in payment intent metadata %s", request.AppID.Namespace, metadataNamespace),
}
}

// Validate the payment intent object
if paymentIntent.Customer == nil {
return AppStripeWebhookResponse{}, appstripe.ValidationError{
return AppStripeWebhookResponse{}, app.ValidationError{
Err: fmt.Errorf("payment intent customer is required"),
}
}

if paymentIntent.PaymentMethod == nil {
return AppStripeWebhookResponse{}, appstripe.ValidationError{
return AppStripeWebhookResponse{}, app.ValidationError{
Err: fmt.Errorf("payment intent payment method is required"),
}
}
Expand Down Expand Up @@ -195,7 +195,7 @@ func (h *handler) AppStripeWebhook() AppStripeWebhookHandler {
}, nil
}

return AppStripeWebhookResponse{}, appstripe.ValidationError{
return AppStripeWebhookResponse{}, app.ValidationError{
Err: fmt.Errorf("unsupported event type: %s", request.Event.Type),
}
},
Expand Down
Loading

0 comments on commit f702679

Please sign in to comment.