SHKeeper
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"""Canonical Stripe reads, not event arrival order, determine entitlements."""
|
||||
"""Canonical provider reads, not callback arrival order, determine entitlements."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timezone as dt_timezone
|
||||
@@ -10,7 +10,8 @@ from django.db import transaction
|
||||
from django.utils import timezone
|
||||
|
||||
from .directory import reconcile_groups
|
||||
from .models import Account, ManagedGroup, Subscription, WebhookEvent
|
||||
from .models import Account, CryptoInvoice, ManagedGroup, Subscription, WebhookEvent
|
||||
from . import shkeeper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -93,6 +94,10 @@ def apply_groups(account):
|
||||
tier = settings.MEMBERSHIP_TIERS.get(sub.tier)
|
||||
if tier:
|
||||
desired.add(tier["group_dn"])
|
||||
for invoice in CryptoInvoice.objects.filter(account=account, status__in=["PAID", "OVERPAID"], access_start__lte=now, access_end__gt=now):
|
||||
tier = settings.MEMBERSHIP_TIERS.get(invoice.tier)
|
||||
if tier:
|
||||
desired.add(tier["group_dn"])
|
||||
managed = set(ManagedGroup.objects.filter(account=account).values_list("dn", flat=True))
|
||||
account.ldap_dn = reconcile_groups(account, desired, managed) if managed else account.ldap_dn
|
||||
account.last_ldap_sync = now
|
||||
@@ -101,10 +106,12 @@ def apply_groups(account):
|
||||
|
||||
|
||||
def sync_account(account_id, refresh_stripe=True):
|
||||
"""Serialize Stripe/LDAP writes per account across web and worker processes.
|
||||
"""Serialize Stripe/SHKeeper/LDAP writes across web and worker processes.
|
||||
|
||||
Local expiry runs even when Stripe is unreachable. LDAP failure is retried on
|
||||
the next pass, with persisted managed DNs allowing crash-safe revocation.
|
||||
The existing refresh_stripe=False option means a local-only sweep: neither
|
||||
provider is contacted, so expiry remains independent of remote availability.
|
||||
"""
|
||||
# Commit management intent independently before external side effects. If
|
||||
# LDAP succeeds but a later DB commit fails, the next sweep still knows
|
||||
@@ -125,9 +132,10 @@ def sync_account(account_id, refresh_stripe=True):
|
||||
except Exception as exc:
|
||||
billing_ok = False
|
||||
logger.error("Billing refresh failed account=%s kind=%s", account.pk, type(exc).__name__)
|
||||
crypto_ok = shkeeper.refresh_account(account)
|
||||
now = timezone.now()
|
||||
count = apply_groups(account)
|
||||
if billing_ok:
|
||||
WebhookEvent.objects.filter(account=account, processed_at__isnull=True).update(processed_at=now)
|
||||
logger.info("Membership reconciled account=%s groups=%s billing_ok=%s", account.pk, count, billing_ok)
|
||||
return billing_ok
|
||||
return billing_ok and crypto_ok
|
||||
|
||||
Reference in New Issue
Block a user