Fix some for sign body to montify and not get 401 error|
This commit is contained in:
@@ -47,26 +47,35 @@ class MontifyHmacSigner
|
||||
* @param array<string, mixed> $query
|
||||
*/
|
||||
public function verifyReturnSignature(array $query): bool
|
||||
{
|
||||
return $this->matchReturnSignatureVariant($query) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* نام قالبی که امضا با آن خواند، یا null اگر هیچکدام نخواند.
|
||||
*
|
||||
* پارتنر ممکن است مقادیر عددی را بهصورت number امضا کند یا پارامتر خالی
|
||||
* (مثل track_number) را اصلاً در payload نگذارد؛ همه حالتها بررسی میشود.
|
||||
*
|
||||
* @param array<string, mixed> $query
|
||||
*/
|
||||
public function matchReturnSignatureVariant(array $query): ?string
|
||||
{
|
||||
$signature = (string) ($query['signature'] ?? '');
|
||||
if ($signature === '' || !ctype_xdigit($signature)) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
unset($query['signature']);
|
||||
|
||||
// فقط اسکالرها — همه string طبق مستند پارتنر
|
||||
$payload = [];
|
||||
foreach ($query as $key => $value) {
|
||||
if (!is_string($key) || is_array($value) || $value === null) {
|
||||
continue;
|
||||
foreach ($this->payloadVariants($query) as $name => $payload) {
|
||||
$expected = hash_hmac('sha256', $this->stableStringify($payload), $this->clientSecret);
|
||||
if ($this->hashEqualsHex($signature, $expected)) {
|
||||
return $name;
|
||||
}
|
||||
$payload[$key] = (string) $value;
|
||||
}
|
||||
|
||||
$expected = hash_hmac('sha256', $this->stableStringify($payload), $this->clientSecret);
|
||||
|
||||
return $this->hashEqualsHex($signature, $expected);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,13 +83,79 @@ class MontifyHmacSigner
|
||||
*/
|
||||
public function verifyWebhookSignature(array $body, ?string $signatureHeader): bool
|
||||
{
|
||||
if ($signatureHeader === null || $signatureHeader === '') {
|
||||
$signature = (string) ($signatureHeader ?? '');
|
||||
if ($signature === '' || !ctype_xdigit($signature)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$expected = hash_hmac('sha256', $this->stableStringify($body), $this->clientSecret);
|
||||
// بدنه همانطور که آمده (نوعها از JSON) اولویت دارد، بعد حالتهای نرمالشده
|
||||
$candidates = array_merge(
|
||||
['raw' => $body],
|
||||
$this->payloadVariants($body)
|
||||
);
|
||||
|
||||
return $this->hashEqualsHex($signatureHeader, $expected);
|
||||
foreach ($candidates as $payload) {
|
||||
$expected = hash_hmac('sha256', $this->stableStringify($payload), $this->clientSecret);
|
||||
if ($this->hashEqualsHex($signature, $expected)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* حالتهای ممکن payload برای مقایسه امضا (بدون تکرار).
|
||||
*
|
||||
* @param array<string, mixed> $payload
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
private function payloadVariants(array $payload): array
|
||||
{
|
||||
$strings = [];
|
||||
foreach ($payload as $key => $value) {
|
||||
if (!is_string($key) || $key === '' || is_array($value) || $value === null) {
|
||||
continue;
|
||||
}
|
||||
$strings[$key] = is_bool($value) ? ($value ? 'true' : 'false') : (string) $value;
|
||||
}
|
||||
|
||||
$withoutEmpty = array_filter($strings, static fn ($value) => $value !== '');
|
||||
|
||||
$variants = [
|
||||
'strings' => $strings,
|
||||
'strings_without_empty' => $withoutEmpty,
|
||||
'numeric' => $this->castNumericValues($strings),
|
||||
'numeric_without_empty' => $this->castNumericValues($withoutEmpty),
|
||||
];
|
||||
|
||||
$unique = [];
|
||||
$seen = [];
|
||||
foreach ($variants as $name => $variant) {
|
||||
$fingerprint = $this->stableStringify($variant);
|
||||
if (isset($seen[$fingerprint])) {
|
||||
continue;
|
||||
}
|
||||
$seen[$fingerprint] = true;
|
||||
$unique[$name] = $variant;
|
||||
}
|
||||
|
||||
return $unique;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $payload
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function castNumericValues(array $payload): array
|
||||
{
|
||||
foreach (['amount', 'timestamp'] as $key) {
|
||||
if (isset($payload[$key]) && is_string($payload[$key]) && ctype_digit($payload[$key])) {
|
||||
$payload[$key] = (int) $payload[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
public function isTimestampFresh(int|string|null $timestamp, int $skewSeconds = 300): bool
|
||||
|
||||
Reference in New Issue
Block a user