Tier-Based Secure Video Platform with Patreon Membership Verification
A creator was paying for premium video hosting and still losing content to link sharing. We built a PHP and Angular platform that checks a viewer's live Patreon membership tier at the moment of playback, then issues a short-lived signed Bunny.net token for that one video, on that one session. No membership, no token. No token, no video.

Project Overview
- Industry
- Creator economy · Membership video · OTT
- Problem
- Paid videos leaking through shared links; tiers managed by hand
- Services
- Patreon API integration, secure video delivery, custom player development, backend and dashboard engineering
- Tech Stack
- PHP, Angular, MySQL, Bunny.net Stream, Patreon API v2 (OAuth 2.0), HLS
- Integrations
- Patreon API v2 · Patreon webhooks · Bunny Stream token authentication
- Status
- Live in production
- Related reading
- Protecting Patreon videos with secure, tier-based access
At a Glance
- What it does: gates premium video behind live Patreon membership tiers instead of behind an unlisted link.
- How access is decided: the platform reads the viewer's currently entitled tiers from the Patreon API v2 at request time, not from a stored copy.
- How video is protected: Bunny Stream embed token authentication - a SHA256 token bound to one video ID and one expiry timestamp, generated server-side per play request.
- What the viewer sees: a custom player built for web, mobile and TV-style devices, with no shareable file URL behind it.
- What happens when a pledge lapses: Patreon webhooks revoke entitlement, and the next token request is refused. Existing tokens expire on their own.
- Stack: PHP (API and token service), Angular (creator dashboard and player shell), MySQL, Bunny.net Stream.
The Challenge
Patreon is very good at collecting money and very deliberately not in the business of hosting long-form video for you. So creators do the sensible thing: they host the files somewhere else and post the link. That link is the entire security model, and a link travels.
Our client had reached the size where that stops being a rounding error. Videos posted for a top tier were turning up in group chats and forums. Nothing about their setup could tell the difference between a paying member opening a video and a stranger opening the same URL an hour later, because from the server's point of view the two requests were identical.
Three problems sat underneath that:
- A link is not an identity. An unlisted or private URL authenticates nothing. Once it leaves the members' area it is simply a public URL that nobody has found yet.
- Tiers were a manual filing job. Deciding which video belonged to which tier - and re-checking it when tiers changed - was human work, and it drifted.
- Lapsed members kept their access. When a pledge stopped, nothing revoked anything. The old links kept working indefinitely.
The requirement in one sentence: access had to be decided at the moment of playback, against the viewer's live membership - not at the moment of publishing, against a list.
The Solution: How Access Is Decided
We built an independent verification layer that sits between the viewer and the video host. Patreon stays the source of truth for who is a member and at what tier; Bunny.net stays the delivery network; the platform in the middle decides, per request, whether those two facts should be introduced to each other.
Every play request follows the same six steps
- Sign in with Patreon. The viewer authenticates through Patreon OAuth 2.0. We request only the identity and identity.memberships scopes - enough to know who they are and what they are entitled to, and nothing more.
- Read the live membership. The platform calls the Patreon API v2 and reads the member's patron_status, last_charge_status and currently_entitled_tiers relationship.
- Resolve the tier to content. The requested video is looked up in the tier-mapping tables in MySQL. A video can belong to one tier, to several, or to a rule such as “this tier and above”.
- Decide. Entitlement is granted only when the member is an active patron, the last charge did not fail, and the entitled tier set intersects the video's allowed tiers.
- Mint a short-lived token. On a pass, the PHP token service generates a Bunny Stream embed token server-side and returns a playback URL scoped to that single video and a near-term expiry.
- Play, then expire. The custom player loads the tokenised stream. The token dies on schedule whether the viewer is still watching or not, and the next segment request needs a fresh one.
Why this is different from a private link: a private link is a permanent credential that anyone can copy. A signed token is a temporary credential bound to one video and one moment. Copying it buys you minutes, not access.
Key Features & Modules
Six parts, each doing one job in the chain between a Patreon pledge and a playing video.
Patreon OAuth & membership verification
Members sign in with their existing Patreon account - no second password to manage, and no membership data for the client to keep in sync by hand. The integration uses Patreon API v2 with the identity and identity.memberships scopes. We check three fields rather than one: patron_status must be active_patron, last_charge_status must not be a failure state such as Declined, and currently_entitled_tiers must contain a tier the video allows. Checking only the first is the common mistake, and it lets failed payments keep watching.
Tier-to-content mapping engine
Creators assign videos to tiers once, in the dashboard, and the rules do the filing from then on. The model supports a single tier, multiple tiers, cumulative access (“this tier and everything below it”), scheduled release windows, and early-access periods where a video is tier-locked for a set number of days before it opens. Because mapping lives in MySQL rather than in a spreadsheet, a tier rename or restructure on Patreon is a mapping update, not a re-tagging marathon.
Signed playback with Bunny Stream tokens
Video never leaves Bunny.net as an open URL. Bunny Stream's embed token authentication signs a request as SHA256 of the token security key, the video ID and the expiry timestamp, passed as token and expires parameters; requests after the expiry are refused with 403. Two rules governed the implementation. Tokens are minted server-side only, so the security key never reaches the browser, the Angular bundle or a device build - a key in client code is not a key, it is an announcement. And HLS needs path-style tokens, because protecting the manifest alone leaves the individual segments reachable.
Custom players for web and devices
Off-the-shelf players assume a stable file URL and a browser. Neither assumption held here, so we built players that do not. The player requests a token immediately before playback rather than at page load, refreshes it transparently for long-form content so a two-hour video does not stop at the token boundary, and fails into a clear “your membership does not include this” state rather than a broken video element. Playback behaviour, resume position and tier messaging stay consistent across browsers, mobile web and device builds, because they all talk to the same PHP entitlement endpoint.
Creator dashboard (Angular)
The Angular dashboard is where creators run the library: upload to Bunny, assign tiers, set release and early-access windows, and see who is watching what. It surfaces the things that used to be invisible - which videos each tier can reach, which members are active, and which access attempts were refused and why. Refusal reasons matter more than they sound: they are how a creator tells a genuine billing problem apart from someone trying a link they were sent.
Revocation through webhooks
Access has to end as reliably as it begins. We subscribe to Patreon's members:update, members:pledge:update and members:pledge:delete webhook events, so a downgrade, a failed charge or a cancellation updates entitlement in near real time. The next token request is refused; any token already issued simply runs out. There is no scenario where a former patron holds a working URL, because the URL was never the credential.
A Closer Look
The client's library is members-only by design, so these show the access model itself - what a verified member gets, and what a forwarded link gets.



How We Built It
The architectural decision that shaped everything else was refusing to cache entitlement as a durable fact. It is tempting to write “user 412 is a Tier 3 member” into the database and read that on playback - it is faster, and it is wrong, because the row is stale the instant a card declines. We treat entitlement as a decision made per request, with Patreon's response as the input.
Engineering decisions worth naming
- One entitlement service, one answer. Web, mobile and device players all call the same PHP endpoint. There is exactly one implementation of “is this person allowed to watch this”, so the rules cannot drift between clients.
- Short expiry, transparent refresh. Tokens are deliberately short-lived. The player renews in the background so viewers never see the mechanism, and a leaked URL is worth almost nothing by the time it is shared.
- Tier logic in data, not in code. Access rules live in MySQL mapping tables. Adding a tier or changing a rule is a data change, not a deployment.
- Cache tokens, never decisions. We cache a generated token for its own short lifetime to avoid re-signing on every segment, and never cache the entitlement decision behind it.
- Graceful Patreon degradation. If the Patreon API is slow or unavailable, the platform holds the last known good entitlement for a strictly bounded grace window rather than locking out paying members - and logs every grace decision for review.
- Every refusal is logged. Denied requests are recorded with the reason. That log is the client's early warning for both billing failures and sharing attempts.
Security & Anti-Piracy
No system stops a determined person pointing a camera at a screen. What a system can do is make the casual sharing - the kind that costs creators money - stop working. Ours does that in layers:
- No permanent video URL. Every playback URL is signed and expires. There is nothing durable to paste into a chat.
- Server-side signing. The Bunny token security key exists only on the server; it is never shipped to a client.
- Domain restrictions and DRM. Bunny's allowed-domain rules and Media Cage DRM keep playback on the embed path and discourage direct downloads.
- Segment-level protection. Path-style tokens protect the HLS .ts segments, not just the manifest.
- Minimum-scope OAuth. We request only identity and identity.memberships. The platform never sees payment instruments or address data.
- Refusal telemetry. Repeated denied attempts against one video are visible, which turns a silent leak into something a creator can see.
Said plainly: this raises the cost of sharing above the price of joining, which is the actual goal. Any vendor promising unbreakable video protection is selling something.
Results & Impact
The platform is live and runs the client's library. What changed:
- Shared links stopped working. A copied playback URL expires within minutes and is bound to one video, so forwarding it accomplishes nothing.
- Tier management became automatic. Videos are mapped once; upgrades, downgrades and cancellations are reflected without anyone editing a list.
- Lapsed access ends on its own. Webhook-driven revocation closes the gap between a cancelled pledge and lost access.
- One experience across devices. The same entitlement rules and the same player behaviour on browser, mobile and device builds.
- Visibility the creator never had. Per-video and per-tier viewing data, and a log of every refused attempt with its reason.
Services We Provided
- Patreon API v2 / OAuth 2.0 integration
- Tier-based access control design
- Bunny.net Stream integration and authentication
- Custom video player development (web, mobile, device)
- PHP backend and entitlement service
- Angular creator dashboard
- MySQL schema design for tier mapping
- Webhook-driven revocation
- Secure video delivery architecture review
Who Built This
The Fly IT Solution team led the backend and integration work on this build, and has spent 10+ years on PHP and Angular systems that connect third-party APIs to media delivery. On this project they were responsible for the Patreon OAuth flow, the entitlement service, the Bunny token layer and the player's token-refresh behaviour.
Fly IT Solution builds custom web and mobile software, with teams in Mohali, India and Minneapolis, USA. Related work: our Accuro EMR integration for a HIPAA-compliant patient flow platform, and our AI-powered operations platform for medical distributors.
Client Feedback
Web Developer to build a site that checks for Patreon Subscription tier before allowing access
“Great communication, understood the project well and did it really quickly and efficiently! Highly recommend!”
Endorsed by client
- Clear Communicator
Secure Patreon Video - FAQs
Common questions about tier gating, signed playback, revocation, and running the same pattern on another membership or video platform.
Can you restrict videos by Patreon tier?
Yes. The platform reads the viewer's currently_entitled_tiers from the Patreon API v2 at the moment of playback and compares it against the tiers mapped to that video. Access can be granted to one tier, several tiers, or a tier and everything below it. Because the check runs per request, an upgrade or downgrade takes effect immediately rather than at the next manual review.
How do you stop Patreon video links from being shared?
By removing the permanent link entirely. Each playback request returns a Bunny Stream URL signed with a SHA256 token bound to one video ID and one expiry timestamp. After that timestamp the URL returns 403. A forwarded link is therefore worth minutes at most, and only for the single video it was minted for.
What is Bunny.net token authentication?
It is Bunny Stream's signed-URL mechanism. The server generates a SHA256 hash of the token security key, the video ID and an expiry timestamp, and passes it as token and expires parameters on the embed URL. Requests after expiry are refused. For HLS, path-style tokens are used so the individual .ts segments are protected, not just the manifest.
What happens when a member cancels or their payment fails?
Patreon webhooks - members:update, members:pledge:update and members:pledge:delete - notify the platform, and entitlement is revoked in near real time. The next token request is refused. Any token already issued expires on its own schedule, so there is no lingering working URL. Failed charges are caught through last_charge_status, not just patron_status.
Can this work with a platform other than Patreon?
Yes. The entitlement service is deliberately separated from the membership provider, so the same architecture works with Stripe subscriptions, Memberful, Ko-fi, or an in-house billing system. What changes is the adapter that answers what a person is entitled to right now; the tier mapping, token signing and player layers stay as they are.
Do you have to use Bunny.net, or can it run on Vimeo or AWS?
The pattern is host-agnostic - any provider with signed URLs or token authentication can slot in, including CloudFront signed URLs. We chose Bunny.net here for its cost profile, its built-in embed token authentication and MediaCage DRM, and the fact that its token model works cleanly with per-request signing.
Why build a custom player instead of using an off-the-shelf one?
Because standard players assume a durable file URL, and this system deliberately has none. A custom player can request a token immediately before playback, refresh it mid-stream so long videos do not stop at the token boundary, and show a meaningful tier message instead of a broken video element - and behave identically across browser, mobile and device builds.
How long does a build like this take?
A focused implementation - Patreon OAuth, tier mapping, signed playback and a web player - typically runs 8 to 12 weeks. Custom device players, early-access scheduling and analytics extend that. The largest variable is usually the number of platforms the player has to ship on, not the integration itself.
Protecting Premium Video Behind a Membership?
If your paid videos are one forwarded link away from being free, we have solved this before. Tell us where your membership lives and where your video is hosted, and we will come back with a scoped approach.
