Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions src/Utility.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,49 @@ public function verifyPaymentSignature($attributes)
}

$secret = Api::getSecret();

// If we're using OAuth token authentication, check if secret is provided in attributes
if (empty($secret) && Api::getToken() !== null)
{
if (isset($attributes['secret']) === true)
{
$secret = $attributes['secret'];
}
else
{
throw new Errors\SignatureVerificationError(
'When using OAuth authentication, you must provide the secret as an attribute for signature verification.');
}
}

self::verifySignature($payload, $actualSignature, $secret);
}

public function verifyWebhookSignature($payload, $actualSignature, $secret)
public function verifyWebhookSignature($payload, $actualSignature, $secret = null)
{
// If secret isn't provided and we're using OAuth token authentication
if ($secret === null)
{
$secret = Api::getSecret();

if (empty($secret) && Api::getToken() !== null)
{
throw new Errors\SignatureVerificationError(
'When using OAuth authentication, you must explicitly provide the secret for webhook signature verification.');
}
}

self::verifySignature($payload, $actualSignature, $secret);
}

public function verifySignature($payload, $actualSignature, $secret)
{
if (empty($secret))
{
throw new Errors\SignatureVerificationError(
'No secret available for signature verification. Make sure to provide it or initialize Api with a secret key.');
}

$expectedSignature = hash_hmac(self::SHA256, $payload, $secret);

// Use lang's built-in hash_equals if exists to mitigate timing attacks
Expand Down Expand Up @@ -90,16 +122,16 @@ private function encrypt($dataToEncrypt, $secret) {
$encryptedData = openssl_encrypt($dataToEncrypt, $cipher, $key, OPENSSL_RAW_DATA, $iv, $tag, '', 16);

if ($encryptedData === false) {
throw new Exception('Encryption failed');
throw new \Exception('Encryption failed');
}

// Concatenate encrypted data with the authentication tag
$finalData = $encryptedData . $tag;

// Convert to hex string
return bin2hex($finalData);
} catch (Exception $e) {
throw new Exception('Encryption failed: ' . $e->getMessage());
} catch (\Exception $e) {
throw new \Exception('Encryption failed: ' . $e->getMessage());
}
}

Expand Down
48 changes: 48 additions & 0 deletions tests/SignatureVerificationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
namespace Razorpay\Tests;

use Razorpay\Api\Request;
use Razorpay\Api\Api;
use Razorpay\Api\Utility;

class SignatureVerificationTest extends TestCase
{
protected $api;
private static $subscriptionId;

public function setUp(): void
Expand Down Expand Up @@ -64,4 +67,49 @@ public function testSubscriptionVerification()
'razorpay_signature' => $signature
)));
}

/**
* Test verification with OAuth token authentication
*/
public function testOAuthTokenVerification()
{
$orderId = 'order_123456789';
$paymentId = 'pay_123456789';
$secret = 'test_secret_key';

// The payload that would be used for signature verification
$payload = $orderId . '|' . $paymentId;

// Generate a valid signature using the test secret
$expectedSignature = hash_hmac('sha256', $payload, $secret);

// Create a new API instance with OAuth token
$apiWithOAuth = new Api(null, null, 'test_oauth_token');

// This should work now with the secret passed in attributes
$this->assertNull($apiWithOAuth->utility->verifyPaymentSignature([
'razorpay_order_id' => $orderId,
'razorpay_payment_id' => $paymentId,
'razorpay_signature' => $expectedSignature,
'secret' => $secret
]));
}

/**
* Test webhook verification with OAuth token authentication
*/
public function testOAuthTokenWebhookVerification()
{
$payload = '{"payment_id":"pay_123456789","order_id":"order_123456789"}';
$secret = 'webhook_secret_key';

// Generate a valid signature using the test secret
$expectedSignature = hash_hmac('sha256', $payload, $secret);

// Create a new API instance with OAuth token
$apiWithOAuth = new Api(null, null, 'test_oauth_token');

// Verify webhook signature with explicit secret
$this->assertNull($apiWithOAuth->utility->verifyWebhookSignature($payload, $expectedSignature, $secret));
}
}