diff --git a/lms/djangoapps/certificates/generation.py b/lms/djangoapps/certificates/generation.py
index 0f6f391c3886a4ccc2f6f11427b8b0c77851b3e7..18d46de42d6eb8f8515431e1aa01d5e6a36e1a18 100644
--- a/lms/djangoapps/certificates/generation.py
+++ b/lms/djangoapps/certificates/generation.py
@@ -46,6 +46,14 @@ def generate_allowlist_certificate(user, course_key):
     return cert
 
 
+def generate_course_certificate(user, course_key):
+    """
+    Generate a regular certificate for this user, in this course run. This method should be called from a task.
+    """
+    # TODO: Implementation will be added in MICROBA-1039
+    log.warning(f'Ignoring course certificate generation for {user.id}: {course_key}')
+
+
 def _generate_certificate(user, course_id):
     """
     Generate a certificate for this user, in this course run.
diff --git a/lms/djangoapps/certificates/generation_handler.py b/lms/djangoapps/certificates/generation_handler.py
index a86545d830a1d0d1be3acd3527448aa3ac8d4e37..434271574f01c2e2d610af5abbc1db1c1b9c07c9 100644
--- a/lms/djangoapps/certificates/generation_handler.py
+++ b/lms/djangoapps/certificates/generation_handler.py
@@ -21,6 +21,7 @@ from lms.djangoapps.certificates.models import (
 from lms.djangoapps.certificates.queue import XQueueCertInterface
 from lms.djangoapps.certificates.tasks import CERTIFICATE_DELAY_SECONDS, generate_certificate
 from lms.djangoapps.certificates.utils import emit_certificate_event, has_html_certificates_enabled
+from lms.djangoapps.grades.api import CourseGradeFactory
 from lms.djangoapps.instructor.access import list_with_level
 from lms.djangoapps.verify_student.services import IDVerificationService
 from openedx.core.djangoapps.certificates.api import auto_certificate_generation_enabled
@@ -34,7 +35,7 @@ WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='certificates_revamp')
 # .. toggle_name: certificates_revamp.use_allowlist
 # .. toggle_implementation: CourseWaffleFlag
 # .. toggle_default: False
-# .. toggle_description: Waffle flag to enable the course certificates allowlist (aka V2 of the certificate whitelist)
+# .. toggle_description: Waffle flag to enable the course certificates allowlist (aka v2 of the certificate whitelist)
 #   on a per-course run basis.
 # .. toggle_use_cases: temporary
 # .. toggle_creation_date: 2021-01-27
@@ -151,6 +152,11 @@ def _can_generate_allowlist_certificate(user, course_key):
                  f'for {user.id}.')
         return False
 
+    if not _is_on_certificate_allowlist(user, course_key):
+        log.info(f'{user.id} : {course_key} is not on the certificate allowlist. Allowlist certificate cannot be '
+                 f'generated.')
+        return False
+
     if not auto_certificate_generation_enabled():
         # Automatic certificate generation is globally disabled
         log.info(f'Automatic certificate generation is globally disabled. Allowlist certificate cannot be generated'
@@ -177,11 +183,6 @@ def _can_generate_allowlist_certificate(user, course_key):
         log.info(f'{user.id} does not have a verified id. Allowlist certificate cannot be generated for {course_key}.')
         return False
 
-    if not _is_on_certificate_allowlist(user, course_key):
-        log.info(f'{user.id} : {course_key} is not on the certificate allowlist. Allowlist certificate cannot be '
-                 f'generated.')
-        return False
-
     log.info(f'{user.id} : {course_key} is on the certificate allowlist')
     return _can_generate_allowlist_certificate_for_status(user, course_key)
 
@@ -196,9 +197,49 @@ def _can_generate_v2_certificate(user, course_key):
         log.info(f'{course_key} is not using v2 course certificates. Certificate cannot be generated.')
         return False
 
-    # TODO: Further implementation will be added in MICROBA-923
-    log.warning(f'Ignoring check on V2 course certificates for {user.id}: {course_key}')
-    return False
+    if not auto_certificate_generation_enabled():
+        # Automatic certificate generation is globally disabled
+        log.info(f'Automatic certificate generation is globally disabled. Certificate cannot be generated for '
+                 f'{user.id} : {course_key}.')
+        return False
+
+    if CertificateInvalidation.has_certificate_invalidation(user, course_key):
+        # The invalidation list prevents certificate generation
+        log.info(f'{user.id} : {course_key} is on the certificate invalidation list. Certificate cannot be generated.')
+        return False
+
+    enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(user, course_key)
+    if enrollment_mode is None:
+        log.info(f'{user.id} : {course_key} does not have an enrollment. Certificate cannot be generated.')
+        return False
+
+    if not modes_api.is_eligible_for_certificate(enrollment_mode):
+        log.info(f'{user.id} : {course_key} has an enrollment mode of {enrollment_mode}, which is not eligible for an '
+                 f'allowlist certificate. Certificate cannot be generated.')
+        return False
+
+    if not IDVerificationService.user_is_verified(user):
+        log.info(f'{user.id} does not have a verified id. Certificate cannot be generated for {course_key}.')
+        return False
+
+    if _is_ccx_course(course_key):
+        log.info(f'{course_key} is a CCX course. Certificate cannot be generated for {user.id}.')
+        return False
+
+    course = modulestore().get_course(course_key, depth=0)
+    if _is_beta_tester(user, course):
+        log.info(f'{user.id} is a beta tester in {course_key}. Certificate cannot be generated.')
+        return False
+
+    if not _has_passing_grade(user, course):
+        log.info(f'{user.id} does not have a passing grade in {course_key}. Certificate cannot be generated.')
+        return False
+
+    if not _can_generate_certificate_for_status(user, course_key):
+        return False
+
+    log.info(f'V2 certificate can be generated for {user.id} : {course_key}')
+    return True
 
 
 def is_using_certificate_allowlist_and_is_on_allowlist(user, course_key):
@@ -212,7 +253,7 @@ def is_using_certificate_allowlist_and_is_on_allowlist(user, course_key):
 
 def is_using_certificate_allowlist(course_key):
     """
-    Check if the course run is using the allowlist, aka V2 of certificate whitelisting
+    Check if the course run is using the allowlist, aka v2 of certificate whitelisting
     """
     return CERTIFICATES_USE_ALLOWLIST.is_enabled(course_key)
 
@@ -250,6 +291,47 @@ def _can_generate_allowlist_certificate_for_status(user, course_key):
     return True
 
 
+def _can_generate_certificate_for_status(user, course_key):
+    """
+    Check if the user's certificate status can handle regular (non-allowlist) certificate generation
+    """
+    cert = GeneratedCertificate.certificate_for_student(user, course_key)
+    if cert is None:
+        return True
+
+    if cert.status == CertificateStatuses.downloadable:
+        log.info(f'Certificate with status {cert.status} already exists for {user.id} : {course_key}, and is not '
+                 f'eligible for generation. Certificate cannot be generated as it is already in a final state.')
+        return False
+
+    log.info(f'Certificate with status {cert.status} already exists for {user.id} : {course_key}, and is eligible for '
+             f'generation')
+    return True
+
+
+def _is_beta_tester(user, course):
+    """
+    Check if the user is a beta tester in this course run
+    """
+    beta_testers_queryset = list_with_level(course, 'beta')
+    return beta_testers_queryset.filter(username=user.username).exists()
+
+
+def _is_ccx_course(course_key):
+    """
+    Check if the course is a CCX (custom edX course)
+    """
+    return hasattr(course_key, 'ccx')
+
+
+def _has_passing_grade(user, course):
+    """
+    Check if the user has a passing grade in this course run
+    """
+    course_grade = CourseGradeFactory().read(user, course)
+    return course_grade.passed
+
+
 def generate_user_certificates(student, course_key, course=None, insecure=False, generation_mode='batch',
                                forced_grade=None):
     """
diff --git a/lms/djangoapps/certificates/tasks.py b/lms/djangoapps/certificates/tasks.py
index bbcba0b5d503e1314bae90fbe1735643b3fd3f49..2691c87b7e0b45cd5027a4778a1986dd9ce3edee 100644
--- a/lms/djangoapps/certificates/tasks.py
+++ b/lms/djangoapps/certificates/tasks.py
@@ -10,7 +10,11 @@ from django.contrib.auth import get_user_model
 from edx_django_utils.monitoring import set_code_owner_attribute
 from opaque_keys.edx.keys import CourseKey
 
-from lms.djangoapps.certificates.generation import generate_allowlist_certificate, generate_user_certificates
+from lms.djangoapps.certificates.generation import (
+    generate_allowlist_certificate,
+    generate_course_certificate,
+    generate_user_certificates
+)
 from lms.djangoapps.verify_student.services import IDVerificationService
 
 log = getLogger(__name__)
@@ -49,8 +53,7 @@ def generate_certificate(self, **kwargs):
         return
 
     if v2_certificate:
-        # TODO: will be implemented in MICROBA-923
-        log.warning(f'Ignoring v2 certificate task request for {student.id}: {course_key}')
+        generate_course_certificate(user=student, course_key=course_key)
         return
 
     if expected_verification_status:
diff --git a/lms/djangoapps/certificates/tests/test_generation_handler.py b/lms/djangoapps/certificates/tests/test_generation_handler.py
index bf28dd6a2505356b86c30ff298e14499467076cc..0d92fa6507dc0331f2383fabe18fcc0f1da7a1d9 100644
--- a/lms/djangoapps/certificates/tests/test_generation_handler.py
+++ b/lms/djangoapps/certificates/tests/test_generation_handler.py
@@ -17,6 +17,7 @@ from lms.djangoapps.certificates.generation_handler import (
     is_using_certificate_allowlist,
     _is_using_v2_course_certificates,
     _can_generate_allowlist_certificate,
+    _can_generate_certificate_for_status,
     _can_generate_v2_certificate,
     can_generate_certificate_task,
     generate_allowlist_certificate_task,
@@ -36,7 +37,10 @@ from xmodule.modulestore.tests.factories import CourseFactory
 
 log = logging.getLogger(__name__)
 
+BETA_TESTER_METHOD = 'lms.djangoapps.certificates.generation_handler._is_beta_tester'
+CCX_COURSE_METHOD = 'lms.djangoapps.certificates.generation_handler._is_ccx_course'
 ID_VERIFIED_METHOD = 'lms.djangoapps.verify_student.services.IDVerificationService.user_is_verified'
+PASSING_GRADE_METHOD = 'lms.djangoapps.certificates.generation_handler._has_passing_grade'
 AUTO_GENERATION_NAMESPACE = waffle.WAFFLE_NAMESPACE
 AUTO_GENERATION_NAME = waffle.AUTO_CERTIFICATE_GENERATION
 AUTO_GENERATION_SWITCH_NAME = f'{AUTO_GENERATION_NAMESPACE}.{AUTO_GENERATION_NAME}'
@@ -259,6 +263,9 @@ class AllowlistTests(ModuleStoreTestCase):
 @override_switch(AUTO_GENERATION_SWITCH_NAME, active=True)
 @override_waffle_flag(CERTIFICATES_USE_UPDATED, active=True)
 @mock.patch(ID_VERIFIED_METHOD, mock.Mock(return_value=True))
+@mock.patch(CCX_COURSE_METHOD, mock.Mock(return_value=False))
+@mock.patch(PASSING_GRADE_METHOD, mock.Mock(return_value=True))
+@ddt.ddt
 class CertificateTests(ModuleStoreTestCase):
     """
     Tests for handling course certificates
@@ -281,13 +288,11 @@ class CertificateTests(ModuleStoreTestCase):
     def test_handle_valid(self):
         """
         Test handling of a valid user/course run combo.
-
-        Note: these assertions are placeholders for now. They will be updated as the implementation is added.
         """
-        assert not _can_generate_v2_certificate(self.user, self.course_run_key)
+        assert _can_generate_v2_certificate(self.user, self.course_run_key)
         assert can_generate_certificate_task(self.user, self.course_run_key)
-        assert not generate_certificate_task(self.user, self.course_run_key)
-        assert not generate_regular_certificate_task(self.user, self.course_run_key)
+        assert generate_certificate_task(self.user, self.course_run_key)
+        assert generate_regular_certificate_task(self.user, self.course_run_key)
 
     @override_waffle_flag(CERTIFICATES_USE_UPDATED, active=False)
     def test_handle_invalid(self):
@@ -311,3 +316,128 @@ class CertificateTests(ModuleStoreTestCase):
         Test the updated flag without the override
         """
         assert not _is_using_v2_course_certificates(self.course_run_key)
+
+    @ddt.data(
+        (CertificateStatuses.deleted, True),
+        (CertificateStatuses.deleting, True),
+        (CertificateStatuses.downloadable, False),
+        (CertificateStatuses.error, True),
+        (CertificateStatuses.generating, True),
+        (CertificateStatuses.notpassing, True),
+        (CertificateStatuses.restricted, True),
+        (CertificateStatuses.unavailable, True),
+        (CertificateStatuses.audit_passing, True),
+        (CertificateStatuses.audit_notpassing, True),
+        (CertificateStatuses.honor_passing, True),
+        (CertificateStatuses.unverified, True),
+        (CertificateStatuses.invalidated, True),
+        (CertificateStatuses.requesting, True))
+    @ddt.unpack
+    def test_generation_status(self, status, expected_response):
+        """
+        Test handling of certificate statuses
+        """
+        u = UserFactory()
+        cr = CourseFactory()
+        key = cr.id  # pylint: disable=no-member
+        GeneratedCertificateFactory(
+            user=u,
+            course_id=key,
+            mode=GeneratedCertificate.MODES.verified,
+            status=status,
+        )
+
+        assert _can_generate_certificate_for_status(u, key) == expected_response
+
+    def test_generation_status_for_none(self):
+        """
+        Test handling of certificate statuses for a non-existent cert
+        """
+        assert _can_generate_certificate_for_status(None, None)
+
+    def test_can_generate_auto_disabled(self):
+        """
+        Test handling when automatic generation is disabled
+        """
+        with override_waffle_switch(AUTO_GENERATION_SWITCH, active=False):
+            assert not _can_generate_v2_certificate(self.user, self.course_run_key)
+
+    def test_can_generate_not_verified(self):
+        """
+        Test handling when the user's id is not verified
+        """
+        with mock.patch(ID_VERIFIED_METHOD, return_value=False):
+            assert not _can_generate_v2_certificate(self.user, self.course_run_key)
+
+    def test_can_generate_ccx(self):
+        """
+        Test handling when the course is a CCX (custom edX) course
+        """
+        with mock.patch(CCX_COURSE_METHOD, return_value=True):
+            assert not _can_generate_v2_certificate(self.user, self.course_run_key)
+
+    def test_can_generate_beta_tester(self):
+        """
+        Test handling when the user is a beta tester
+        """
+        with mock.patch(BETA_TESTER_METHOD, return_value=True):
+            assert not _can_generate_v2_certificate(self.user, self.course_run_key)
+
+    def test_can_generate_failing_grade(self):
+        """
+        Test handling when the user does not have a passing grade
+        """
+        with mock.patch(PASSING_GRADE_METHOD, return_value=False):
+            assert not _can_generate_v2_certificate(self.user, self.course_run_key)
+
+    def test_can_generate_not_enrolled(self):
+        """
+        Test handling when user is not enrolled
+        """
+        u = UserFactory()
+        cr = CourseFactory()
+        key = cr.id  # pylint: disable=no-member
+        assert not _can_generate_v2_certificate(u, key)
+
+    def test_can_generate_audit(self):
+        """
+        Test handling when user is enrolled in audit mode
+        """
+        u = UserFactory()
+        cr = CourseFactory()
+        key = cr.id  # pylint: disable=no-member
+        CourseEnrollmentFactory(
+            user=u,
+            course_id=key,
+            is_active=True,
+            mode="audit",
+        )
+
+        assert not _can_generate_v2_certificate(u, key)
+
+    def test_can_generate_invalidated(self):
+        """
+        Test handling when user is on the invalidate list
+        """
+        u = UserFactory()
+        cr = CourseFactory()
+        key = cr.id  # pylint: disable=no-member
+        CourseEnrollmentFactory(
+            user=u,
+            course_id=key,
+            is_active=True,
+            mode="verified",
+        )
+        cert = GeneratedCertificateFactory(
+            user=u,
+            course_id=key,
+            mode=GeneratedCertificate.MODES.verified,
+            status=CertificateStatuses.downloadable
+        )
+        CertificateInvalidationFactory.create(
+            generated_certificate=cert,
+            invalidated_by=self.user,
+            active=True
+        )
+
+        assert not _can_generate_v2_certificate(u, key)