diff --git a/cmd/serve.go b/cmd/serve.go index 33d0ed78..87a9a6c9 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -16,10 +16,10 @@ import ( "syscall" "time" - "github.com/stripe/stripe-go/v74" "github.com/urfave/cli/v2" "github.com/urfave/cli/v2/altsrc" "heckel.io/ntfy/v2/log" + "heckel.io/ntfy/v2/payments" "heckel.io/ntfy/v2/server" "heckel.io/ntfy/v2/user" "heckel.io/ntfy/v2/util" @@ -279,6 +279,8 @@ func execServe(c *cli.Context) error { // Check values if firebaseKeyFile != "" && !util.FileExists(firebaseKeyFile) { return errors.New("if set, FCM key file must exist") + } else if firebaseKeyFile != "" && !server.FirebaseAvailable { + return errors.New("cannot set firebase-key-file, support for Firebase is not available (nofirebase)") } else if webPushPublicKey != "" && (webPushPrivateKey == "" || webPushFile == "" || webPushEmailAddress == "" || baseURL == "") { return errors.New("if web push is enabled, web-push-private-key, web-push-public-key, web-push-file, web-push-email-address, and base-url should be set. run 'ntfy webpush keys' to generate keys") } else if keepaliveInterval < 5*time.Second { @@ -320,6 +322,8 @@ func execServe(c *cli.Context) error { return errors.New("cannot set enable-signup, enable-login, enable-reserve-topics, or stripe-secret-key if auth-file is not set") } else if enableSignup && !enableLogin { return errors.New("cannot set enable-signup without also setting enable-login") + } else if !payments.Available && (stripeSecretKey != "" || stripeWebhookKey != "") { + return errors.New("cannot set stripe-secret-key or stripe-webhook-key, support for payments is not available in this build (nopayments)") } else if stripeSecretKey != "" && (stripeWebhookKey == "" || baseURL == "") { return errors.New("if stripe-secret-key is set, stripe-webhook-key and base-url must also be set") } else if twilioAccount != "" && (twilioAuthToken == "" || twilioPhoneNumber == "" || twilioVerifyService == "" || baseURL == "" || authFile == "") { @@ -329,6 +333,8 @@ func execServe(c *cli.Context) error { if messageSizeLimit > 5*1024*1024 { return errors.New("message-size-limit cannot be higher than 5M") } + } else if !server.WebPushAvailable && (webPushPrivateKey != "" || webPushPublicKey != "" || webPushFile != "") { + return errors.New("cannot enable WebPush, support is not available in this build (nowebpush)") } else if webPushExpiryWarningDuration > 0 && webPushExpiryWarningDuration > webPushExpiryDuration { return errors.New("web push expiry warning duration cannot be higher than web push expiry duration") } else if behindProxy && proxyForwardedHeader == "" { @@ -396,8 +402,7 @@ func execServe(c *cli.Context) error { // Stripe things if stripeSecretKey != "" { - stripe.EnableTelemetry = false // Whoa! - stripe.Key = stripeSecretKey + payments.Setup(stripeSecretKey) } // Add default forbidden topics diff --git a/cmd/webpush.go b/cmd/webpush.go index fdcf4ff1..90d9268c 100644 --- a/cmd/webpush.go +++ b/cmd/webpush.go @@ -1,4 +1,4 @@ -//go:build !noserver +//go:build !noserver && !nowebpush package cmd diff --git a/payments/payments.go b/payments/payments.go new file mode 100644 index 00000000..76cfcc00 --- /dev/null +++ b/payments/payments.go @@ -0,0 +1,21 @@ +//go:build !nopayments + +package payments + +import "github.com/stripe/stripe-go/v74" + +// Available is a constant used to indicate that Stripe support is available. +// It can be disabled with the 'nopayments' build tag. +const Available = true + +// SubscriptionStatus is an alias for stripe.SubscriptionStatus +type SubscriptionStatus stripe.SubscriptionStatus + +// PriceRecurringInterval is an alias for stripe.PriceRecurringInterval +type PriceRecurringInterval stripe.PriceRecurringInterval + +// Setup sets the Stripe secret key and disables telemetry +func Setup(stripeSecretKey string) { + stripe.EnableTelemetry = false // Whoa! + stripe.Key = stripeSecretKey +} diff --git a/payments/payments_dummy.go b/payments/payments_dummy.go new file mode 100644 index 00000000..4db8fc2e --- /dev/null +++ b/payments/payments_dummy.go @@ -0,0 +1,18 @@ +//go:build nopayments + +package payments + +// Available is a constant used to indicate that Stripe support is available. +// It can be disabled with the 'nopayments' build tag. +const Available = false + +// SubscriptionStatus is a dummy type +type SubscriptionStatus string + +// PriceRecurringInterval is dummy type +type PriceRecurringInterval string + +// Setup is a dummy type +func Setup(stripeSecretKey string) { + // Nothing to see here +} diff --git a/server/server.go b/server/server.go index 05b5b63a..8b5fb00b 100644 --- a/server/server.go +++ b/server/server.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "gopkg.in/yaml.v2" + "heckel.io/ntfy/v2/payments" "io" "net" "net/http" @@ -165,7 +166,7 @@ func New(conf *Config) (*Server, error) { mailer = &smtpSender{config: conf} } var stripe stripeAPI - if conf.StripeSecretKey != "" { + if payments.Available && conf.StripeSecretKey != "" { stripe = newStripeAPI() } messageCache, err := createMessageCache(conf) diff --git a/server/server_firebase.go b/server/server_firebase.go index 99f1fb28..1b80172e 100644 --- a/server/server_firebase.go +++ b/server/server_firebase.go @@ -1,3 +1,5 @@ +//go:build !nofirebase + package server import ( @@ -14,6 +16,10 @@ import ( ) const ( + // FirebaseAvailable is a constant used to indicate that Firebase support is available. + // It can be disabled with the 'nofirebase' build tag. + FirebaseAvailable = true + fcmMessageLimit = 4000 fcmApnsBodyMessageLimit = 100 ) @@ -73,7 +79,7 @@ type firebaseSenderImpl struct { client *messaging.Client } -func newFirebaseSender(credentialsFile string) (*firebaseSenderImpl, error) { +func newFirebaseSender(credentialsFile string) (firebaseSender, error) { fb, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsFile(credentialsFile)) if err != nil { return nil, err diff --git a/server/server_firebase_dummy.go b/server/server_firebase_dummy.go new file mode 100644 index 00000000..bddceff1 --- /dev/null +++ b/server/server_firebase_dummy.go @@ -0,0 +1,38 @@ +//go:build nofirebase + +package server + +import ( + "errors" + "heckel.io/ntfy/v2/user" +) + +const ( + // FirebaseAvailable is a constant used to indicate that Firebase support is available. + // It can be disabled with the 'nofirebase' build tag. + FirebaseAvailable = false +) + +var ( + errFirebaseNotAvailable = errors.New("Firebase not available") + errFirebaseTemporarilyBanned = errors.New("visitor temporarily banned from using Firebase") +) + +type firebaseClient struct { +} + +func (c *firebaseClient) Send(v *visitor, m *message) error { + return errFirebaseNotAvailable +} + +type firebaseSender interface { + Send(m string) error +} + +func newFirebaseClient(sender firebaseSender, auther user.Auther) *firebaseClient { + return nil +} + +func newFirebaseSender(credentialsFile string) (firebaseSender, error) { + return nil, errFirebaseNotAvailable +} diff --git a/server/server_firebase_test.go b/server/server_firebase_test.go index 2f5b7287..89004cd3 100644 --- a/server/server_firebase_test.go +++ b/server/server_firebase_test.go @@ -1,3 +1,5 @@ +//go:build !nofirebase + package server import ( diff --git a/server/server_payments.go b/server/server_payments.go index 334301bb..0226df4f 100644 --- a/server/server_payments.go +++ b/server/server_payments.go @@ -1,3 +1,5 @@ +//go:build !nopayments + package server import ( @@ -12,6 +14,7 @@ import ( "github.com/stripe/stripe-go/v74/subscription" "github.com/stripe/stripe-go/v74/webhook" "heckel.io/ntfy/v2/log" + "heckel.io/ntfy/v2/payments" "heckel.io/ntfy/v2/user" "heckel.io/ntfy/v2/util" "io" @@ -22,7 +25,7 @@ import ( // Payments in ntfy are done via Stripe. // -// Pretty much all payments related things are in this file. The following processes +// Pretty much all payments-related things are in this file. The following processes // handle payments: // // - Checkout: @@ -464,8 +467,8 @@ func (s *Server) updateSubscriptionAndTier(r *http.Request, v *visitor, u *user. billing := &user.Billing{ StripeCustomerID: customerID, StripeSubscriptionID: subscriptionID, - StripeSubscriptionStatus: stripe.SubscriptionStatus(status), - StripeSubscriptionInterval: stripe.PriceRecurringInterval(interval), + StripeSubscriptionStatus: payments.SubscriptionStatus(status), + StripeSubscriptionInterval: payments.PriceRecurringInterval(interval), StripeSubscriptionPaidUntil: time.Unix(paidUntil, 0), StripeSubscriptionCancelAt: time.Unix(cancelAt, 0), } diff --git a/server/server_payments_dummy.go b/server/server_payments_dummy.go new file mode 100644 index 00000000..dcdc31c6 --- /dev/null +++ b/server/server_payments_dummy.go @@ -0,0 +1,47 @@ +//go:build nopayments + +package server + +import ( + "net/http" +) + +type stripeAPI interface { + CancelSubscription(id string) (string, error) +} + +func newStripeAPI() stripeAPI { + return nil +} + +func (s *Server) fetchStripePrices() (map[string]int64, error) { + return nil, errHTTPNotFound +} + +func (s *Server) handleBillingTiersGet(w http.ResponseWriter, _ *http.Request, _ *visitor) error { + return errHTTPNotFound +} + +func (s *Server) handleAccountBillingSubscriptionCreate(w http.ResponseWriter, r *http.Request, v *visitor) error { + return errHTTPNotFound +} + +func (s *Server) handleAccountBillingSubscriptionCreateSuccess(w http.ResponseWriter, r *http.Request, v *visitor) error { + return errHTTPNotFound +} + +func (s *Server) handleAccountBillingSubscriptionUpdate(w http.ResponseWriter, r *http.Request, v *visitor) error { + return errHTTPNotFound +} + +func (s *Server) handleAccountBillingSubscriptionDelete(w http.ResponseWriter, r *http.Request, v *visitor) error { + return errHTTPNotFound +} + +func (s *Server) handleAccountBillingPortalSessionCreate(w http.ResponseWriter, r *http.Request, v *visitor) error { + return errHTTPNotFound +} + +func (s *Server) handleAccountBillingWebhook(_ http.ResponseWriter, r *http.Request, v *visitor) error { + return errHTTPNotFound +} diff --git a/server/server_payments_test.go b/server/server_payments_test.go index 56d4cc6a..5dd75921 100644 --- a/server/server_payments_test.go +++ b/server/server_payments_test.go @@ -1,3 +1,5 @@ +//go:build !nopayments + package server import ( @@ -6,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/stripe/stripe-go/v74" "golang.org/x/time/rate" + "heckel.io/ntfy/v2/payments" "heckel.io/ntfy/v2/user" "heckel.io/ntfy/v2/util" "io" @@ -345,8 +348,8 @@ func TestPayments_Checkout_Success_And_Increase_Rate_Limits_Reset_Visitor(t *tes require.Nil(t, u.Tier) require.Equal(t, "", u.Billing.StripeCustomerID) require.Equal(t, "", u.Billing.StripeSubscriptionID) - require.Equal(t, stripe.SubscriptionStatus(""), u.Billing.StripeSubscriptionStatus) - require.Equal(t, stripe.PriceRecurringInterval(""), u.Billing.StripeSubscriptionInterval) + require.Equal(t, payments.SubscriptionStatus(""), u.Billing.StripeSubscriptionStatus) + require.Equal(t, payments.PriceRecurringInterval(""), u.Billing.StripeSubscriptionInterval) require.Equal(t, int64(0), u.Billing.StripeSubscriptionPaidUntil.Unix()) require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix()) require.Equal(t, int64(0), u.Stats.Messages) // Messages and emails are not persisted for no-tier users! @@ -362,8 +365,8 @@ func TestPayments_Checkout_Success_And_Increase_Rate_Limits_Reset_Visitor(t *tes require.Equal(t, "starter", u.Tier.Code) // Not "pro" require.Equal(t, "acct_5555", u.Billing.StripeCustomerID) require.Equal(t, "sub_1234", u.Billing.StripeSubscriptionID) - require.Equal(t, stripe.SubscriptionStatusActive, u.Billing.StripeSubscriptionStatus) - require.Equal(t, stripe.PriceRecurringIntervalMonth, u.Billing.StripeSubscriptionInterval) + require.Equal(t, payments.SubscriptionStatus(stripe.SubscriptionStatusActive), u.Billing.StripeSubscriptionStatus) + require.Equal(t, payments.PriceRecurringInterval(stripe.PriceRecurringIntervalMonth), u.Billing.StripeSubscriptionInterval) require.Equal(t, int64(123456789), u.Billing.StripeSubscriptionPaidUntil.Unix()) require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix()) require.Equal(t, int64(0), u.Stats.Messages) @@ -473,8 +476,8 @@ func TestPayments_Webhook_Subscription_Updated_Downgrade_From_PastDue_To_Active( billing := &user.Billing{ StripeCustomerID: "acct_5555", StripeSubscriptionID: "sub_1234", - StripeSubscriptionStatus: stripe.SubscriptionStatusPastDue, - StripeSubscriptionInterval: stripe.PriceRecurringIntervalMonth, + StripeSubscriptionStatus: payments.SubscriptionStatus(stripe.SubscriptionStatusPastDue), + StripeSubscriptionInterval: payments.PriceRecurringInterval(stripe.PriceRecurringIntervalMonth), StripeSubscriptionPaidUntil: time.Unix(123, 0), StripeSubscriptionCancelAt: time.Unix(456, 0), } @@ -517,10 +520,10 @@ func TestPayments_Webhook_Subscription_Updated_Downgrade_From_PastDue_To_Active( require.Equal(t, "starter", u.Tier.Code) // Not "pro" require.Equal(t, "acct_5555", u.Billing.StripeCustomerID) require.Equal(t, "sub_1234", u.Billing.StripeSubscriptionID) - require.Equal(t, stripe.SubscriptionStatusActive, u.Billing.StripeSubscriptionStatus) // Not "past_due" - require.Equal(t, stripe.PriceRecurringIntervalYear, u.Billing.StripeSubscriptionInterval) // Not "month" - require.Equal(t, int64(1674268231), u.Billing.StripeSubscriptionPaidUntil.Unix()) // Updated - require.Equal(t, int64(1674299999), u.Billing.StripeSubscriptionCancelAt.Unix()) // Updated + require.Equal(t, payments.SubscriptionStatus(stripe.SubscriptionStatusActive), u.Billing.StripeSubscriptionStatus) // Not "past_due" + require.Equal(t, payments.PriceRecurringInterval(stripe.PriceRecurringIntervalYear), u.Billing.StripeSubscriptionInterval) // Not "month" + require.Equal(t, int64(1674268231), u.Billing.StripeSubscriptionPaidUntil.Unix()) // Updated + require.Equal(t, int64(1674299999), u.Billing.StripeSubscriptionCancelAt.Unix()) // Updated // Verify that reservations were deleted r, err := s.userManager.Reservations("phil") @@ -580,8 +583,8 @@ func TestPayments_Webhook_Subscription_Deleted(t *testing.T) { require.Nil(t, s.userManager.ChangeBilling(u.Name, &user.Billing{ StripeCustomerID: "acct_5555", StripeSubscriptionID: "sub_1234", - StripeSubscriptionStatus: stripe.SubscriptionStatusPastDue, - StripeSubscriptionInterval: stripe.PriceRecurringIntervalMonth, + StripeSubscriptionStatus: payments.SubscriptionStatus(stripe.SubscriptionStatusPastDue), + StripeSubscriptionInterval: payments.PriceRecurringInterval(stripe.PriceRecurringIntervalMonth), StripeSubscriptionPaidUntil: time.Unix(123, 0), StripeSubscriptionCancelAt: time.Unix(0, 0), })) @@ -598,7 +601,7 @@ func TestPayments_Webhook_Subscription_Deleted(t *testing.T) { require.Nil(t, u.Tier) require.Equal(t, "acct_5555", u.Billing.StripeCustomerID) require.Equal(t, "", u.Billing.StripeSubscriptionID) - require.Equal(t, stripe.SubscriptionStatus(""), u.Billing.StripeSubscriptionStatus) + require.Equal(t, payments.SubscriptionStatus(""), u.Billing.StripeSubscriptionStatus) require.Equal(t, int64(0), u.Billing.StripeSubscriptionPaidUntil.Unix()) require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix()) diff --git a/server/server_test.go b/server/server_test.go index 41633dd5..19c0165c 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -23,7 +23,6 @@ import ( "testing" "time" - "github.com/SherClockHolmes/webpush-go" "github.com/stretchr/testify/require" "heckel.io/ntfy/v2/log" "heckel.io/ntfy/v2/util" @@ -281,30 +280,6 @@ func TestServer_WebEnabled(t *testing.T) { rr = request(t, s2, "GET", "/app.html", "", nil) require.Equal(t, 200, rr.Code) } - -func TestServer_WebPushEnabled(t *testing.T) { - conf := newTestConfig(t) - conf.WebRoot = "" // Disable web app - s := newTestServer(t, conf) - - rr := request(t, s, "GET", "/manifest.webmanifest", "", nil) - require.Equal(t, 404, rr.Code) - - conf2 := newTestConfig(t) - s2 := newTestServer(t, conf2) - - rr = request(t, s2, "GET", "/manifest.webmanifest", "", nil) - require.Equal(t, 404, rr.Code) - - conf3 := newTestConfigWithWebPush(t) - s3 := newTestServer(t, conf3) - - rr = request(t, s3, "GET", "/manifest.webmanifest", "", nil) - require.Equal(t, 200, rr.Code) - require.Equal(t, "application/manifest+json", rr.Header().Get("Content-Type")) - -} - func TestServer_PublishLargeMessage(t *testing.T) { c := newTestConfig(t) c.AttachmentCacheDir = "" // Disable attachments @@ -3257,17 +3232,6 @@ func newTestConfigWithAuthFile(t *testing.T) *Config { return conf } -func newTestConfigWithWebPush(t *testing.T) *Config { - conf := newTestConfig(t) - privateKey, publicKey, err := webpush.GenerateVAPIDKeys() - require.Nil(t, err) - conf.WebPushFile = filepath.Join(t.TempDir(), "webpush.db") - conf.WebPushEmailAddress = "testing@example.com" - conf.WebPushPrivateKey = privateKey - conf.WebPushPublicKey = publicKey - return conf -} - func newTestServer(t *testing.T, config *Config) *Server { server, err := New(config) require.Nil(t, err) diff --git a/server/server_webpush.go b/server/server_webpush.go index cd41759d..526e06f2 100644 --- a/server/server_webpush.go +++ b/server/server_webpush.go @@ -1,3 +1,5 @@ +//go:build !nowebpush + package server import ( @@ -13,6 +15,10 @@ import ( ) const ( + // WebPushAvailable is a constant used to indicate that WebPush support is available. + // It can be disabled with the 'nowebpush' build tag. + WebPushAvailable = true + webPushTopicSubscribeLimit = 50 ) diff --git a/server/server_webpush_dummy.go b/server/server_webpush_dummy.go new file mode 100644 index 00000000..425b22a6 --- /dev/null +++ b/server/server_webpush_dummy.go @@ -0,0 +1,29 @@ +//go:build nowebpush + +package server + +import ( + "net/http" +) + +const ( + // WebPushAvailable is a constant used to indicate that WebPush support is available. + // It can be disabled with the 'nowebpush' build tag. + WebPushAvailable = false +) + +func (s *Server) handleWebPushUpdate(w http.ResponseWriter, r *http.Request, v *visitor) error { + return errHTTPNotFound +} + +func (s *Server) handleWebPushDelete(w http.ResponseWriter, r *http.Request, _ *visitor) error { + return errHTTPNotFound +} + +func (s *Server) publishToWebPushEndpoints(v *visitor, m *message) { + // Nothing to see here +} + +func (s *Server) pruneAndNotifyWebPushSubscriptions() { + // Nothing to see here +} diff --git a/server/server_webpush_test.go b/server/server_webpush_test.go index f7379511..f116103a 100644 --- a/server/server_webpush_test.go +++ b/server/server_webpush_test.go @@ -1,8 +1,11 @@ +//go:build !nowebpush + package server import ( "encoding/json" "fmt" + "github.com/SherClockHolmes/webpush-go" "github.com/stretchr/testify/require" "heckel.io/ntfy/v2/user" "heckel.io/ntfy/v2/util" @@ -10,6 +13,7 @@ import ( "net/http" "net/http/httptest" "net/netip" + "path/filepath" "strings" "sync/atomic" "testing" @@ -20,6 +24,28 @@ const ( testWebPushEndpoint = "https://updates.push.services.mozilla.com/wpush/v1/AAABBCCCDDEEEFFF" ) +func TestServer_WebPush_Enabled(t *testing.T) { + conf := newTestConfig(t) + conf.WebRoot = "" // Disable web app + s := newTestServer(t, conf) + + rr := request(t, s, "GET", "/manifest.webmanifest", "", nil) + require.Equal(t, 404, rr.Code) + + conf2 := newTestConfig(t) + s2 := newTestServer(t, conf2) + + rr = request(t, s2, "GET", "/manifest.webmanifest", "", nil) + require.Equal(t, 404, rr.Code) + + conf3 := newTestConfigWithWebPush(t) + s3 := newTestServer(t, conf3) + + rr = request(t, s3, "GET", "/manifest.webmanifest", "", nil) + require.Equal(t, 200, rr.Code) + require.Equal(t, "application/manifest+json", rr.Header().Get("Content-Type")) + +} func TestServer_WebPush_Disabled(t *testing.T) { s := newTestServer(t, newTestConfig(t)) @@ -254,3 +280,14 @@ func requireSubscriptionCount(t *testing.T, s *Server, topic string, expectedLen require.Nil(t, err) require.Len(t, subs, expectedLength) } + +func newTestConfigWithWebPush(t *testing.T) *Config { + conf := newTestConfig(t) + privateKey, publicKey, err := webpush.GenerateVAPIDKeys() + require.Nil(t, err) + conf.WebPushFile = filepath.Join(t.TempDir(), "webpush.db") + conf.WebPushEmailAddress = "testing@example.com" + conf.WebPushPrivateKey = privateKey + conf.WebPushPublicKey = publicKey + return conf +} diff --git a/user/manager.go b/user/manager.go index 8cea653c..dc9bbaee 100644 --- a/user/manager.go +++ b/user/manager.go @@ -7,9 +7,9 @@ import ( "errors" "fmt" "github.com/mattn/go-sqlite3" - "github.com/stripe/stripe-go/v74" "golang.org/x/crypto/bcrypt" "heckel.io/ntfy/v2/log" + "heckel.io/ntfy/v2/payments" "heckel.io/ntfy/v2/util" "net/netip" "path/filepath" @@ -1242,12 +1242,12 @@ func (a *Manager) readUser(rows *sql.Rows) (*User, error) { Calls: calls, }, Billing: &Billing{ - StripeCustomerID: stripeCustomerID.String, // May be empty - StripeSubscriptionID: stripeSubscriptionID.String, // May be empty - StripeSubscriptionStatus: stripe.SubscriptionStatus(stripeSubscriptionStatus.String), // May be empty - StripeSubscriptionInterval: stripe.PriceRecurringInterval(stripeSubscriptionInterval.String), // May be empty - StripeSubscriptionPaidUntil: time.Unix(stripeSubscriptionPaidUntil.Int64, 0), // May be zero - StripeSubscriptionCancelAt: time.Unix(stripeSubscriptionCancelAt.Int64, 0), // May be zero + StripeCustomerID: stripeCustomerID.String, // May be empty + StripeSubscriptionID: stripeSubscriptionID.String, // May be empty + StripeSubscriptionStatus: payments.SubscriptionStatus(stripeSubscriptionStatus.String), // May be empty + StripeSubscriptionInterval: payments.PriceRecurringInterval(stripeSubscriptionInterval.String), // May be empty + StripeSubscriptionPaidUntil: time.Unix(stripeSubscriptionPaidUntil.Int64, 0), // May be zero + StripeSubscriptionCancelAt: time.Unix(stripeSubscriptionCancelAt.Int64, 0), // May be zero }, Deleted: deleted.Valid, } diff --git a/user/manager_test.go b/user/manager_test.go index c2ae8b87..b8817682 100644 --- a/user/manager_test.go +++ b/user/manager_test.go @@ -4,7 +4,6 @@ import ( "database/sql" "fmt" "github.com/stretchr/testify/require" - "github.com/stripe/stripe-go/v74" "golang.org/x/crypto/bcrypt" "heckel.io/ntfy/v2/util" "net/netip" @@ -164,8 +163,8 @@ func TestManager_AddUser_And_Query(t *testing.T) { require.Nil(t, a.ChangeBilling("user", &Billing{ StripeCustomerID: "acct_123", StripeSubscriptionID: "sub_123", - StripeSubscriptionStatus: stripe.SubscriptionStatusActive, - StripeSubscriptionInterval: stripe.PriceRecurringIntervalMonth, + StripeSubscriptionStatus: "active", + StripeSubscriptionInterval: "month", StripeSubscriptionPaidUntil: time.Now().Add(time.Hour), StripeSubscriptionCancelAt: time.Unix(0, 0), })) diff --git a/user/types.go b/user/types.go index e501e732..085f88fd 100644 --- a/user/types.go +++ b/user/types.go @@ -2,8 +2,8 @@ package user import ( "errors" - "github.com/stripe/stripe-go/v74" "heckel.io/ntfy/v2/log" + "heckel.io/ntfy/v2/payments" "net/netip" "strings" "time" @@ -140,8 +140,8 @@ type Stats struct { type Billing struct { StripeCustomerID string StripeSubscriptionID string - StripeSubscriptionStatus stripe.SubscriptionStatus - StripeSubscriptionInterval stripe.PriceRecurringInterval + StripeSubscriptionStatus payments.SubscriptionStatus + StripeSubscriptionInterval payments.PriceRecurringInterval StripeSubscriptionPaidUntil time.Time StripeSubscriptionCancelAt time.Time }