Skip to main content

Must Know

  • Upon payment success or failure, the gateway will redirect the user to the provided failUrl or returnUrl and call the webhook_url if one is provided.

  • User must implement payment and confirmation pages with these specific criteria:      

    • The site must have SSL security.

    • The redirection to the SATIM payment page must be done on a web browser independent of the merchant's website or application.      

    • The payment page show the final amount to pay, CIB logo, captcha, checkbox to accept the general conditions of online payment.

    • Display all the fields of the ePay confirmation. The fields are:

    • Display this message on the confirmation page "En cas de problème de paiement, veuillez contacter le numéro vert de la SATIM 3020 Example banner"

    • From confirmation page, web merchant must allow:

      • Printing receipt

      • Downloading receipt as PDF

      • Sending receipt via email

  • It is possible to request status of your transaction on endpoint /gateway/status/details/ with a max of 5 times per minunte. More than that, your request will get throttled.

  • The gateway includes a built-in safety mechanism to handle potential failures. It automatically monitors transactions that remain pending for an extended period, updates their status, and notifies your system via the webhook. To ensure smooth operations and avoid missed updates, we strongly recommend implementing disaster recovery to fully leverage this feature.

  • Webhooks may be called more than once for the same transaction, so ensure your code is designed to handle multiple notifications gracefully.

  • All timestamps are based on the Algeria/Algiers timezone.

  • For both redirection URLs (returnUrl, failUrl) and webhook calls, the user must recalculate the signature and compare it with the one sent by the gateway to verify the legitimacy of the request. The gateway calculates the hash by applying HMAC using your API secret key on the data 'invoice_id': ID-OF-ORDER, 'total': str(PRICE-TO-PAY). Here's how the hash is included in each case:

    • Redirection URLs:

      The hash is appended to the URL as a query parameter along with the id of the order (e.g., ?id=14&hash=abc123) to ensure the request originates from the gateway.

    • Webhook Calls:

      The hash is included in the payload headers (e.g., X-Signature: abc123) to authenticate the incoming request.

  • Here is an example of a webhook payload of a successfully paid checkout:

{
"invoice_id": 99,
"satim_order_id": "UmXcQHI7aAYGUQAAFQTH",
"tamayyuz_epay_id": 1298,
"epay_amount": "2500.59",
"date": "2023-10-01T12:34:56Z",
"refunded": false,
"refund_amount": null,
"status": "S",
"description": "Transaction succeeded",
"cardholder_name": "",
"satim_description": "Votre paiement a été accepté",
"approval_code": 123456,
"return_url": "https://your-website.com/return",
"fail_url": "https://your-website.com/fail",
}
  • Here is an example of a webhook endpoint you may implement to receive our webhook calls.
class EpayWebhookView(views.APIView):
def post(self, request):
invoice_id = request.data.get('invoice_id')
try:
invoice = Invoice.objects.get(id=invoice_id)
except ObjectDoesNotExist:
return Response({"detail": "Invoice does not exist..."}, status=status.HTTP_404_NOT_FOUND)

# If invoice already confirmed, skip updating it again.
if invoice.status == 'S' or invoice.status == 'F':
return Response({"detail": _("Invoice already updated...")}, status=status.HTTP_200_OK)

# Verify hash
total = invoice.total
hash_value = self.request.headers.get('X-Signature')
data = {'invoice_id': invoice_id, 'total': str(total)}
# Ensure consistent formatting
json_string = json.dumps(data, separators=(',', ':'), sort_keys=True, default=str).encode('utf-8')
calculated_signature = hmac.new(
EPAY_SECRET_KEY.encode('utf-8'),
msg=json_string,
digestmod=hashlib.sha256
).hexdigest().upper()
if calculated_signature != hash_value:
return Response({"detail": _('Invalid hash')}, status=status.HTTP_400_BAD_REQUEST)

# Update invoice status
try:
if request.data.get('status') == 'S':
invoice.status = 'S'
invoice.save(update_fields=['status'])

except Exception as e:
return Response({"detail": "Failed to update Invoice - Error: %(formatted_text)s" % {"formatted_text": str(e)}}, status=status.HTTP_404_NOT_FOUND)

return Response({"detail": "Invoice has been updated"}, status=status.HTTP_200_OK)