-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #105 from Midtrans/snapbi-webhook-verification
Snapbi webhook verification
- Loading branch information
Showing
9 changed files
with
169 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,8 @@ | |
|
||
namespace SnapBi; | ||
|
||
use Exception; | ||
|
||
/** | ||
* Provide Snap-Bi functionalities (create transaction, refund, cancel, get status) | ||
*/ | ||
|
@@ -34,6 +36,9 @@ class SnapBi | |
private $deviceId; | ||
private $debugId; | ||
private $timeStamp; | ||
private $signature; | ||
private $timestamp; | ||
private $notificationUrlPath; | ||
|
||
public function __construct($paymentMethod) | ||
{ | ||
|
@@ -64,7 +69,12 @@ public static function qris() | |
{ | ||
return new self("qris"); | ||
} | ||
|
||
/** | ||
* this method chain is used to verify webhook notification | ||
*/ | ||
public static function notification(){ | ||
return new self(""); | ||
} | ||
/** | ||
* this method chain is used to add additional header during access token request | ||
*/ | ||
|
@@ -95,7 +105,7 @@ public function withAccessToken($accessToken) | |
/** | ||
* this method chain is used to supply the request body/ payload | ||
*/ | ||
public function withBody(array $body) | ||
public function withBody($body) | ||
{ | ||
$this->body = $body; | ||
return $this; | ||
|
@@ -146,6 +156,22 @@ public function withDebuglId($debugId) | |
return $this; | ||
} | ||
|
||
public function withSignature($signature) | ||
{ | ||
$this->signature = $signature; | ||
return $this; | ||
} | ||
public function withTimeStamp($timeStamp) | ||
{ | ||
$this->timestamp = $timeStamp; | ||
return $this; | ||
} | ||
public function withNotificationUrlPath($notificationUrlPath) | ||
{ | ||
$this->notificationUrlPath = $notificationUrlPath; | ||
return $this; | ||
} | ||
|
||
/** | ||
* these method chain is used to execute create payment | ||
*/ | ||
|
@@ -195,6 +221,28 @@ public function getAccessToken() | |
return SnapBiApiRequestor::remoteCall(SnapBiConfig::getSnapBiTransactionBaseUrl() . self::ACCESS_TOKEN, $snapBiAccessTokenHeader, $openApiPayload); | ||
} | ||
|
||
/** | ||
* @throws Exception | ||
*/ | ||
public function isWebhookNotificationVerified(){ | ||
if (!SnapBiConfig::$snapBiPublicKey){ | ||
throw new Exception( | ||
'The public key is null, You need to set the public key from SnapBiConfig.' . | ||
'For more details contact support at [email protected] if you have any questions.' | ||
); | ||
} | ||
$notificationHttpMethod = "POST"; | ||
$minifiedNotificationBodyJsonString = json_encode($this->body); | ||
$hashedNotificationBodyJsonString = hash('sha256', $minifiedNotificationBodyJsonString); | ||
$rawStringDataToVerifyAgainstSignature = $notificationHttpMethod . ':' . $this->notificationUrlPath . ':' . $hashedNotificationBodyJsonString . ':' . $this->timestamp; | ||
$isSignatureVerified = openssl_verify( | ||
$rawStringDataToVerifyAgainstSignature, | ||
base64_decode($this->signature), | ||
SnapBiConfig::$snapBiPublicKey, | ||
OPENSSL_ALGO_SHA256 | ||
); | ||
return $isSignatureVerified === 1; | ||
} | ||
private function createConnection($externalId = null) | ||
{ | ||
// Attempt to get the access token if it's not already set | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
// notification.php | ||
|
||
namespace SnapBi; | ||
|
||
require_once dirname(__FILE__) . '/../../../Midtrans.php'; | ||
// Only allow POST requests | ||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { | ||
header("HTTP/1.1 405 Method Not Allowed"); | ||
echo json_encode(["error" => "Only POST requests are allowed"]); | ||
exit; | ||
} | ||
|
||
// Read all headers | ||
$headers = getallheaders(); | ||
|
||
// Example: Get a specific header (like 'X-Signature') | ||
$xSignature = isset($headers['X-Signature']) ? $headers['X-Signature'] : "kosong"; | ||
$xTimeStamp = isset($headers['X-Timestamp']) ? $headers['X-Timestamp'] : "kosong"; | ||
$requestUri = $_SERVER['REQUEST_URI']; | ||
|
||
// Extract everything after '/notification.php' | ||
$afterNotification = strstr($requestUri, '/notification.php'); | ||
$pathAfterNotification = substr($afterNotification, strlen('/notification.php')); | ||
|
||
// Read the input/payload from the request body | ||
$input = file_get_contents("php://input"); | ||
$payload = json_decode($input); | ||
|
||
if (!$input) { | ||
// Respond with an error if no payload is received | ||
header("Content-Type: application/json"); | ||
echo json_encode(["error" => "No input received"]); | ||
exit; | ||
} | ||
header("Content-Type: application/json"); | ||
$notificationUrlPath = $pathAfterNotification; | ||
|
||
$publicKeyString = "-----BEGIN PUBLIC KEY-----\nABCDefghlhuoJgoXiK21s2NIW0+uJb08sHmd/+/Cm7UH7M/oU3VE9oLhU89oOzXZgtsiw7lR8duWJ0w738NfzvkdA5pX8OYnIL+5Hfa/CxvlT4yAX/abcdEFgh\n-----END PUBLIC KEY-----\n"; | ||
SnapBiConfig::$snapBiPublicKey = $publicKeyString; | ||
|
||
|
||
try { | ||
echo json_encode([ | ||
"message" => "Webhook verified successfully", | ||
"isVerified" => SnapBi::notification() | ||
->withBody($payload) | ||
->withSignature($xSignature) | ||
->withTimeStamp($xTimeStamp) | ||
->withNotificationUrlPath($notificationUrlPath) | ||
->isWebhookNotificationVerified(), | ||
]); | ||
} catch (\Exception $e) { | ||
echo json_encode([ | ||
"message" => "Webhook verification error", | ||
"error" => $e->getMessage() | ||
]); | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,22 +13,20 @@ | |
*/ | ||
|
||
$client_id = "Zabcdefg-MIDTRANS-CLIENT-SNAP"; | ||
|
||
//make sure to add 3 newline "\n" to your private key as shown below | ||
$private_key = "-----BEGIN PRIVATE KEY-----\nABCDEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7Zk6kJjqamLddaN1lK03XJW3vi5zOSA7V+5eSiYeM9tCOGouJewN/Py58wgvRh7OMAMm1IbSZpAbcZbBa1=\n-----END PRIVATE KEY-----\n"; | ||
$client_secret = "ABcdefghiSrLJPgKRXqdjaSAuj5WDAbeaXAX8Vn7CWGHuBCfFgABCDVqRLvNZf8BaqPGKaksMjrZDrZqzZEbaA1AYFwBewIWCqLZr4PuvuLBqfTmYIzAbCakHKejABCa"; | ||
$partner_id = "partner-id"; | ||
$merchant_id = "M001234"; | ||
|
||
$channel_id = "12345"; | ||
|
||
$external_id = "uzi-order-testing" . uniqid(); | ||
$customerVaNo = generateRandomNumber(); | ||
|
||
$vaParams = array( | ||
"partnerServiceId"=> " 70012", | ||
"customerNo"=> $customerVaNo, | ||
"virtualAccountNo"=> " 70012" . $customerVaNo, | ||
"partnerServiceId"=> " 1234", | ||
"customerNo"=> "0000000000", | ||
"virtualAccountNo"=> " 12340000000000", | ||
"virtualAccountName"=> "Jokul Doe", | ||
"virtualAccountEmail"=> "[email protected]", | ||
"virtualAccountPhone"=> "6281828384858", | ||
|
@@ -39,19 +37,9 @@ | |
], | ||
"additionalInfo"=> [ | ||
"merchantId"=> $merchant_id, | ||
"bank"=> "mandiri", | ||
"bank"=> "bca", | ||
"flags"=> [ | ||
"shouldRandomizeVaNumber"=> false | ||
], | ||
"mandiri"=> [ | ||
"billInfo1"=> "bank_name", | ||
"billInfo2"=> "mandiri", | ||
"billInfo3"=> "Name:", | ||
"billInfo4"=> "Budi Utomo", | ||
"billInfo5"=> "Class:", | ||
"billInfo6"=> "Computer Science", | ||
"billInfo7"=> "ID:", | ||
"billInfo8"=> "VT-12345" | ||
"shouldRandomizeVaNumber"=> true | ||
], | ||
"customerDetails"=> [ | ||
"firstName"=> "Jokul", | ||
|
@@ -118,8 +106,8 @@ | |
SnapBiConfig::$snapBiPrivateKey = $private_key; | ||
SnapBiConfig::$snapBiClientSecret = $client_secret; | ||
SnapBiConfig::$snapBiPartnerId = $partner_id; | ||
SnapBiConfig::$snapBiChannelId = $partner_id; | ||
SnapBiConfig::$snapBiChannelId = $channel_id; | ||
SnapBiConfig::$enableLogging = true; | ||
|
||
try { | ||
|
||
|