Przeglądaj źródła

Add `billing-contact` config option

binwiederhier 3 lat temu
rodzic
commit
fe3a225f8f

+ 3 - 0
cmd/serve.go

@@ -84,6 +84,7 @@ var flagsServe = append(
 	altsrc.NewBoolFlag(&cli.BoolFlag{Name: "behind-proxy", Aliases: []string{"behind_proxy", "P"}, EnvVars: []string{"NTFY_BEHIND_PROXY"}, Value: false, Usage: "if set, use X-Forwarded-For header to determine visitor IP address (for rate limiting)"}),
 	altsrc.NewStringFlag(&cli.StringFlag{Name: "stripe-secret-key", Aliases: []string{"stripe_secret_key"}, EnvVars: []string{"NTFY_STRIPE_SECRET_KEY"}, Value: "", Usage: "key used for the Stripe API communication, this enables payments"}),
 	altsrc.NewStringFlag(&cli.StringFlag{Name: "stripe-webhook-key", Aliases: []string{"stripe_webhook_key"}, EnvVars: []string{"NTFY_STRIPE_WEBHOOK_KEY"}, Value: "", Usage: "key required to validate the authenticity of incoming webhooks from Stripe"}),
+	altsrc.NewStringFlag(&cli.StringFlag{Name: "billing-contact", Aliases: []string{"billing_contact"}, EnvVars: []string{"NTFY_BILLING_CONTACT"}, Value: "", Usage: "e-mail or website to display in upgrade dialog (only if payments are enabled)"}),
 )
 
 var cmdServe = &cli.Command{
@@ -159,6 +160,7 @@ func execServe(c *cli.Context) error {
 	behindProxy := c.Bool("behind-proxy")
 	stripeSecretKey := c.String("stripe-secret-key")
 	stripeWebhookKey := c.String("stripe-webhook-key")
+	billingContact := c.String("billing-contact")
 
 	// Check values
 	if firebaseKeyFile != "" && !util.FileExists(firebaseKeyFile) {
@@ -305,6 +307,7 @@ func execServe(c *cli.Context) error {
 	conf.BehindProxy = behindProxy
 	conf.StripeSecretKey = stripeSecretKey
 	conf.StripeWebhookKey = stripeWebhookKey
+	conf.BillingContact = billingContact
 	conf.EnableWeb = enableWeb
 	conf.EnableSignup = enableSignup
 	conf.EnableLogin = enableLogin

+ 5 - 0
docs/config.md

@@ -839,6 +839,8 @@ config options:
    enables payments in the ntfy web app (e.g. Upgrade dialog). See [API keys](https://dashboard.stripe.com/apikeys).
 * `stripe-webhook-key` is the key required to validate the authenticity of incoming webhooks from Stripe.
    Webhooks are essential to keep the local database in sync with the payment provider. See [Webhooks](https://dashboard.stripe.com/webhooks).
+* `billing-contact` is an email address or website displayed in the "Upgrade tier" dialog to let people reach
+   out with billing questions. If unset, nothing will be displayed.
 
 In addition to setting these two options, you also need to define a [Stripe webhook](https://dashboard.stripe.com/webhooks)
 for the `customer.subscription.updated` and `customer.subscription.deleted` event, which points 
@@ -849,6 +851,7 @@ Here's an example:
 ``` yaml
 stripe-secret-key: "sk_test_ZmhzZGtmbGhkc2tqZmhzYcO2a2hmbGtnaHNkbGtnaGRsc2hnbG"
 stripe-webhook-key: "whsec_ZnNkZnNIRExBSFNES0hBRFNmaHNka2ZsaGR"
+billing-contact: "phil@example.com"
 ```
 
 ## Rate limiting
@@ -1194,6 +1197,7 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`).
 | `enable-reservations`                      | `NTFY_ENABLE_RESERVATIONS`                      | *boolean* (`true` or `false`)                       | `false`           | Allows users to reserve topics (if their tier allows it)                                                                                                                                                                        |
 | `stripe-secret-key`                        | `NTFY_STRIPE_SECRET_KEY`                        | *string*                                            | -                 | Payments: Key used for the Stripe API communication, this enables payments                                                                                                                                                      |
 | `stripe-webhook-key`                       | `NTFY_STRIPE_WEBHOOK_KEY`                       | *string*                                            | -                 | Payments: Key required to validate the authenticity of incoming webhooks from Stripe                                                                                                                                            |
+| `billing-contact`                          | `NTFY_BILLING_CONTACT`                          | *email address* or *website*                        | -                 | Payments: Email or website displayed in Upgrade dialog as a billing contact                                                                                                                                                     |
 
 The format for a *duration* is: `<number>(smh)`, e.g. 30s, 20m or 1h.   
 The format for a *size* is: `<number>(GMK)`, e.g. 1G, 200M or 4000k.
@@ -1277,6 +1281,7 @@ OPTIONS:
    --behind-proxy, --behind_proxy, -P                                                                                     if set, use X-Forwarded-For header to determine visitor IP address (for rate limiting) (default: false) [$NTFY_BEHIND_PROXY]
    --stripe-secret-key value, --stripe_secret_key value                                                                   key used for the Stripe API communication, this enables payments [$NTFY_STRIPE_SECRET_KEY]
    --stripe-webhook-key value, --stripe_webhook_key value                                                                 key required to validate the authenticity of incoming webhooks from Stripe [$NTFY_STRIPE_WEBHOOK_KEY]
+   --billing-contact value, --billing_contact value                                                                       e-mail or website to display in upgrade dialog (only if payments are enabled) [$NTFY_BILLING_CONTACT]   
    --help, -h                                                                                                             show help (default: false)
 ```
 

+ 1 - 0
docs/releases.md

@@ -21,6 +21,7 @@ are no closed-source features. So if you'd like to run your own server, you can!
 * Upgrade dialog: Disable submit button for free tier (no ticket)
 * Allow multiple `log-level-overrides` on the same field (no ticket)
 * Actually remove `ntfy publish --env-topic` flag (as per [deprecations](deprecations.md), no ticket)
+* Added `billing-contact` config option (no ticket)
 
 ## ntfy server v2.1.0
 Released February 25, 2023

+ 1 - 0
server/config.go

@@ -128,6 +128,7 @@ type Config struct {
 	StripeSecretKey                      string
 	StripeWebhookKey                     string
 	StripePriceCacheDuration             time.Duration
+	BillingContact                       string
 	EnableWeb                            bool
 	EnableSignup                         bool // Enable creation of accounts via API and UI
 	EnableLogin                          bool

+ 1 - 0
server/server.go

@@ -485,6 +485,7 @@ func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visi
 		EnableSignup:       s.config.EnableSignup,
 		EnablePayments:     s.config.StripeSecretKey != "",
 		EnableReservations: s.config.EnableReservations,
+		BillingContact:     s.config.BillingContact,
 		DisallowedTopics:   s.config.DisallowedTopics,
 	}
 	b, err := json.MarshalIndent(response, "", "  ")

+ 3 - 0
server/server.yml

@@ -240,9 +240,12 @@
 #   enables payments in the ntfy web app (e.g. Upgrade dialog). See https://dashboard.stripe.com/apikeys.
 # - stripe-webhook-key is the key required to validate the authenticity of incoming webhooks from Stripe.
 #   Webhooks are essential up keep the local database in sync with the payment provider. See https://dashboard.stripe.com/webhooks.
+# - billing-contact is an email address or website displayed in the "Upgrade tier" dialog to let people reach
+#   out with billing questions. If unset, nothing will be displayed.
 #
 # stripe-secret-key:
 # stripe-webhook-key:
+# billing-contact:
 
 # Logging options
 #

+ 1 - 0
server/types.go

@@ -341,6 +341,7 @@ type apiConfigResponse struct {
 	EnableSignup       bool     `json:"enable_signup"`
 	EnablePayments     bool     `json:"enable_payments"`
 	EnableReservations bool     `json:"enable_reservations"`
+	BillingContact     string   `json:"billing_contact"`
 	DisallowedTopics   []string `json:"disallowed_topics"`
 }
 

+ 2 - 1
web/public/config.js

@@ -6,11 +6,12 @@
 // During web development, you may change values here for rapid testing.
 
 var config = {
-    base_url: "https://127.0.0.1", // to test against a different server
+    base_url: window.location.origin, // Change to test against a different server
     app_root: "/app",
     enable_login: true,
     enable_signup: true,
     enable_payments: true,
     enable_reservations: true,
+    billing_contact: "",
     disallowed_topics: ["docs", "static", "file", "app", "account", "settings", "signup", "login", "v1"]
 };

+ 2 - 0
web/public/static/langs/en.json

@@ -236,6 +236,8 @@
   "account_upgrade_dialog_tier_price_billed_yearly": "{{price}} billed annually. Save {{save}}.",
   "account_upgrade_dialog_tier_selected_label": "Selected",
   "account_upgrade_dialog_tier_current_label": "Current",
+  "account_upgrade_dialog_billing_contact_email": "For billing questions, please <Link>contact us</Link> directly.",
+  "account_upgrade_dialog_billing_contact_website": "For billing questions, please refer to our <Link>website</Link>.",
   "account_upgrade_dialog_button_cancel": "Cancel",
   "account_upgrade_dialog_button_redirect_signup": "Sign up now",
   "account_upgrade_dialog_button_pay_now": "Pay now and subscribe",

+ 32 - 6
web/src/components/UpgradeDialog.js

@@ -3,9 +3,8 @@ import {useContext, useEffect, useState} from 'react';
 import Dialog from '@mui/material/Dialog';
 import DialogContent from '@mui/material/DialogContent';
 import DialogTitle from '@mui/material/DialogTitle';
-import {Alert, Badge, CardActionArea, CardContent, Chip, ListItem, Stack, Switch, useMediaQuery} from "@mui/material";
+import {Alert, CardActionArea, CardContent, Chip, Link, ListItem, Switch, useMediaQuery} from "@mui/material";
 import theme from "./theme";
-import DialogFooter from "./DialogFooter";
 import Button from "@mui/material/Button";
 import accountApi, {SubscriptionInterval} from "../app/AccountApi";
 import session from "../app/Session";
@@ -22,6 +21,8 @@ import ListItemText from "@mui/material/ListItemText";
 import Box from "@mui/material/Box";
 import {NavLink} from "react-router-dom";
 import {UnauthorizedError} from "../app/errors";
+import DialogContentText from "@mui/material/DialogContentText";
+import DialogActions from "@mui/material/DialogActions";
 
 const UpgradeDialog = (props) => {
     const { t } = useTranslation();
@@ -204,10 +205,35 @@ const UpgradeDialog = (props) => {
                     </Alert>
                 }
             </DialogContent>
-            <DialogFooter status={error}>
-                <Button onClick={props.onCancel}>{t("account_upgrade_dialog_button_cancel")}</Button>
-                <Button onClick={handleSubmit} disabled={!submitAction}>{submitButtonLabel}</Button>
-            </DialogFooter>
+            <Box sx={{
+                display: 'flex',
+                flexDirection: 'row',
+                justifyContent: 'space-between',
+                paddingLeft: '24px',
+                paddingBottom: '8px',
+            }}>
+                <DialogContentText
+                    component="div"
+                    aria-live="polite"
+                    sx={{
+                        margin: '0px',
+                        paddingTop: '12px',
+                        paddingBottom: '4px'
+                    }}
+                >
+                    {config.billing_contact.indexOf('@') !== -1 &&
+                        <><Trans i18nKey="account_upgrade_dialog_billing_contact_email" components={{ Link: <Link href={`mailto:${config.billing_contact}`}/> }}/>{" "}</>
+                    }
+                    {config.billing_contact.match(`^http?s://`) &&
+                        <><Trans i18nKey="account_upgrade_dialog_billing_contact_website" components={{ Link: <Link href={config.billing_contact} target="_blank"/> }}/>{" "}</>
+                    }
+                    {error}
+                </DialogContentText>
+                <DialogActions sx={{paddingRight: 2}}>
+                    <Button onClick={props.onCancel}>{t("account_upgrade_dialog_button_cancel")}</Button>
+                    <Button onClick={handleSubmit} disabled={!submitAction}>{submitButtonLabel}</Button>
+                </DialogActions>
+            </Box>
         </Dialog>
     );
 };