# HTML فارم کا دستخط

گاہک براؤزر میں ہی فارم کی کوئی بھی فیلڈ بدل سکتا ہے — مثال کے طور پر، کم رقم لکھ سکتا ہے۔ اسی لیے انوائس کی فیلڈز دستخط شدہ کنٹینر میں جاتی ہیں: سروس دستخط کا دوبارہ حساب لگاتی ہے اور اگر وہ مماثل نہ ہو تو انوائس نہیں بناتی۔

اسٹور کا سرور دستخط کا حساب لگاتا ہے۔ براؤزر تک صرف تیار `data` اور `signature` کا جوڑا پہنچتا ہے۔

## دستخط کس چیز پر ہوتا ہے

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

HMAC کا حساب `data` اسٹرنگ پر ہوتا ہے، JSON کے بائٹس پر نہیں۔ وصول کنندہ دستخط کو بھیجی گئی اسٹرنگ سے جوں کا توں جانچتا ہے، اور صرف اس کے بعد اسے ڈی کوڈ کرتا ہے۔

اس سے یہ نتیجہ نکلتا ہے کہ کوئی canonicalization نہیں ہوتی: کلیدوں کی ترتیب، JSON کی انڈینٹیشن اور Unicode escaping کا طریقہ دستخط پر اثر نہیں ڈالتے۔ کوئی بھی معیاری JSON انکوڈر کام کرتا ہے۔

## حوالہ جاتی سیٹ

یہ قدریں اپنے کوڈ میں ڈالیں اور نتیجے کا موازنہ کریں۔

مثال کی خفیہ کلید:

```
dvc3khto5WpjEPojZfYJsbJzFEPPVnRa
```

رقم اور ادائیگی کی مہلت نمبرز کی صورت میں؛ اسٹرنگز بھی قبول ہیں:

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

`data` اسٹرنگ انہی بائٹس کا base64url ہے۔ یہاں پیڈنگ ہٹی ہوئی ہے؛ وصول کنندہ اسے پیڈنگ کے ساتھ بھی قبول کرتا ہے:

```
eyJwcm9qZWN0SWQiOiJhMWIyYzNkNC01ZTZmLTRhN2ItOGM5ZC0wZTFmMmEzYjRjNWQiLCJhbW91bnRGaWF0IjoxMC41LCJjdXJyZW5jeUZpYXQiOiJVU0QiLCJ0aW1lVG9QYXkiOjEsImRlc2NyaXB0aW9uIjoiT3JkZXIgIzcsIGRlbGl2ZXJ5Iiwic2VydmljZURhdGEiOiJvcmRlci03In0
```

اوپر والی خفیہ کلید کے ساتھ اس اسٹرنگ کا دستخط:

```
09628020c47400c9231420450af642adfdb3c246f33e3e12cb8e28f96c2a785e
```

ان قدروں کے ساتھ نیچے کی مثالیں بالکل یہی `data` اور دستخط پرنٹ کرتی ہیں۔ یہی JSON، آخر میں `"output":"errors"` کلید کے ساتھ، ایک مختلف `data` اسٹرنگ اور مختلف دستخط دیتا ہے۔

اگر JSON ایک جیسا نظر آنے کے باوجود `data` مماثل نہیں ہے، تو وجہ JSON انکوڈر ہے: کلیدوں کی مختلف ترتیب یا فاصل کے بعد خالی جگہیں۔ وصول کنندہ ایسا کنٹینر قبول کرتا ہے، لیکن وہ حوالے سے حرف بہ حرف مماثل نہیں ہوتا۔

## مثالیں

**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**

یہ اسٹور کے سرور کا کوڈ ہے، براؤزر کا نہیں: دستخط کا حساب وہیں لگتا ہے جہاں خفیہ کلید محفوظ ہے۔

```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` آرگیومنٹ ضروری ہے۔ وصول کنندہ JSON کی ڈیفالٹ سیٹنگز کے ساتھ بھی ٹھیک کام کرتا ہے۔

مثال کی قدریں ASCII ہیں، لیکن کوڈ بغیر کسی تبدیلی کے کسی بھی Unicode کے ساتھ کام کرتا ہے۔ PHP اور Python بطور ڈیفالٹ غیر ASCII حروف کو `\uXXXX` کی صورت میں escape کرتے ہیں، جبکہ JavaScript خام UTF-8 بھیجتا ہے — وصول کنندہ دونوں قبول کرتا ہے۔

## دو عام غلطیاں

**کنٹینر کے بجائے JSON پر دستخط ہوتا ہے۔** HMAC کا حساب `data` اسٹرنگ پر ہوتا ہے، یعنی base64url پر۔ یہ غلطی «سب کچھ مماثل ہے، لیکن دستخط نہیں» جیسی نظر آتی ہے۔

**دستخط کے بعد کنٹینر دوبارہ بنایا جاتا ہے۔** اگر `data` کا ایک بائٹ بھی بدل جائے، تو دستخط غلط ہو جاتا ہے۔ جوڑا اپنے کوڈ میں ایک ہی جگہ بنائیں۔

## اگر دستخط مماثل نہ ہو

JSON سے `data` تک اور `data` سے دستخط تک کی زنجیر کو مرحلہ وار جانچیں؛ حوالے سے سب سے پہلے الگ ہونے والی کڑی ہی وجہ ہے۔ پہلے `data` جانچیں: یہ خفیہ کلید پر منحصر نہیں۔
