DocsAdministration

Fee Gating

Optionally block students with overdue fees from course access

Fee gating (Track D) is an optional policy that blocks students whose OptiCRM fees are OVERDUE from enrolling in new courses or accessing existing lessons. It's off by default — most institutions never enable it.

When enabled and triggered, a FeeOverdueBanner component is shown to the affected student with the fee message and a deep link back to the OptiCRM portal to clear payment.

What it gates

When LMSSettings.feeGatingEnabled = true AND OptiCRM reports the student's status as OVERDUE:

ActionBlocked?
Browse the catalogueNo
View a course landing pageNo (banner shown)
Self-enroll in a new courseYesFEE_OVERDUE 402
Open a lessonYes
Submit progress / quiz / assignmentYes
Existing certificatesNo
NotificationsNo

PARTIAL is allowed — the gate fires only on OVERDUE. The intent is "you missed a due date", not "you owe a rupee".

Enabling per institution

Admins flip the toggle at /settings/general:

  1. Open Settings → General

  2. Scroll to the Fee gating section

  3. Toggle on

  4. (Optional) Customise the message students see in the banner — defaults to:

    Your school fees are overdue. Please contact the school office to clear them and restore course access.

  5. Save

The setting is per-institution — each tenant controls its own policy.

What students see

A red FeeOverdueBanner appears at the top of:

  • The dashboard
  • Course landing pages
  • The catalogue (when scrolling past the header)

Attempted lesson opens redirect back with the same banner inline. Quiz / assignment / enrollment APIs return 402 FEE_OVERDUE.

Soft-fail behaviour

If the OptiCRM bridge is unreachable when checking fee status, OptiLearn degrades open — the student keeps working as if their fees were paid.

The reasoning: a transient OptiCRM outage must never lock an entire institution out of the LMS. Better to let an unpaid student through for an hour than to brick a school during a network blip.

The failure is logged with [fee-gating] OptiCRM fee-status fetch failed so on-call notices, but no error surfaces to the user.

Note

This means a malicious student who can disrupt the OptiCRM bridge could bypass the gate temporarily. Fee gating is a courtesy feature, not a security control.

Performance

The fee-status check uses a React cache() memo per request. A single page load that hits the gate from multiple paths (lesson load + progress save + quiz attempt) only triggers one OptiCRM round-trip.

The setting itself is also cached per request. Institutions with feeGatingEnabled = false (the default) pay zero OptiCRM cost — the check short-circuits before touching the bridge.

Caller responsibility

The fee gate is only enforced for student Contacts. Routes that call enforceFeePaid() or checkFeePaid() choose whether to invoke them based on user.isContact. Instructors, admins, and staff bypass automatically — they never call into the helper.

Bypass for testing

To test the banner without an actual overdue fee, point a dev student account at an OptiCRM Student record with feeStatus = OVERDUE. There's no per-account override — the source of truth is OptiCRM's Student.feeStatus.

  • OptiCRM Integration/api/lms/students/[id]/fee-status endpoint
  • Source: src/lib/fee-gating.ts