Controlling/selling node access by node reference?
Why not go custom access the main thing is relating your "Class" or Item to the product the person buys aka on the product you could create a entity reference to the node or class or whatever  ...

you can do some thing like the following 

use Drupal\Core\Access\AccessResult;
/**
* Implements hook_node_access().
*/
function mymodule_node_access(\Drupal\node\NodeInterface $node, $op, \Drupal\Core\Session\AccountInterface $account){
// Note thes checks do not work on admin (user 1). and clear cache each time.
$type = $node-&gt;bundle();
if ($type === 'your_content_type') {
  if ($account-&gt;id() !== 0) {
    $user = $account-&gt;getAccount();
    $uid = $user-&gt;id();
    // Ok we know is trying to access and the node type.
    $storage = \Drupal::entityTypeManager()-&gt;getStorage('commerce_order');
    $orders = $storage-&gt;loadByProperties(['uid' =&gt; $uid]);
    if (count($orders) != 0) {
      $has_bought = FALSE;

      // CUSTOM FIND Your Corse .
      $corse_type = $node-&gt;field_corse_type-&gt;value;
      foreach ($orders as $order) {
        $order_items = $order-&gt;order_items-&gt;referencedEntities();
        foreach ($order_items as $item) {
          // Do better than this lol ...
          // dump($item);
          if ($item-&gt;title-&gt;value == 'SOME Course' &amp;&amp; $corse_type == 'SOMETHING RELATED TO PRODUCT') {
            $has_bought = TRUE;
        }
      }
    }
    // final check.
    if ($has_bought === TRUE) {
      return AccessResult::isAllowed();
    }
    else{
      // Kick them. as not bought.
      return AccessResult::forbidden();
    }
  }
  else {
    // Kick them. as not paid.
    return AccessResult::forbidden();
  }

  }
  else {
  // kick them.
  return AccessResult::forbidden();
  }
 }
 else {
  return AccessResult::neutral();
  }
}