إنتقل إلى المحتوى الرئيسي

توقيع نموذج HTML

يستطيع المشتري تغيير أي حقل في النموذج مباشرة في المتصفح — مثلًا وضع مبلغ أقل. لهذا تنتقل حقول الفاتورة في حاوية موقَّعة: تعيد الخدمة حساب التوقيع، وإذا لم يتطابق، لا تنشئ الفاتورة.

خادم المتجر هو الذي يحسب التوقيع. ولا يذهب إلى المتصفح إلا الزوج الجاهز data وsignature.

ما الذي يُوقَّع

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

يُحسب HMAC على السلسلة data، لا على بايتات JSON. تتحقق جهة الاستقبال من التوقيع مقابل السلسلة المرسلة كما هي، ثم تفكّ ترميزها بعد ذلك فقط.

يترتب على ذلك أنه لا وجود لصيغة قانونية موحدة: ترتيب المفاتيح وإزاحة JSON وأسلوب تهريب Unicode لا تؤثر في التوقيع. وأي مرمّز JSON قياسي يفي بالغرض.

المجموعة المرجعية

ضع هذه القيم في كودك وقارن الناتج.

المفتاح السري في المثال:

dvc3khto5WpjEPojZfYJsbJzFEPPVnRa

المبلغ ومهلة الدفع كأرقام؛ وتُقبل السلاسل النصية أيضًا:

{"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 مختلفة وتوقيعًا مختلفًا.

إذا لم تتطابق data بينما يبدو JSON نفسه، فمرمّز JSON هو السبب: ترتيب مفاتيح مختلف أو مسافات بعد الفواصل. ستقبل جهة الاستقبال حاوية كهذه، لكنها لن تطابق المرجع حرفيًا.

أمثلة

<?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";

قيم المثال بترميز ASCII، لكن الكود يعمل مع أي Unicode من دون تغيير. يهرّب PHP وPython ما ليس ASCII إلى \uXXXX افتراضيًا، بينما يرسل JavaScript بايتات UTF-8 الخام — وتقبل جهة الاستقبال كليهما.

خطآن شائعان

يُوقَّع JSON بدل الحاوية. يُحسب HMAC على السلسلة data، أي على base64url. يبدو الخطأ بالشكل «كل شيء متطابق، لكن التوقيع لا يتطابق».

يُعاد بناء الحاوية بعد التوقيع. إذا تغيّر بايت واحد من data، بطل التوقيع. ابنِ الزوج في مكان واحد من كودك.

إذا لم يتطابق التوقيع

تتبَّع السلسلة من JSON إلى data إلى التوقيع؛ أول حلقة تنحرف عن المرجع هي السبب. تحقق من data أولًا: فهي لا تعتمد على المفتاح السري.