I'm answering my own questions, but I figure I'd write this because I struggled quite a bit with this issue, and I'm also interested in hearing alternate answers.
The environment
We're using Stripe server-side for payments (one-time and subscriptions), and we're using Stripe webhooks to record payments and events in our DB. We have multiple servers behind a load balancer, and we use a canary/rolling deploy, so during deploy not all servers run the same code version.
The problem
When you want to upgrade the Stripe API version, there are often breaking model changes (e.g. new or renamed fields) that require code changes. And if you're not upgrading frequently, it can be a lot of changes! cough cough
There's a catch-22 during a rolling deployment:
- if you wait until all servers are deployed with the new code/library to update the Stripe version in the Stripe dashboard, "new" servers may throw errors during the rollout because they're receiving webhooks in the old JSON format.
- if you update the Stripe version before all servers are deployed, then it's the "old" servers that may throw errors because they're not compatible with the new JSON format.
- if you update the Stripe version while you're deploying... you get a mix of both.
Stripe allows you to setup multiple webhook URLs, each with its own API version. But all matching events are sent to all webhook URLS (this wasn't clear to me at all at first).
There's also a mechanism in newer Stripe Java libraries to implement your own "exception handler" when the event model doesn't match, but it requires writing code and keeping track of every model change since the last upgrade, which in our case was... too many :)
Also with newer Stripe libraries, you cannot override the Stripe version: it's now hardcoded (which makes sense).