Skip to content

Commit 1f66d40

Browse files
authored
Merge pull request #8771 from Couchers-org/backend/feature/donations-feature-flag
Backend: migrate ENABLE_DONATIONS to donations_enabled feature flag
2 parents 0bb6428 + d16102d commit 1f66d40

5 files changed

Lines changed: 18 additions & 14 deletions

File tree

app/backend.dev.env

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ DATABASE_CONNECTION_STRING=postgresql://postgres:203d805f4b62c0a1b2f1f6b82d4583d
1313

1414
ADD_DUMMY_DATA=1
1515

16-
ENABLE_DONATIONS=0
1716
STRIPE_API_KEY=sk_test_...
1817
STRIPE_WEBHOOK_SECRET=whsec_...
1918
STRIPE_RECURRING_PRODUCT_ID=price_...

app/backend/src/couchers/config.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@
4040
("GEOLITE2_ASN_MMDB_FILE_LOCATION", str, ""),
4141
# Whether to try adding dummy data
4242
("ADD_DUMMY_DATA", bool),
43-
# Donations
44-
("ENABLE_DONATIONS", bool),
43+
# Donations (gated at runtime by the `donations_enabled` feature flag)
4544
("STRIPE_API_KEY", str),
4645
("STRIPE_WEBHOOK_SECRET", str),
4746
("STRIPE_RECURRING_PRODUCT_ID", str),
@@ -154,10 +153,10 @@ def check_config(cfg: dict[str, Any]) -> None:
154153
raise Exception("Production site must have SMS enabled")
155154
if cfg["IN_TEST"]:
156155
raise Exception("IN_TEST while not DEV")
157-
158-
if cfg["ENABLE_DONATIONS"]:
156+
# Donations are gated at runtime by the `donations_enabled` feature flag, which can be flipped on
157+
# remotely at any time, so prod must always have Stripe credentials present so the feature can run.
159158
if not cfg["STRIPE_API_KEY"] or not cfg["STRIPE_WEBHOOK_SECRET"] or not cfg["STRIPE_RECURRING_PRODUCT_ID"]:
160-
raise Exception("No Stripe API key/recurring donation ID but donations enabled")
159+
raise Exception("Stripe credentials must be configured in production")
161160

162161
if cfg["ENABLE_STRONG_VERIFICATION"]:
163162
if not cfg["IRIS_ID_PUBKEY"] or not cfg["IRIS_ID_SECRET"] or not cfg["VERIFICATION_DATA_PUBLIC_KEY"]:

app/backend/src/couchers/servicers/donations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Donations(donations_pb2_grpc.DonationsServicer):
4141
def InitiateDonation(
4242
self, request: donations_pb2.InitiateDonationReq, context: CouchersContext, session: Session
4343
) -> donations_pb2.InitiateDonationRes:
44-
if not config["ENABLE_DONATIONS"]:
44+
if not context.get_boolean_value("donations_enabled", default=False):
4545
context.abort_with_error_code(grpc.StatusCode.UNAVAILABLE, "donations_disabled")
4646

4747
user = session.execute(select(User).where(User.id == context.user_id)).scalar_one()
@@ -108,7 +108,7 @@ def InitiateDonation(
108108
def GetDonationPortalLink(
109109
self, request: empty_pb2.Empty, context: CouchersContext, session: Session
110110
) -> donations_pb2.GetDonationPortalLinkRes:
111-
if not config["ENABLE_DONATIONS"]:
111+
if not context.get_boolean_value("donations_enabled", default=False):
112112
context.abort_with_error_code(grpc.StatusCode.UNAVAILABLE, "donations_disabled")
113113

114114
user = session.execute(select(User).where(User.id == context.user_id)).scalar_one()

app/backend/src/tests/conftest.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ def testconfig():
176176
config["MODS_EMAIL_RECIPIENT"] = "mods@couchers.org.invalid"
177177
config["ENABLE_EMAIL_ICS_ATTACHMENTS"] = True
178178

179-
config["ENABLE_DONATIONS"] = False
180179
config["STRIPE_API_KEY"] = ""
181180
config["STRIPE_WEBHOOK_SECRET"] = ""
182181
config["STRIPE_RECURRING_PRODUCT_ID"] = ""

app/backend/src/tests/test_donations.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
from unittest.mock import patch
33

4+
import grpc
45
import pytest
56
from google.protobuf import empty_pb2
67
from sqlalchemy import select
@@ -21,13 +22,23 @@ def _(testconfig):
2122
pass
2223

2324

25+
def test_donations_disabled(db, feature_flags):
26+
feature_flags.set("donations_enabled", False)
27+
_, token = generate_user()
28+
29+
with donations_session(token) as donations:
30+
with pytest.raises(grpc.RpcError) as e:
31+
donations.InitiateDonation(donations_pb2.InitiateDonationReq(amount=100))
32+
assert e.value.code() == grpc.StatusCode.UNAVAILABLE
33+
assert e.value.details() == "Donations are currently disabled."
34+
35+
2436
def test_one_time_donation_flow(db, monkeypatch):
2537
user, token = generate_user()
2638
user_email = user.email
2739
user_id = user.id
2840

2941
new_config = config.copy()
30-
new_config["ENABLE_DONATIONS"] = True
3142
new_config["STRIPE_API_KEY"] = "dummy_api_key"
3243
new_config["STRIPE_WEBHOOK_SECRET"] = "dummy_webhook_secret"
3344
new_config["STRIPE_RECURRING_PRODUCT_ID"] = "price_1KIbmbIfR5z29g5kFWPEUnC6"
@@ -132,7 +143,6 @@ def test_recurring_donation_flow(db, monkeypatch):
132143
user_id = user.id
133144

134145
new_config = config.copy()
135-
new_config["ENABLE_DONATIONS"] = True
136146
new_config["STRIPE_API_KEY"] = "dummy_api_key"
137147
new_config["STRIPE_WEBHOOK_SECRET"] = "dummy_webhook_secret"
138148
new_config["STRIPE_RECURRING_PRODUCT_ID"] = "price_1IRoHdE5kUmYuPWz9tX8UpRv"
@@ -257,7 +267,6 @@ def test_customer_portal_url(db, monkeypatch):
257267
user_id = user.id
258268

259269
new_config = config.copy()
260-
new_config["ENABLE_DONATIONS"] = True
261270
new_config["STRIPE_API_KEY"] = "dummy_api_key"
262271

263272
monkeypatch.setattr(couchers.servicers.donations, "config", new_config)
@@ -376,7 +385,6 @@ def test_slack_notification_on_one_time_donation(db, monkeypatch):
376385
user, token = generate_user()
377386

378387
new_config = config.copy()
379-
new_config["ENABLE_DONATIONS"] = True
380388
new_config["STRIPE_API_KEY"] = "dummy_api_key"
381389
new_config["STRIPE_WEBHOOK_SECRET"] = "dummy_webhook_secret"
382390
new_config["STRIPE_RECURRING_PRODUCT_ID"] = "price_1KIbmbIfR5z29g5kFWPEUnC6"
@@ -407,7 +415,6 @@ def test_slack_notification_on_recurring_donation(db, monkeypatch):
407415
user, token = generate_user()
408416

409417
new_config = config.copy()
410-
new_config["ENABLE_DONATIONS"] = True
411418
new_config["STRIPE_API_KEY"] = "dummy_api_key"
412419
new_config["STRIPE_WEBHOOK_SECRET"] = "dummy_webhook_secret"
413420
new_config["STRIPE_RECURRING_PRODUCT_ID"] = "price_1IRoHdE5kUmYuPWz9tX8UpRv"

0 commit comments

Comments
 (0)