Handling HTTP Notification

In order to increase the security aspect, there are several ways to ensure that the notification received is from Midtrans.


<?php

require_once('Veritrans.php');
Veritrans_Config::$isProduction = false;
Veritrans_Config::$serverKey = '<your serverkey>';
$notif = new Veritrans_Notification();

$transaction = $notif->transaction_status;
$type = $notif->payment_type;
$order_id = $notif->order_id;
$fraud = $notif->fraud_status;

if ($transaction == 'capture') {
  // For credit card transaction, we need to check whether transaction is challenge by FDS or not
  if ($type == 'credit_card'){
    if($fraud == 'deny'){
      // TODO set payment status in merchant's database to 'Challenge by FDS'
      // TODO merchant should decide whether this transaction is authorized or not in MAP
      echo "Transaction order_id: " . $order_id ." is denied by FDS";
      }
      else {
      // TODO set payment status in merchant's database to 'Success'
      echo "Transaction order_id: " . $order_id ." successfully captured using " . $type;
      }
    }
  }
else if ($transaction == 'settlement'){
  // TODO set payment status in merchant's database to 'Settlement'
  echo "Transaction order_id: " . $order_id ." successfully transfered using " . $type;
  }
  else if($transaction == 'pending'){
  // TODO set payment status in merchant's database to 'Pending'
  echo "Waiting customer to finish transaction order_id: " . $order_id . " using " . $type;
  }
  else if ($transaction == 'deny') {
  // TODO set payment status in merchant's database to 'Denied'
  echo "Payment using " . $type . " for transaction order_id: " . $order_id . " is denied.";
  }
  else if ($transaction == 'expire') {
  // TODO set payment status in merchant's database to 'expire'
  echo "Payment using " . $type . " for transaction order_id: " . $order_id . " is expired.";
  }
  else if ($transaction == 'cancel') {
  // TODO set payment status in merchant's database to 'Denied'
  echo "Payment using " . $type . " for transaction order_id: " . $order_id . " is canceled.";
}
?>


Challenge Response


An additional mechanism we provide to verify the content and the origin of the notification is to challenge. This can be achieved by calling the get status API. The response is the same as the notification status.





Signature Key


We added signature key information in our notification. The purpose of this signature key is to validate whether the notification is originated from Midtrans or not. Should the notification not be genuine, merchants can disregard the notification. Please find on the side, the logic of the signature key and the sample code to generate signature key.


SHA512(order_id + status_code + gross_amount + serverkey)
<?php
$orderId = "1111";
$statusCode = "200";
$grossAmount = "100000.00";
$serverKey = "askvnoibnosifnboseofinbofinfgbiufglnbfg";
$input = $orderId.$statusCode.$grossAmount.$serverKey;
$signature = openssl_digest($input, 'sha512');
echo "INPUT: " , $input."<br/>";
echo "SIGNATURE: " , $signature;
?>



Best Practice to Handle notification


  • Always use an HTTPS endpoint. It is secure and there cannot be MITM attacks because we validate the certificates match the hosts. Also do not use self signed certificates.
  • Always implement notification in an idempotent way, in extremely rare cases, we may send multiple notifications for the same transaction event twice. It should not cause double entries on the merchant end. The simple way of achieving this is to use orderid as the key to track the entries.
  • Always check the signature hash of the notification, This will confirm that the notification was actually sent by Midtrans, because we encode the shared secret (server key). Nobody else can build this signature hash.
  • Always check all the following three fields to confirm successful transactions
    • status code: Should be 200 for successful transactions
    • fraud status: ACCEPT
    • transaction status : settlement/capture
  • We strive to send the notification immediately after the transaction has occurred, but in extremely rare cases, it may be delayed because of transaction spikes. If you have not received a notification, please use the Status API to check the current status of the transaction.
  • It is safe to call Status API to get the latest status of the transaction/order on each notification.
  • We set the HTTP timeout to 30 seconds. Please strive to keep the response time of the HTTP notifications under 5 seconds.
  • In extremely rare cases we may send the HTTP notifications out of order, ie. a settlement status for a notification before the notification for Pending status. It’s important that such later notifications are ignored. Here’s the state transition diagram that you could use. But again, use our /status API to confirm the actual status.
  • We send the notification body as JSON, please parse the JSON with a JSON parser. Always expect new fields will be added to the notification body, so parse it in a non strict format, so if the parser sees new fields, it should not throw exception. It should gracefully ignore the new fields. This allows us to extend our notification system for newer use cases without breaking old clients.
  • Always use the right HTTP Status code for responding to the notification, we handle retry for error cases differently based on the status code
    • for 2xx: No retries, it is considered success
    • for 500: We will retry 1 times in the 1 minute interval
    • for 503: Retry 4 times
    • for 400/404: retry 2 times in 1 minute interval
    • for all other failures: Retry 5 times at 1 minute interval
  • Redirection
    • for 307/308: The request will be repeated with the new URL using POST method and the same notification body. A maximum of 5 redirection is allowed.
      -for 301/301/303: The job will be marked as failed without further retries. The merchant will be notified via email. We suggest either to use 307/308 or update the notification endpoint settings in merchant portal.