Thanks again for your help @Bryan-Square. Looking at other posts, it seems that the <redirect_uri> parameter was changed to require an exact match within the past year. This seems like a silly choice for Square to have made. That parameter has a distinct purpose - for those who need to redirect their users dynamically (e.g. many websites using the same Square integration).
If Square’s developers determined that this parameter created a security vulnerability, they should have just removed it from existence. Keeping it in the docs but not mentioning that it provides no value is confusing.
Another poster (New OAuth redirect_url requirement for static URI) has solved their “multi-domain multi-tenancy” application problem by sticking their tenant information into the CSRF token, redirecting to one canonical service URL, then redirecting again from their service URL to their tenant based on the information that was shoved into the CSRF token. I suppose I’ll go that same route, but it’d be good to understand the rationale behind Square’s choice in this matter.