diff --git a/cmd/serve.go b/cmd/serve.go index 33d0ed78..37e4c431 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" @@ -320,6 +320,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 == "") { @@ -396,8 +398,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/main.go b/main.go index 4e01a0d6..a492e5d8 100644 --- a/main.go +++ b/main.go @@ -3,9 +3,11 @@ package main import ( "fmt" "github.com/urfave/cli/v2" + "go/build" "heckel.io/ntfy/v2/cmd" "os" "runtime" + "strings" ) var ( @@ -15,16 +17,7 @@ var ( ) func main() { - cli.AppHelpTemplate += fmt.Sprintf(` -Try 'ntfy COMMAND --help' or https://ntfy.sh/docs/ for more information. - -To report a bug, open an issue on GitHub: https://github.com/binwiederhier/ntfy/issues. -If you want to chat, simply join the Discord server (https://discord.gg/cT7ECsZj9w), or -the Matrix room (https://matrix.to/#/#ntfy:matrix.org). - -ntfy %s (%s), runtime %s, built at %s -Copyright (C) Philipp C. Heckel, licensed under Apache License 2.0 & GPLv2 -`, version, commit[:7], runtime.Version(), date) + cli.AppHelpTemplate += buildHelp() app := cmd.New() app.Version = version @@ -34,3 +27,23 @@ Copyright (C) Philipp C. Heckel, licensed under Apache License 2.0 & GPLv2 os.Exit(1) } } + +func buildHelp() string { + if len(commit) > 7 { + commit = commit[:7] + } + var tags string + if len(build.Default.BuildTags) > 0 { + tags = ", with tags " + strings.Join(build.Default.BuildTags, ", ") + } + return fmt.Sprintf(` +Try 'ntfy COMMAND --help' or https://ntfy.sh/docs/ for more information. + +To report a bug, open an issue on GitHub: https://github.com/binwiederhier/ntfy/issues. +If you want to chat, simply join the Discord server (https://discord.gg/cT7ECsZj9w), or +the Matrix room (https://matrix.to/#/#ntfy:matrix.org). + +ntfy %s (%s), runtime %s, built at %s%s +Copyright (C) Philipp C. Heckel, licensed under Apache License 2.0 & GPLv2 +`, version, commit, runtime.Version(), date, tags) +} diff --git a/payments/payments.go b/payments/payments.go new file mode 100644 index 00000000..017ee1ed --- /dev/null +++ b/payments/payments.go @@ -0,0 +1,16 @@ +//go:build !nopayments + +package payments + +import "github.com/stripe/stripe-go/v74" + +const Available = true + +type SubscriptionStatus stripe.SubscriptionStatus + +type PriceRecurringInterval stripe.PriceRecurringInterval + +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..bffa53f5 --- /dev/null +++ b/payments/payments_dummy.go @@ -0,0 +1,13 @@ +//go:build nopayments + +package payments + +const Available = false + +type SubscriptionStatus string + +type PriceRecurringInterval string + +func Setup(stripeSecretKey string) { + // Nothing to see here +} diff --git a/server/server.go b/server/server.go index 43d6064a..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 hasStripe && conf.StripeSecretKey != "" { + if payments.Available && conf.StripeSecretKey != "" { stripe = newStripeAPI() } messageCache, err := createMessageCache(conf) diff --git a/server/server_payments.go b/server/server_payments.go index 3c4b1fc6..0226df4f 100644 --- a/server/server_payments.go +++ b/server/server_payments.go @@ -14,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" @@ -44,8 +45,6 @@ import ( // This is used to keep the local user database fields up to date. Stripe is the source of truth. // What Stripe says is mirrored and not questioned. -const hasStripe = true - var ( errNotAPaidTier = errors.New("tier does not have billing price identifier") errMultipleBillingSubscriptions = errors.New("cannot have multiple billing subscriptions") @@ -468,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 index 3915453c..dcdc31c6 100644 --- a/server/server_payments_dummy.go +++ b/server/server_payments_dummy.go @@ -2,7 +2,21 @@ package server -const hasStripe = false +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 @@ -31,11 +45,3 @@ func (s *Server) handleAccountBillingPortalSessionCreate(w http.ResponseWriter, func (s *Server) handleAccountBillingWebhook(_ http.ResponseWriter, r *http.Request, v *visitor) error { return errHTTPNotFound } - -func (s *Server) handleAccountBillingWebhookSubscriptionUpdated(r *http.Request, v *visitor, event stripe.Event) error { - return errHTTPNotFound -} - -func (s *Server) handleAccountBillingWebhookSubscriptionDeleted(r *http.Request, v *visitor, event stripe.Event) error { - return errHTTPNotFound -} diff --git a/stripe/types.go b/stripe/types.go deleted file mode 100644 index 0e0d17df..00000000 --- a/stripe/types.go +++ /dev/null @@ -1 +0,0 @@ -package stripe 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..5b0b3fa5 100644 --- a/user/manager_test.go +++ b/user/manager_test.go @@ -4,8 +4,8 @@ import ( "database/sql" "fmt" "github.com/stretchr/testify/require" - "github.com/stripe/stripe-go/v74" "golang.org/x/crypto/bcrypt" + "heckel.io/ntfy/v2/payments" "heckel.io/ntfy/v2/util" "net/netip" "path/filepath" @@ -164,8 +164,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: payments.SubscriptionStatusActive, + StripeSubscriptionInterval: payments.PriceRecurringIntervalMonth, 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 }