# HTML formu imzası

Alıcı, herhangi bir form alanını doğrudan tarayıcıda değiştirebilir — örneğin daha düşük bir tutar girebilir. Bu yüzden fatura alanları imzalı bir kapta taşınır: hizmet imzayı yeniden hesaplar ve eşleşmezse faturayı oluşturmaz.

İmzayı mağazanın sunucusu hesaplar. Tarayıcıya yalnızca hazır `data` ve `signature` çifti gider.

## Ne imzalanır

```
data      = base64url(json)
signature = HMAC-SHA256(data, formSecret)
```

HMAC, JSON baytları üzerinden değil, `data` dizesi üzerinden hesaplanır. Hizmet, imzayı gönderilen dizeye göre olduğu gibi doğrular ve ancak ondan sonra dizeyi çözer.

Buradan çıkan sonuç, kanonikleştirme olmadığıdır: anahtar sırası, JSON girintileme ve Unicode kaçış stili imzayı etkilemez. Herhangi bir standart JSON kodlayıcı iş görür.

## Referans seti

Bu değerleri kodunuza koyun ve çıktıyı karşılaştırın.

Örnek gizli anahtar:

```
dvc3khto5WpjEPojZfYJsbJzFEPPVnRa
```

Tutar ve ödeme süresi sayı olarak; dizeler de kabul edilir:

```json
{"projectId":"a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d","amountFiat":10.5,"currencyFiat":"USD","timeToPay":1,"description":"Order #7, delivery","serviceData":"order-7"}
```

`data` dizesi, bu baytların base64url'sidir. Burada dolgu kaldırılmıştır; hizmet dolgulu halini de kabul eder:

```
eyJwcm9qZWN0SWQiOiJhMWIyYzNkNC01ZTZmLTRhN2ItOGM5ZC0wZTFmMmEzYjRjNWQiLCJhbW91bnRGaWF0IjoxMC41LCJjdXJyZW5jeUZpYXQiOiJVU0QiLCJ0aW1lVG9QYXkiOjEsImRlc2NyaXB0aW9uIjoiT3JkZXIgIzcsIGRlbGl2ZXJ5Iiwic2VydmljZURhdGEiOiJvcmRlci03In0
```

Bu dizenin yukarıdaki gizli anahtarla imzası:

```
09628020c47400c9231420450af642adfdb3c246f33e3e12cb8e28f96c2a785e
```

Bu değerlerle, aşağıdaki örnekler tam olarak bu `data` ve imzayı yazdırır. Sonuna `"output":"errors"` anahtarı eklenmiş aynı JSON, farklı bir `data` dizesi ve farklı bir imza üretir.

JSON aynı göründüğü halde `data` eşleşmiyorsa neden JSON kodlayıcıdır: farklı anahtar sırası veya ayırıcılardan sonra boşluklar. Hizmet böyle bir kabı kabul eder, ancak referansla birebir eşleşmez.

## Örnekler

**PHP**

```php showLineNumbers
<?php

function signedContainer(array $fields, string $formSecret): array {
    $data = rtrim(strtr(base64_encode(json_encode($fields)), '+/', '-_'), '=');

    return [
        'data'      => $data,
        'signature' => hash_hmac('sha256', $data, $formSecret),
    ];
}

// Test values from the reference set. Before going live, 
// replace the secret with the form secret from your project settings
$container = signedContainer([
    'projectId'    => 'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
    'amountFiat'   => 10.5,
    'currencyFiat' => 'USD',
    'timeToPay'    => 1,
    'description'  => 'Order #7, delivery',
    'serviceData'  => 'order-7',
], 'dvc3khto5WpjEPojZfYJsbJzFEPPVnRa');

echo $container['data'], "\n";
echo $container['signature'], "\n";
```

**TypeScript**

```typescript showLineNumbers
import { createHmac } from 'node:crypto';

// Values are JSON strings or numbers, optional keys may be omitted
export interface FormFields {
    projectId: string;
    amountFiat: string | number;
    currencyFiat: string;
    timeToPay: string | number;
    description?: string;
    serviceData?: string;
    output?: string;
}

export interface FormContainer {
    data: string;
    signature: string;
}

export const signedContainer = (fields: FormFields, formSecret: string): FormContainer => {
    const data = Buffer.from(JSON.stringify(fields), 'utf8').toString('base64url');

    return {
        data,
        signature: createHmac('sha256', formSecret).update(data, 'utf8').digest('hex'),
    };
};

// Test values from the reference set. Before going live, 
// replace the secret with the form secret from your project settings
const container = signedContainer({
    projectId:    'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
    amountFiat:   10.5,
    currencyFiat: 'USD',
    timeToPay:    1,
    description:  'Order #7, delivery',
    serviceData:  'order-7',
}, 'dvc3khto5WpjEPojZfYJsbJzFEPPVnRa');

console.log(container.data);
console.log(container.signature);
```

**JavaScript**

Bu, tarayıcı için değil mağazanın sunucusu için koddur: imza, gizli anahtarın saklandığı yerde hesaplanır.

```js showLineNumbers
import { createHmac } from 'node:crypto';

const signedContainer = (fields, formSecret) => {
    const data = Buffer.from(JSON.stringify(fields), 'utf8').toString('base64url');

    return {
        data,
        signature: createHmac('sha256', formSecret).update(data, 'utf8').digest('hex'),
    };
};

// Test values from the reference set. Before going live, 
// replace the secret with the form secret from your project settings
const container = signedContainer({
    projectId:    'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
    amountFiat:   10.5,
    currencyFiat: 'USD',
    timeToPay:    1,
    description:  'Order #7, delivery',
    serviceData:  'order-7',
}, 'dvc3khto5WpjEPojZfYJsbJzFEPPVnRa');

console.log(container.data);
console.log(container.signature);
```

**Python**

```python showLineNumbers
import base64
import hashlib
import hmac
import json

def signed_container(fields: dict, form_secret: str) -> dict:
    # compact separators: the default ones add spaces after ',' and ':'
    body = json.dumps(fields, separators=(',', ':'))
    data = base64.urlsafe_b64encode(body.encode('utf-8')).decode().rstrip('=')

    return {
        'data': data,
        'signature': hmac.new(
            form_secret.encode('utf-8'), data.encode('utf-8'), hashlib.sha256
        ).hexdigest(),
    }

# Test values from the reference set. Before going live, 
# replace the secret with the form secret from your project settings
container = signed_container({
    'projectId':    'a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d',
    'amountFiat':   10.5,
    'currencyFiat': 'USD',
    'timeToPay':    1,
    'description':  'Order #7, delivery',
    'serviceData':  'order-7',
}, 'dvc3khto5WpjEPojZfYJsbJzFEPPVnRa')

print(container['data'])
print(container['signature'])
```

`separators` argümanı, referansla birebir eşleşme için gereklidir. Hizmet, varsayılan JSON ayarlarıyla da sorunsuz çalışır.

Örnek değerler ASCII'dir, ancak kod herhangi bir Unicode ile değişiklik gerekmeden çalışır. PHP ve Python, ASCII dışı karakterleri varsayılan olarak `\uXXXX` biçimine dönüştürür; JavaScript ham UTF-8 gönderir — hizmet ikisini de kabul eder.

## İki yaygın hata

**Kap yerine JSON imzalanır.** HMAC, `data` dizesi üzerinden, yani base64url üzerinden hesaplanır. Hata kendini “her şey eşleşiyor ama imza eşleşmiyor” şeklinde gösterir.

**Kap, imzalandıktan sonra yeniden oluşturulur.** `data` dizesinin tek bir baytı bile değişirse imza geçersizdir. Çifti, kodunuzda tek bir yerde oluşturun.

## İmza eşleşmiyorsa

JSON → `data` → imza zincirini adım adım izleyin; referanstan sapan ilk halka nedendir. Önce `data` dizesini kontrol edin: gizli anahtara bağlı değildir.
