59 lines
1.7 KiB
PHP
59 lines
1.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Apoloz\AlfaBank\Traits;
|
|
|
|
use Apoloz\AlfaBank\Contracts\SignerInterface;
|
|
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
|
|
use Spatie\LaravelData\DataCollection;
|
|
|
|
trait SignableData
|
|
{
|
|
public function sign(SignerInterface $signer): self
|
|
{
|
|
// 1. Get raw data as array
|
|
$data = $this->toArray();
|
|
|
|
// 2. Remove existing signatures if any
|
|
unset($data['digestSignatures']);
|
|
|
|
// 3. Canonicalize (RFC 8785 - simple version: sort keys, remove nulls, no spaces)
|
|
$canonicalData = $this->canonicalizeArray($data);
|
|
|
|
$jsonToSign = json_encode($canonicalData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
|
|
// 4. Sign
|
|
$signature = $signer->sign($jsonToSign);
|
|
|
|
// 5. Attach signature to the original object
|
|
if ($this->digestSignatures === null) {
|
|
$this->digestSignatures = new DataCollection(DigestSignatureData::class, []);
|
|
}
|
|
|
|
$current = array_values($this->digestSignatures->toCollection()->all());
|
|
$current[] = new DigestSignatureData($signature, 'RSA');
|
|
|
|
$this->digestSignatures = new DataCollection(DigestSignatureData::class, $current);
|
|
|
|
return $this;
|
|
}
|
|
|
|
private function canonicalizeArray(array $array): array
|
|
{
|
|
// Remove nulls and empty strings
|
|
$array = array_filter($array, fn ($v) => $v !== null);
|
|
|
|
// Sort keys lexically
|
|
ksort($array);
|
|
|
|
foreach ($array as $key => $value) {
|
|
if (is_array($value)) {
|
|
$array[$key] = $this->canonicalizeArray($value);
|
|
}
|
|
}
|
|
|
|
return $array;
|
|
}
|
|
}
|