How I Locked My 4K Downloads Behind Patreon — Without a Plugin

:white_check_mark: How I Locked My 4K Downloads Behind Patreon — Without a Plugin

After 2 full days of testing, I figured out a smart workaround to lock 4K video downloads behind Patreon using just:

  • :white_check_mark: One hidden page
  • :white_check_mark: A few lines of HTML + JavaScript
  • :white_check_mark: 3 small PHP files

No bloated plugin, and best of all — **the 4K link is not exposed in the source code. Obviously for me this was useful for a button, but you can hide anything by simply having a class id if you are using Elementor, you can hide certain sections if needed. Hope this helps someone!

:locked: Step 1: Create a “Patreon Access Check” Page

On a hidden page (set to private or visible only to patrons), inject this:

html

<!-- This div is shown only if user has access -->
<div id="patreon-access" data-access="true" style="display:none;"></div>

If the user doesn’t have access, the element won’t render — and we use that in step 2.

:gear: Step 2: Add This to functions.php

To get the current post ID into your HTML via a shortcode:

add_shortcode('post_id', function () {
    return get_the_ID();
});

:inbox_tray: Step 3: Add This HTML to Your Page

Paste this into an Elementor HTML widget or any page editor:

<iframe id="patreon-check-frame" src="https://yourwebsite.com/patreon-check/" style="display:none;" onload="checkPatreonAccess()"></iframe>

<div id="acf-4k-link" data-post="[post_id]" style="display:none;"></div>

<div id="4k-button-container">
  <a class="button button-4k-download" style="display:none;" download>🎉 Download 4K</a>
  <div class="button button-4k-not-available" style="display:none;">🚫 4K Not Available</div>
  <a href="https://patreon.com/YOURPATREON" class="button button-upgrade-patreon" target="_blank" style="display:none;">🔒 Unlock 4K Access</a>
</div>

<script>
function checkPatreonAccess() {
  setTimeout(() => {
    try {
      const iframeDoc = document.getElementById('patreon-check-frame').contentWindow.document;
      const accessEl = iframeDoc.querySelector('#patreon-access');

      if (accessEl?.dataset.access === "true") {
        fetch('/wp-content/themes/YOUR-CHILD-THEME/set-session.php')
          .then(() => {
            const postId = document.getElementById('acf-4k-link').dataset.post;
            fetch(`/wp-content/themes/YOUR-CHILD-THEME/check-4k.php?post_id=${postId}`)
              .then(res => res.json())
              .then(data => {
                if (data.has_4k) {
                  const btn = document.querySelector('.button-4k-download');
                  btn.href = `/wp-content/themes/YOUR-CHILD-THEME/secure-download.php?post_id=${postId}`;
                  btn.style.display = 'inline-block';
                } else {
                  document.querySelector('.button-4k-not-available').style.display = 'inline-block';
                }
              });
          });
      } else {
        document.querySelector('.button-upgrade-patreon').style.display = 'inline-block';
      }
    } catch (e) {
      console.warn("Access check failed:", e);
    }
  }, 500);
}
</script>

:locked_with_key: Step 4: secure-download.php

Create this file at:
:file_folder: /wp-content/themes/hello-theme-child-master/secure-download.php

php

CopyEdit

<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/wp-load.php');
session_start();

$post_id = intval($_GET['post_id'] ?? 0);
if (!$post_id) wp_die('Missing post ID.');
if (!is_user_logged_in()) wp_die('You must be logged in.');
if ($_SESSION['patreon_access'] !== true) wp_die('Access denied.');

$video_url = get_field('4k_video_url', $post_id);
if (!$video_url) wp_die('4K video not found.');

wp_redirect($video_url);
exit;

:brain: Step 5: set-session.php

:file_folder: /wp-content/themes/hello-theme-child-master/set-session.php

<?php
session_start();
$_SESSION['patreon_access'] = true;

:white_check_mark: Step 6: check-4k.php

:file_folder: /wp-content/themes/hello-theme-child-master/check-4k.php

php

CopyEdit

<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/wp-load.php');

$post_id = intval($_GET['post_id'] ?? 0);
$has_4k = get_field('4k_video_url', $post_id) ? true : false;

echo json_encode(['has_4k' => $has_4k]);

:tada: Final Result

  • :white_check_mark: Button only shows if user is a Patron
  • :white_check_mark: 4K link is hidden behind a secure PHP redirect
  • :white_check_mark: If no link: “4K Not Available”
  • :cross_mark: If not a Patron: shows “Unlock on Patreon”
1 Like