package billing import ( "context" "testing" "bookra/apps/backend/internal/config" "bookra/apps/backend/internal/db" "bookra/apps/backend/internal/domain" ) func testConfig() config.Config { return config.Config{ FrontendURL: "http://localhost:3000", PaddleAPIKey: "pdl_sdbx_apikey_123", PaddleWebhookKey: "pdl_ntf_123", PaddlePriceMatrix: map[string]map[string]string{ "starter": {"czk": "pri_starter_czk", "usd": "pri_starter_usd"}, "pro": {"czk": "pri_pro_czk", "usd": "pri_pro_usd"}, "business": {"czk": "pri_business_czk", "usd": "pri_business_usd"}, }, } } func TestGetSubscriptionFallsBackToSnapshotAndEntitlements(t *testing.T) { service := NewService(testConfig(), db.NewMemoryRepository()) snapshot, err := service.GetSubscription(context.Background(), domain.Principal{ Subject: "demo-owner", Email: "owner@bookra.dev", }) if err != nil { t.Fatalf("get subscription: %v", err) } if snapshot.PlanCode != "pro" { t.Fatalf("expected pro, got %s", snapshot.PlanCode) } if snapshot.Provider != "paddle" { t.Fatalf("expected paddle provider, got %s", snapshot.Provider) } if snapshot.Entitlements.MaxLocations != 3 { t.Fatalf("expected 3 locations, got %d", snapshot.Entitlements.MaxLocations) } } func TestCreateCheckoutRequiresPaddleConfig(t *testing.T) { cfg := testConfig() cfg.PaddleAPIKey = "" service := NewService(cfg, db.NewMemoryRepository()) response, err := service.CreateCheckoutSession(context.Background(), domain.Principal{ Subject: "demo-owner", Email: "owner@bookra.dev", }, "pro", "czk") if err != ErrPaddleNotConfigured { t.Fatalf("expected ErrPaddleNotConfigured, got response=%v err=%v", response, err) } } func TestCreateCheckoutReturnsLaunchPayload(t *testing.T) { service := NewService(testConfig(), db.NewMemoryRepository()) response, err := service.CreateCheckoutSession(context.Background(), domain.Principal{ Subject: "demo-owner", Email: "owner@bookra.dev", }, "pro", "czk") if err != nil { t.Fatalf("create checkout: %v", err) } if response.PriceID != "pri_pro_czk" { t.Fatalf("expected pri_pro_czk, got %s", response.PriceID) } if response.CustomData["tenantId"] == "" { t.Fatal("expected tenantId in customData") } if response.SuccessRedirectURL == "" || response.CancelRedirectURL == "" { t.Fatal("expected redirect URLs") } } func TestRefreshRequiresPaddleKeyWhenCustomerExists(t *testing.T) { cfg := testConfig() cfg.PaddleAPIKey = "" service := NewService(cfg, db.NewMemoryRepository()) snapshot, err := service.Refresh(context.Background(), domain.Principal{ Subject: "demo-owner", Email: "owner@bookra.dev", }) if err != ErrPaddleNotConfigured { t.Fatalf("expected ErrPaddleNotConfigured, got snapshot=%v err=%v", snapshot, err) } } func TestGetSubscriptionDisablesCheckoutWhenWebhookMissing(t *testing.T) { cfg := testConfig() cfg.PaddleWebhookKey = "" service := NewService(cfg, db.NewMemoryRepository()) snapshot, err := service.GetSubscription(context.Background(), domain.Principal{ Subject: "demo-owner", Email: "owner@bookra.dev", }) if err != nil { t.Fatalf("get subscription: %v", err) } if snapshot.CheckoutURLAvailable { t.Fatal("expected checkout unavailable without webhook secret") } } func TestGetSubscriptionEnablesCheckoutWhenPaddleConfigured(t *testing.T) { service := NewService(testConfig(), db.NewMemoryRepository()) snapshot, err := service.GetSubscription(context.Background(), domain.Principal{ Subject: "demo-owner", Email: "owner@bookra.dev", }) if err != nil { t.Fatalf("get subscription: %v", err) } if !snapshot.CheckoutURLAvailable { t.Fatal("expected checkout available when paddle is configured") } if !snapshot.PortalAvailable { t.Fatal("expected portal available when customer exists") } } func TestCreatePortalSessionRequiresCustomer(t *testing.T) { repo := db.NewMemoryRepository() membership, err := repo.GetTenantMembershipByUserID(context.Background(), "demo-owner") if err != nil { t.Fatalf("get membership: %v", err) } if err := repo.UpdateTenantBillingCustomerID(context.Background(), membership.Tenant.ID, ""); err != nil { t.Fatalf("clear billing customer: %v", err) } service := NewService(testConfig(), repo) _, err = service.CreatePortalSession(context.Background(), domain.Principal{ Subject: "demo-owner", Email: "owner@bookra.dev", }) if err != ErrBillingCustomerMissing { t.Fatalf("expected ErrBillingCustomerMissing, got %v", err) } }