Fix some for sign body to montify and not get 401 error|
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user