fix(sign)

This commit is contained in:
Anatoliy Poloz
2026-03-08 18:35:02 +03:00
parent 706fcb01b2
commit c070297211
2 changed files with 54 additions and 57 deletions

View File

@@ -12,37 +12,47 @@ trait SignableData
{
public function sign(SignerInterface $signer): self
{
// 1. Create a clone to represent the data-to-be-signed (without existing signatures)
$clone = clone $this;
// 1. Get raw data as array
$data = $this->toArray();
// We explicitly set digestSignatures to null or empty for the signature calculation
// assuming the bank expects the "business payload" only.
// If the bank expects the field "digestSignatures": [] to be present in signed string,
// we should initialize it as empty collection.
// Based on common practices, we exclude it or keep it null.
// Let's assume we exclude it (set to null if the property is nullable).
$clone->digestSignatures = null;
// 2. Remove existing signatures if any
unset($data['digestSignatures']);
// 2. Serialize to JSON
// We use JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES to match typical API expectations
$jsonToSign = $clone->toJson(JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
// 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);
// 3. Sign
// 4. Sign
$signature = $signer->sign($jsonToSign);
// 4. Attach signature to the original object
// 5. Attach signature to the original object
if ($this->digestSignatures === null) {
$this->digestSignatures = new DataCollection(DigestSignatureData::class, []);
}
// We can't push to DataCollection directly if it's not array-access compatible in all versions,
// but Spatie DataCollection is usually iterable.
// Safest way is to reconstruct it.
$current = array_values($this->digestSignatures->toCollection()->all());
$current[] = new DigestSignatureData($signature, 'cms');
$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;
}
}