Fix some for sign body to montify and not get 401 error|

This commit is contained in:
mahmood
2026-07-27 01:36:02 +03:30
parent 815da9896d
commit 933424ddfc
8 changed files with 381 additions and 36 deletions
@@ -0,0 +1,100 @@
<?php
namespace App\Console\Commands;
use App\Services\ConfigService;
use App\Services\Payment\Support\MontifyHmacSigner;
use Illuminate\Console\Command;
/**
* عیب‌یابی امضای بازگشت Montify: نشان می‌دهد امضای دریافتی با کدام قالب payload می‌خواند.
* مثال:
* php artisan montify:check-return "https://site.ir/wallet/payment-result?status=success&..."
*/
class MontifyCheckReturnCommand extends Command
{
protected $signature = 'montify:check-return {url : آدرس کامل بازگشت یا فقط query string}';
protected $description = 'بررسی امضای بازگشت درگاه Montify و نمایش رشته canonical هر قالب';
public function handle(ConfigService $config): int
{
$input = (string) $this->argument('url');
$queryString = str_contains($input, '?') ? substr($input, strpos($input, '?') + 1) : $input;
parse_str($queryString, $query);
if (!is_array($query) || $query === []) {
$this->error('پارامتری در ورودی پیدا نشد.');
return self::FAILURE;
}
$clientId = $config->getString('external_payment_client_id', '');
$clientSecret = $config->getString('external_payment_client_secret', '');
if ($clientSecret === '') {
$this->error('external_payment_client_secret در تنظیمات خالی است.');
return self::FAILURE;
}
$signature = strtolower((string) ($query['signature'] ?? ''));
unset($query['signature']);
$strings = [];
foreach ($query as $key => $value) {
if (is_array($value)) {
continue;
}
$strings[$key] = (string) $value;
}
$withoutEmpty = array_filter($strings, static fn ($value) => $value !== '');
$signer = new MontifyHmacSigner($clientId, $clientSecret);
$castNumeric = static function (array $payload) {
foreach (['amount', 'timestamp'] as $key) {
if (isset($payload[$key]) && ctype_digit((string) $payload[$key])) {
$payload[$key] = (int) $payload[$key];
}
}
return $payload;
};
$variants = [
'strings' => $strings,
'strings_without_empty' => $withoutEmpty,
'numeric' => $castNumeric($strings),
'numeric_without_empty' => $castNumeric($withoutEmpty),
];
$this->line('');
$this->info('امضای دریافتی: ' . ($signature !== '' ? $signature : '— (خالی)'));
$this->line('طول client_secret: ' . strlen($clientSecret) . ' کاراکتر');
$this->line('');
$matched = null;
foreach ($variants as $name => $payload) {
$canonical = $signer->stableStringify($payload);
$expected = hash_hmac('sha256', $canonical, $clientSecret);
$ok = $signature !== '' && hash_equals($expected, $signature);
if ($ok && $matched === null) {
$matched = $name;
}
$this->line(($ok ? '[OK] ' : '[خطا] ') . $name);
$this->line(' canonical: ' . $canonical);
$this->line(' expected : ' . $expected);
$this->line('');
}
if ($matched !== null) {
$this->info('امضا با قالب «' . $matched . '» می‌خواند.');
return self::SUCCESS;
}
$this->error('هیچ قالبی با امضا نخواند — احتمالاً client_secret اشتباه است یا پارامتری از URL حذف شده.');
return self::FAILURE;
}
}