diff --git a/app/main.py b/app/main.py index 6e5ef7d..dec5919 100644 --- a/app/main.py +++ b/app/main.py @@ -1299,11 +1299,22 @@ async def opencollective_webhook(request: Request, token: str): # Membership provision: only on payments to the "Membership" tier. A # one-time donation (no tier, or any other tier) grants BACKER role on # OC but is NOT a membership — don't provision it. - tier_name = "" - order_tier = (data.get("tier") or data.get("order", {}) or {}) - if isinstance(order_tier, dict): - tier_name = (order_tier.get("name") or "").strip() - amount_cents = data.get("amount") or data.get("valueInCents") or 0 + # + # Tier detection: the webhook payload carries only `data.order.TierId` + # (numeric) — NO tier name. The order description reliably embeds the + # tier name in parens ("Financial contribution to Inference + # Cooperative (Credit pack)"), so we parse it from there. + order_desc = (data.get("order") or {}).get("description") or "" + m = re.search(r"\(([^)]+)\)\s*$", order_desc.strip()) + tier_name = m.group(1).strip() if m else "" + if not tier_name: + # Fallback: description without parens IS the tier name in some + # OC webhook shapes ("Financial contribution to X: TierName"). + m2 = re.search(r":\s*([^:]+)$", order_desc.strip()) + tier_name = m2.group(1).strip() if m2 else "" + amount_cents = data.get("amount") or 0 + if not amount_cents: + amount_cents = ((data.get("order") or {}).get("totalAmount")) or 0 # Credit-pack detection: one-time contributions to a "Credit pack" # tier route to the member's NON-EXPIRING credit balance instead of