Skip to content

Commit

Permalink
🐛(backend) submit for signature handle timeout delete signing procedure
Browse files Browse the repository at this point in the history
When the method `submit_for_signature` needs to delete a signing procedure
because some elements have changed overtime in the contract definition
and before the student signed the document, it appears that it may take
a while on the signature provider side to execute the deletion.
In order to handle and update our contract, we have added a try
except block to catch the timeout issue, and proceed to update the contract
as if we would have received the response from the signature provider.
Before we added the fix, the contract would keep the old reference that would
not work when the student wanted to sign the document, causing another error
because it has already been deleted after the timeout we have set in the
signature backend. In some cases, due to the timeout error, when we attempt
to delete the outdated reference, we would then have a NOT_FOUND status
code, we should also reset the contrat submission value as well to restart with
a new contrat to submit.
  • Loading branch information
jonathanreveille committed Sep 13, 2024
1 parent 0476ca6 commit 4eaed88
Show file tree
Hide file tree
Showing 4 changed files with 838 additions and 4 deletions.
9 changes: 9 additions & 0 deletions src/backend/joanie/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Specific exceptions for the core application
"""

from requests.exceptions import ReadTimeout


class EnrollmentError(Exception):
"""An exception to raise if an enrollment fails."""
Expand Down Expand Up @@ -29,3 +31,10 @@ class CertificateGenerationError(Exception):
Exception raised when the certificate generation process fails due to the order not meeting
all specified conditions.
"""


class BackendTimeOut(ReadTimeout):
"""
Exception raised when a backend reaches the timeout set when we are waiting
for the response.
"""
26 changes: 22 additions & 4 deletions src/backend/joanie/core/models/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from urllib3.util import Retry

from joanie.core import enums
from joanie.core.exceptions import CertificateGenerationError
from joanie.core.exceptions import BackendTimeOut, CertificateGenerationError
from joanie.core.fields.schedule import OrderPaymentScheduleEncoder
from joanie.core.flows.order import OrderFlow
from joanie.core.models.accounts import User
Expand All @@ -37,7 +37,9 @@
from joanie.core.utils import contract_definition as contract_definition_utility
from joanie.core.utils import issuers, webhooks
from joanie.core.utils.payment_schedule import generate as generate_payment_schedule
from joanie.core.utils.signature import handle_signature_deletion_error
from joanie.signature.backends import get_signature_backend
from joanie.signature.exceptions import DeleteSignatureProcedureFailed

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -965,9 +967,25 @@ def submit_for_signature(self, user: User):
)

if should_be_resubmitted:
backend_signature.delete_signing_procedure(
contract.signature_backend_reference
)
# The signature provider may take some time to respond with the confirmation of the
# deletion. If we reach the timeout limit, we should reset the contract submission
# values because the signature provider will delete them.
# We won't be able to use the `contract.signature_backend_reference` again.
# There can be edge case where the signature backend reference was already deleted,
# causing an error that the reference was not found at the signature provider side.
try:
backend_signature.delete_signing_procedure(
contract.signature_backend_reference
)
except BackendTimeOut as exception: # pylint: disable=unused-variable
handle_signature_deletion_error(
order=self, error_message="Timeout on signature reference deletion"
)
except DeleteSignatureProcedureFailed as exception: # pylint: disable=unused-variable
handle_signature_deletion_error(
order=self, error_message="Failed to delete signature procedure"
)
was_already_submitted = False

# We want to submit or re-submit the contract for signature in three cases:
# 1- the contract was never submitted for signature before
Expand Down
24 changes: 24 additions & 0 deletions src/backend/joanie/core/utils/signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

import hashlib
import hmac
import logging

from django.conf import settings

from rest_framework import exceptions

logger = logging.getLogger(__name__)


def check_signature(request, settings_name):
"""Check the signature of a request."""
Expand All @@ -33,3 +36,24 @@ def check_signature(request, settings_name):
)
if not signature_is_valid:
raise exceptions.AuthenticationFailed("Invalid authentication.")


def handle_signature_deletion_error(order, error_message):
"""
Handles the exception raised on the deleting of signing procedure.
It's responsible to reset the submission values from the outdated
contract of the order.
"""
logger.error(
error_message,
extra={
"context": {
"order": order.to_dict(),
"product": order.product.to_dict(),
"signature_backend_reference": (
order.contract.signature_backend_reference
),
}
},
)
order.contract.reset_submission_for_signature()
Loading

0 comments on commit 4eaed88

Please sign in to comment.