Skip to content

Commit b5a2580

Browse files
committed
feat: add refund policy and acceptable use policy pages
Two new legal templates for the paid launch, served at /refund and /acceptable-use with the same alias set as the existing legal pages. The new paths are reserved so no short code can shadow them. Effective dates are placeholders until the launch date is set.
1 parent c71e5d6 commit b5a2580

6 files changed

Lines changed: 260 additions & 0 deletions

File tree

routes/static_routes.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,33 @@ async def terms_of_service(request: Request) -> Response:
116116
)
117117

118118

119+
@router.get("/docs/refund-policy")
120+
@router.get("/legal/refund-policy")
121+
@router.get("/refund-policy")
122+
@router.get("/refund")
123+
@limiter.exempt
124+
async def refund_policy(request: Request) -> Response:
125+
return templates.TemplateResponse(
126+
request,
127+
"legal/refund-policy.html",
128+
{"host_url": str(request.base_url)},
129+
)
130+
131+
132+
@router.get("/docs/acceptable-use-policy")
133+
@router.get("/legal/acceptable-use-policy")
134+
@router.get("/acceptable-use-policy")
135+
@router.get("/acceptable-use")
136+
@router.get("/aup")
137+
@limiter.exempt
138+
async def acceptable_use_policy(request: Request) -> Response:
139+
return templates.TemplateResponse(
140+
request,
141+
"legal/acceptable-use-policy.html",
142+
{"host_url": str(request.base_url)},
143+
)
144+
145+
119146
@router.get("/docs/{path:path}")
120147
@limiter.exempt
121148
async def docs_wildcard(path: str, request: Request) -> Response:

shared/reserved_aliases.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
RESERVED_ALIASES: frozenset[str] = frozenset(
1818
{
1919
"about",
20+
"acceptable-use",
21+
"acceptable-use-policy",
2022
"api",
2123
"apps",
24+
"aup",
2225
"auth",
2326
"billing",
2427
"blog",
@@ -59,6 +62,8 @@
5962
"profile",
6063
"profile-pictures",
6164
"public",
65+
"refund",
66+
"refund-policy",
6267
"register",
6368
"restore-account",
6469
"relay",
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{% extends "legal/base.html" %}
2+
3+
{% block title %}Acceptable Use Policy - spoo.me{% endblock %}
4+
5+
{% block content %}
6+
<span><img alt="Shield emoji" class="emoji"
7+
src="https://raw.githubusercontent.com/microsoft/fluentui-emoji/main/assets/Shield/Flat/shield_flat.svg"></span>
8+
<h1>Acceptable Use Policy</h1>
9+
<br>
10+
11+
<p>Effective 2026-09-XX.</p>
12+
13+
<p>This policy says what you may not do with spoo.me. It applies to every link, custom domain, API key, connected app, and account, free or paid. It is part of our <a href="/terms-of-service">Terms of Service</a>. If you break it, we may block your links, slow your account down, or close it.</p>
14+
15+
<br>
16+
17+
<h2 id="links-you-may-not-create">Links you may not create</h2>
18+
19+
<p>Do not shorten, share, or promote links that:</p>
20+
21+
<ul>
22+
<li>lead to phishing, malware, or pages built to deceive, such as fake login pages, fake stores, or fake virus warnings</li>
23+
<li>are used for spam, unsolicited bulk messaging, or other commercial abuse</li>
24+
<li>point to illegal content, or break the laws that apply to you or to the people you send there</li>
25+
<li>infringe copyright, trademarks, or other rights of others</li>
26+
<li>harass, threaten, or defame a person</li>
27+
<li>exist to get past filters such as Google Safe Browsing, Spamhaus, PhishTank, or similar, including by creating fresh links faster than they can be flagged</li>
28+
</ul>
29+
30+
<br>
31+
32+
<h2 id="every-destination-is-checked">Every destination is checked</h2>
33+
34+
<p>We screen every destination when a link is created and again over its lifetime. That includes the destination for each country when you set geo rules, every variant of a link, links behind a password, and links on custom domains.</p>
35+
36+
<p>Screening uses our own rules plus outside sources. We may block, remove, or report a link on our own judgement, even if no outside blocklist has flagged it yet. A blocked link shows a notice instead of redirecting.</p>
37+
38+
<br>
39+
40+
<h2 id="your-account">Your account</h2>
41+
42+
<ul>
43+
<li>Keep your login and API keys to yourself. Do not share them.</li>
44+
<li>Do not create extra accounts to get around a limit or a ban.</li>
45+
<li>Do not try to get into other accounts or into our systems.</li>
46+
<li>Do not scrape, overload, probe, or otherwise interfere with the service.</li>
47+
<li>Do not work around rate limits or security measures.</li>
48+
<li>Use the API for your own projects. Do not resell or redistribute API access.</li>
49+
</ul>
50+
51+
<br>
52+
53+
<h2 id="paid-plans">Paid plans</h2>
54+
55+
<p><strong>No reselling.</strong> Pro is for you and your own projects or business. Do not sell access to your account, offer spoo.me link creation as a paid service of your own, or sublicense the API. If you want to build a product on top of spoo.me, email us first.</p>
56+
57+
<p><strong>Unlimited links, in good faith.</strong> There is no cap on how many links you create for real use. Automated use that looks like abuse, such as link farms, creating and discarding links at machine speed, or using us as a load-testing target, may be throttled. Throttling slows down creating new links. It never stops your existing legitimate links from redirecting.</p>
58+
59+
<p><strong>Custom domains you own.</strong> You may only connect a domain you own or are authorised to manage, and we verify that through DNS. Do not connect a domain that impersonates a brand, a person, or a service you do not represent. If a domain stops being yours, links on it stop and we may remove it.</p>
60+
61+
<p><strong>Screening still applies.</strong> Paying for Pro does not exempt any destination, geo rule, or variant from the checks above.</p>
62+
63+
<br>
64+
65+
<h2 id="what-we-do-about-it">What we do about it</h2>
66+
67+
<p>Depending on how serious it is, we may warn you, throttle your account, block the links involved, or suspend or close the account. When people are being harmed, we act first and tell you afterwards. Where the law requires it or the harm is serious, we report to the relevant authorities and blocklists.</p>
68+
69+
<p>If you think we got it wrong, email <a href="mailto:support@spoo.me">support@spoo.me</a> with the link and we will look again.</p>
70+
71+
<br>
72+
73+
<h2 id="reporting-abuse">Reporting abuse</h2>
74+
75+
<p>If you find a spoo.me link that breaks this policy, report it at <a href="/report">spoo.me/report</a> or email <a href="mailto:support@spoo.me">support@spoo.me</a>. Reports are handled ahead of everything else.</p>
76+
77+
<br>
78+
79+
<h2 id="governing-law">Governing law</h2>
80+
81+
<p>This policy is part of our <a href="/terms-of-service">Terms of Service</a> and follows the same governing law, the laws of the Netherlands. If you live in the EU, nothing here affects your rights under the consumer protection laws of your country of residence.</p>
82+
83+
<br>
84+
85+
<h2 id="changes-to-this-policy">Changes to this policy</h2>
86+
87+
<p>We may update this policy. When we do, we change the effective date at the top, and we notify registered users of changes that matter.</p>
88+
89+
<br>
90+
91+
<h2 id="contact">Contact</h2>
92+
93+
<p>Questions about this policy go to <a href="mailto:support@spoo.me">support@spoo.me</a>.</p>
94+
95+
<p>Support Team, spoo.me</p>
96+
{% endblock %}

templates/legal/refund-policy.html

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
{% extends "legal/base.html" %}
2+
3+
{% block title %}Refund Policy - spoo.me{% endblock %}
4+
5+
{% block content %}
6+
<span><img alt="Receipt emoji" class="emoji"
7+
src="https://raw.githubusercontent.com/microsoft/fluentui-emoji/main/assets/Receipt/Flat/receipt_flat.svg"></span>
8+
<h1>Refund Policy</h1>
9+
<br>
10+
11+
<p>Effective 2026-09-XX.</p>
12+
13+
<p>This policy covers payments for spoo.me Pro. It is short on purpose. If anything is unclear, email <a href="mailto:support@spoo.me">support@spoo.me</a> and a person will answer.</p>
14+
15+
<br>
16+
17+
<h2 id="who-charges-your-card">Who charges your card</h2>
18+
19+
<p>Paddle is our Merchant of Record. Paddle is the seller on your receipt, collects the payment, and handles any sales tax or VAT owed where you live. Paddle also issues refunds on our behalf. The charge on your bank statement shows Paddle's name next to spoo.me.</p>
20+
21+
<p>Prices on our pricing page are before tax. Paddle adds any tax at checkout based on your location.</p>
22+
23+
<br>
24+
25+
<h2 id="what-pro-costs">What Pro costs</h2>
26+
27+
<ul>
28+
<li><strong>Pro monthly:</strong> $15 a month. It renews every month until you cancel.</li>
29+
<li><strong>Pro annual:</strong> $144, paid once for 12 months. It does not renew. We remind you before the year ends, and you buy another year if you want to keep Pro.</li>
30+
<li><strong>Founding price:</strong> early subscribers pay $9 a month or $90 a year. Everything on this page applies to them the same way.</li>
31+
</ul>
32+
33+
<br>
34+
35+
<h2 id="14-days-no-questions">14 days, no questions</h2>
36+
37+
<p>You can get a full refund within 14 days of your first payment, on both the monthly and the annual plan. You do not need to give a reason.</p>
38+
39+
<p>To ask for one, email <a href="mailto:support@spoo.me">support@spoo.me</a> from the address on your account with "Refund" in the subject. We ask Paddle to refund the charge, and Paddle sends the money back to the payment method you used. Banks usually show it within 5 to 10 business days.</p>
40+
41+
<p>Your plan goes back to Free as soon as the refund is issued. Nothing is deleted. Your links keep redirecting, your click data stays, and your account stays. Pro-only features turn off, as described under <a href="#going-back-to-free">Going back to Free</a>.</p>
42+
43+
<p>The 14 days count from your first payment. Monthly renewals are not refundable, but you can cancel before a renewal and you will not be charged again.</p>
44+
45+
<br>
46+
47+
<h2 id="after-14-days">After 14 days</h2>
48+
49+
<p><strong>Monthly.</strong> Cancel any time from Billing in your dashboard or by emailing us. Pro stays on until the end of the month you already paid for, then your account goes to Free. There are no further charges. We do not refund the remaining days of a month.</p>
50+
51+
<p><strong>Annual.</strong> The prepaid year is non-refundable after the first 14 days. Pro stays on until the 12 months end, plus a 14-day grace window so you have time to buy another year. Because the year does not renew, there is nothing to cancel and no surprise charge.</p>
52+
53+
<br>
54+
55+
<h2 id="switching-plans">Switching plans</h2>
56+
57+
<p>To move from monthly to annual, buy the annual plan. Your monthly subscription ends at the close of the period you already paid for, and the annual plan starts from its purchase date. We do not prorate.</p>
58+
59+
<br>
60+
61+
<h2 id="failed-payments">Failed payments</h2>
62+
63+
<p>If a monthly renewal fails, Paddle retries the card for a few days and emails you. If the payment still cannot be collected, the subscription ends and your account goes to Free under the same rules as below.</p>
64+
65+
<br>
66+
67+
<h2 id="going-back-to-free">Going back to Free</h2>
68+
69+
<p>Whether Pro ends through a refund, a cancellation, or a year running out, the same things happen:</p>
70+
71+
<ul>
72+
<li>Nothing is deleted. Your links, click data, QR codes, and settings stay in your account.</li>
73+
<li>Links on spoo.me keep redirecting.</li>
74+
<li>Pro-only features turn off. Free plan limits apply to anything new you create.</li>
75+
<li>Custom domains are a Pro feature. Links on your custom domains stop resolving after the grace window. We keep your domain settings and those links for 90 days. If you come back to Pro within that time, everything resolves again without any setup. After 90 days we may remove the domain configuration.</li>
76+
</ul>
77+
78+
<br>
79+
80+
<h2 id="your-rights">Your rights</h2>
81+
82+
<p>This policy is part of our <a href="/terms-of-service">Terms of Service</a> and follows the same governing law, the laws of the Netherlands. If you live in the EU, nothing here affects your rights under the consumer protection laws of your country of residence.</p>
83+
84+
<br>
85+
86+
<h2 id="changes-to-this-policy">Changes to this policy</h2>
87+
88+
<p>We may update this policy. When we do, we change the effective date at the top, and we email paying subscribers about changes that affect them.</p>
89+
90+
<br>
91+
92+
<h2 id="contact">Contact</h2>
93+
94+
<p>Questions about a charge or a refund go to <a href="mailto:support@spoo.me">support@spoo.me</a>. Put "Refund" in the subject and we will find your payment faster.</p>
95+
96+
<p>Support Team, spoo.me</p>
97+
{% endblock %}

tests/integration/test_static_routes.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,36 @@ def test_terms_of_service_alias():
181181
assert "text/html" in resp.headers["content-type"]
182182

183183

184+
def test_refund_policy_renders():
185+
app = _build_test_app()
186+
with TestClient(app) as c:
187+
resp = c.get("/refund")
188+
assert resp.status_code == 200
189+
assert "Paddle is our Merchant of Record" in resp.text
190+
191+
192+
def test_refund_policy_alias():
193+
app = _build_test_app()
194+
with TestClient(app) as c:
195+
resp = c.get("/legal/refund-policy")
196+
assert resp.status_code == 200
197+
198+
199+
def test_acceptable_use_policy_renders():
200+
app = _build_test_app()
201+
with TestClient(app) as c:
202+
resp = c.get("/acceptable-use")
203+
assert resp.status_code == 200
204+
assert "Every destination is checked" in resp.text
205+
206+
207+
def test_acceptable_use_policy_alias():
208+
app = _build_test_app()
209+
with TestClient(app) as c:
210+
resp = c.get("/aup")
211+
assert resp.status_code == 200
212+
213+
184214
# ── Contact ──────────────────────────────────────────────────────────────────
185215

186216

tests/smoke/test_routes_registered.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ def test_all_expected_paths_registered(smoke_app: FastAPI) -> None:
124124
"/privacy",
125125
"/tos",
126126
"/terms-of-service",
127+
"/refund",
128+
"/refund-policy",
129+
"/acceptable-use",
130+
"/acceptable-use-policy",
131+
"/aup",
127132
"/docs/{path:path}",
128133
"/contact",
129134
"/report",

0 commit comments

Comments
 (0)