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:
{"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
- TypeScript
- JavaScript
- Python
<?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";
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);
Bu, tarayıcı için değil mağazanın sunucusu için koddur: imza, gizli anahtarın saklandığı yerde hesaplanır.
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);
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.