From fa1568b0bdf773fc7c78a929790afeb9ca8ec478 Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Fri, 23 Oct 2026 14:25:25 -0400 Subject: [PATCH] Memberships --- membership/members/forms.py | 2 +- membership/members/views.py | 8 +++--- membership/templates/members.html | 33 +++++++++++++++-------- membership/tiers.eom.json | 45 ++++++++++++++++++++++++------- 4 files changed, 62 insertions(+), 26 deletions(-) diff --git a/membership/members/forms.py b/membership/members/forms.py index 8cbb023..0e2f2bf 100755 --- a/membership/members/forms.py +++ b/membership/members/forms.py @@ -11,7 +11,7 @@ class RegistrationForm(forms.Form): class PasswordForm(forms.Form): - password = forms.CharField(max_length=1024, widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), label="Password", help_text="At least 12 characters. Stored only in OpenLDAP, never on this website.") + password = forms.CharField(max_length=1024, widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), label="Password", help_text="Use at least 12 characters. Choose a strong, unique password for your eom.dev account.") confirm_password = forms.CharField(max_length=1024, widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), label="Confirm password") def clean_password(self): diff --git a/membership/members/views.py b/membership/members/views.py index 95796c2..c76e893 100755 --- a/membership/members/views.py +++ b/membership/members/views.py @@ -91,7 +91,7 @@ def register(request): Registration.objects.filter(pk=digest).delete() logger.error("Registration email failed kind=%s", type(exc).__name__) return page(request, title="Email unavailable", message="Account email could not be sent. Please try again later.") - return page(request, title="Check your inbox", message="Use the verification link to choose a password and create your free account. If you already have an account, sign in through Dex instead.") + return page(request, title="Check your inbox", message="Follow the confirmation link in your email to choose a password and finish creating your free eom.dev account. If you already have an account, sign in instead.") return page(request, title="Create your free account", form=form, submit="Send verification email", registration=True) @@ -129,12 +129,12 @@ def finish_registration(request): remember_groups(account) Registration.objects.filter(uid=registration.uid).delete() request.session.pop("registration_hash", None) - return page(request, title="Account created", message="Your free account is now in OpenLDAP. Sign in through Dex to manage membership.") + return page(request, title="Account created", message="Your free eom.dev account is ready. Sign in to explore the community and its services, or choose an optional paid membership for additional benefits.") except ValueError: - form.add_error(None, "An account with those details may already exist. Sign in through Dex or contact the administrator.") + form.add_error(None, "An account with those details may already exist. Try signing in or contact us for help.") except Exception as exc: logger.error("Account provisioning failed kind=%s", type(exc).__name__) - form.add_error(None, "Account creation could not finish. Try Dex sign-in first, or contact the administrator before retrying.") + form.add_error(None, "Account creation could not finish. Try signing in first, or contact us for help before retrying.") return page(request, title="Choose your password", form=form, submit="Create free account") diff --git a/membership/templates/members.html b/membership/templates/members.html index b646a5a..de2a9f5 100755 --- a/membership/templates/members.html +++ b/membership/templates/members.html @@ -20,7 +20,15 @@

One account. An independent community.

{{ title }}

{% if message %}

{{ message }}

{% endif %} - {% if registration %}

Everyone can join. Verify your email, set a password in OpenLDAP, then sign in with Dex. A paid membership is optional.

{% endif %} + {% if registration %} +

One account for all of eom.dev. Use the same sign-in to explore the community and its services, without creating a separate account for each one.

+
    +
  1. Choose a username and enter your email address below.
  2. +
  3. Check your inbox and follow the confirmation link.
  4. +
  5. Choose a password, then sign in and start exploring.
  6. +
+

Creating an account is free. Paid memberships are optional and unlock additional benefits.

+ {% endif %} {% if form %}
{% csrf_token %} @@ -29,16 +37,16 @@
{% endif %}

Back to membership

-
{% csrf_token %}
+
{% csrf_token %}
{% else %}

Keep your own corner of the internet alive

A place to belong.
A way to give back.

-

Start with a free eom.dev account. Choose an optional membership to support the servers, experiments, and independent work behind the community.

+

A private place to connect, a closer look at what is being made, and opportunities to help shape it. Start with a free eom.dev account, then choose an optional membership to support the independent work.

{% if account %}
-

Your support, your choice

Find your membership

Your account stays free. Paid group access lasts only for the service period covered by your subscription.

+

Your support, your choice

Find your membership

Public projects and ordinary community participation stay free. Membership adds a closer connection to the work, not a barrier to joining in.

The starting point

Community

Free / always

-

An account for the eom.dev community, stored in OpenLDAP and signed into through Dex.

+

One account for eom.dev, its community, and its services.

  • No payment required
  • One account for compatible community services
  • Upgrade whenever you choose
{% if not account %}Join the community{% else %}

Your free account is ready.

{% endif %}
@@ -72,17 +80,20 @@

Independent work, supported together

{{ tier.label }}

{{ tier.price_label }}

{{ tier.description|default:"Help keep the projects and community running." }}

-
  • Tier-specific community access
  • Recurring payments through Stripe{% if shkeeper_enabled and tier.usd_amount %}, or prepaid crypto through SHKeeper{% endif %}
  • Your free account stays when paid access ends
+
    {% for benefit in tier.benefits %}
  • {{ benefit }}
  • {% empty %}
  • Support independent projects and the community
  • {% endfor %}
{% if account %}
{% csrf_token %}
{% if shkeeper_enabled and tier.usd_amount %}Pay with crypto{% endif %}
{% else %}Create an account to join{% endif %}
{% empty %}

Paid memberships are coming soon.

You can create your free account now. Paid tiers will appear when their prices and groups are configured.

{% endfor %}
+

One shared members' space, not a separate community at every price. Sustainer and Patron include the same access as Collaborator; choose them if you would like to contribute more.

+

Previews are shared when available, and playtests, listening sessions and feedback gatherings are occasional. Membership does not include storage quotas, personal consulting, guaranteed support or promised feature development. Recognition is always optional.

Simple, by design.

-
What happens when a membership ends?

Your LDAP account stays. Paid group access is removed automatically when the paid service period expires. An immediate cancellation removes access on the next successful sync.

-
Where is my password stored?

Only OpenLDAP stores your password hash. This website sends your initial password over verified TLS to OpenLDAP's password-modify operation. Future sign-ins happen through Dex.

+
What happens when a membership ends?

Your free eom.dev account stays. Member-only access ends when your paid membership expires. An immediate cancellation removes that access once your membership status updates.

+
Where do members connect?

The shared members' Matrix space is the home for informal conversations and work in progress. Private forum discussions can complement it as they become available, without splitting everyone into separate rooms for each tier.

+
What can I preview or take part in?

Depending on what is being made, Insider members and above may preview music, game builds, videos or new experiments and join playtests or listening sessions. Collaborator members and above can also take part in project-topic polls and occasional small-group feedback sessions. Polls help inform the work; they are not promises to build requested features.

Can I change or cancel a plan?

Use Manage billing after signing in. The billing portal offers the changes enabled for your plan. Cancellation at period end keeps access through the paid expiry date.

{% if shkeeper_enabled %}
How do crypto memberships renew?

SHKeeper payments prepay one calendar month, without automatic charges. Create a new invoice for each renewal. Confirmed payment on the same tier extends its prepaid term. Partial or unconfirmed payments do not grant access. Do not send renewal funds to an old invoice address.

{% endif %}
diff --git a/membership/tiers.eom.json b/membership/tiers.eom.json index a71bf69..6324043 100755 --- a/membership/tiers.eom.json +++ b/membership/tiers.eom.json @@ -1,42 +1,67 @@ { "level-0": { - "label": "Level 0", + "label": "Supporter", "price_id": "price_1Rgv4xC5KPXqTvVLfMd6z7wn", "price_label": "$4 USD / month", "usd_amount": "4.00", "group_dn": "cn=www-level-0,ou=groups,dc=eom,dc=dev", - "description": "Support the independent work with a Level 0 membership." + "description": "A private place to connect and a closer look at what is being made.", + "benefits": [ + "Access to the shared members' Matrix space", + "Behind-the-scenes notes and work-in-progress updates", + "Optional recognition on the supporters page" + ] }, "level-1": { - "label": "Level 1", + "label": "Insider", "price_id": "price_1Rgv5QC5KPXqTvVLv1PaHWb5", "price_label": "$8 USD / month", "usd_amount": "8.00", "group_dn": "cn=www-level-1,ou=groups,dc=eom,dc=dev", - "description": "Support the independent work with a Level 1 membership." + "description": "See new experiments take shape, and try things before their public release.", + "benefits": [ + "Everything in Supporter", + "Early previews of music, games, videos and experiments when available", + "Invitations to member playtests and listening sessions" + ] }, "level-2": { - "label": "Level 2", + "label": "Collaborator", "price_id": "price_1Rgv5rC5KPXqTvVL8Ol8sMUf", "price_label": "$16 USD / month", "usd_amount": "16.00", "group_dn": "cn=www-level-2,ou=groups,dc=eom,dc=dev", - "description": "Support the independent work with a Level 2 membership." + "description": "Share your perspective and help shape the next community experiment.", + "benefits": [ + "Everything in Insider", + "Member polls on future project topics and experiments", + "Invitations to occasional small-group feedback sessions" + ] }, "level-3": { - "label": "Level 3", + "label": "Sustainer", "price_id": "price_1Rgv6FC5KPXqTvVL39mAxYjm", "price_label": "$32 USD / month", "usd_amount": "32.00", "group_dn": "cn=www-level-3,ou=groups,dc=eom,dc=dev", - "description": "Support the independent work with a Level 3 membership." + "description": "The same community access, with a larger contribution to keeping it independent.", + "benefits": [ + "Everything in Collaborator, with the same access", + "A larger contribution toward servers and public projects", + "Choose this level if you would like to give more" + ] }, "level-4": { - "label": "Level 4", + "label": "Patron", "price_id": "price_1Rgv6mC5KPXqTvVLkcmznWZa", "price_label": "$64 USD / month", "usd_amount": "64.00", "group_dn": "cn=www-level-4,ou=groups,dc=eom,dc=dev", - "description": "Support the independent work with a Level 4 membership." + "description": "Help underwrite the public work and the community around it.", + "benefits": [ + "Everything in Collaborator, with the same access", + "Optional patron credit on the supporters page", + "Your highest level of support for independent public projects" + ] } }