Интеграция WayForPay в PHP: формуляр за плащане, подпис и обратно извикване
WayForPay е практичният начин за приемане на картови плащания в UAH, USD и EUR от украински бизнес. Това ръководство преминава през пълната интеграция в PHP: подписване на формуляра за плащане, получаване на обратното извикване на сървъра, отговаряне правилно и потвърждаване на статуса на поръчката.
Накратко
- Потокът има три части: подписан формуляр за плащане, който изпраща клиента към WayForPay, обратно извикване от сървър към сървър (
serviceUrl) и вашата собствена страница със статус на поръчка. - Всяка заявка и отговор се подписват с HMAC_MD5 с помощта на вашия таен ключ на търговеца; редът на полето има значение.
- Маркирайте поръчка като платена само от потвърдено обратно извикване или отговор
CHECK_STATUS, никога от клиента, който се връща на вашия сайт. - Трябва да отговорите на обратното извикване с подписан JSON отговор „приемам“, в противен случай WayForPay продължава да опитва отново.
- Съхранявайте суми, валута и справка за поръчка във вашата база данни и ги сравнявайте с това, което казва обратното извикване.
Интеграцията WayForPay в PHP е малко количество код, но има няколко места, където тиха грешка означава или неплатени поръчки, маркирани като платени, или платени поръчки, които никога не се изпълняват. Ние провеждаме плащане с WayForPay на нашите собствени сайтове, включително платените услуги на този уебсайт, и сме го свързали с няколко клиентски магазина. По-долу е структурата, която използваме, с подробностите, които документацията изброява, но хората са склонни да преглеждат.
Как работи потокът на плащане
- Клиентът натиска „Плати“. Вашият сървър създава поръчка в базата данни с уникален
orderReference, фиксирана сума и валута. - Вашият сървър изгражда полетата на формуляра за плащане, подписва ги и изобразява HTML формуляр за автоматично изпращане, който се публикува на страницата за плащане WayForPay.
- Клиентът плаща на страницата WayForPay (карта, Apple Pay, Google Pay и други методи, активирани във вашия акаунт на търговец).
- WayForPay извиква вашия
serviceUrlс резултата от транзакцията. Това обаждане от сървър към сървър е това, на което имате доверие. - Клиентът се пренасочва към вашия
returnUrl, където показвате текущия статус на поръчката.
Стъпка 5 може да се случи преди стъпка 4 да завърши, така че на страницата за връщане трябва да пише „потвърждаване на вашето плащане“, докато обратното извикване не бъде обработено.
Какво ви трябва от акаунта на търговеца
merchantAccount- вашето логин за търговец.merchantSecretKey- тайната, използвана за подписи. Съхранявайте го в променлива на средата или в непроследен конфигурационен файл, никога в git или в преден код.merchantDomainName- домейнът на вашия сайт, както е регистриран в акаунта.- Валутите и методите на плащане, активирани за вашия акаунт.
Стъпка 1. Изградете и подпишете формуляра за плащане
Подписът е HMAC с MD5 върху избрани полета, съединени с точка и запетая. За покупка поръчката е: акаунт на търговец, име на домейн на търговец, справка за поръчката, дата на поръчката (клеймо за време на Unix), сума, валута, след това всички имена на продукти, всички брои продукти и всички цени на продуктите.
function wfpSignature(array $parts, string $secret): string
{
return hash_hmac('md5', implode(';', $parts), $secret);
}
$orderReference = 'ORD-' . $orderId . '-' . time();
$orderDate = time();
$amount = '50.00'; // string with a dot, the same value everywhere
$currency = 'EUR';
$products = [['name' => 'Consultation, 1 hour', 'count' => 1, 'price' => '50.00']];
$parts = [$merchantAccount, $merchantDomain, $orderReference, $orderDate, $amount, $currency];
foreach ($products as $p) { $parts[] = $p['name']; }
foreach ($products as $p) { $parts[] = (string) $p['count']; }
foreach ($products as $p) { $parts[] = $p['price']; }
$signature = wfpSignature($parts, $secretKey);
След това изобразете формата. Продуктовите полета са масиви, така че използват суфикса []:
<form id="wfp" method="post" action="https://secure.wayforpay.com/pay" accept-charset="utf-8">
<input type="hidden" name="merchantAccount" value="...">
<input type="hidden" name="merchantAuthType" value="SimpleSignature">
<input type="hidden" name="merchantDomainName" value="example.com">
<input type="hidden" name="merchantSignature" value="<?= htmlspecialchars($signature) ?>">
<input type="hidden" name="orderReference" value="<?= htmlspecialchars($orderReference) ?>">
<input type="hidden" name="orderDate" value="<?= $orderDate ?>">
<input type="hidden" name="amount" value="50.00">
<input type="hidden" name="currency" value="EUR">
<input type="hidden" name="productName[]" value="Consultation, 1 hour">
<input type="hidden" name="productCount[]" value="1">
<input type="hidden" name="productPrice[]" value="50.00">
<input type="hidden" name="returnUrl" value="https://example.com/pay/return">
<input type="hidden" name="serviceUrl" value="https://example.com/pay/callback">
</form>
<script>document.getElementById('wfp').submit();</script>
Най-честите грешки в подписа са: различен формат на числата в подписа и формуляра (50 срещу 50.00), име на продукт, което е променено (подрязано, HTML-escaped) след подписване и забравяне, че редът на полетата е фиксиран.
Стъпка 2. Получете обратното извикване на serviceUrl
Когато плащането достигне крайно състояние, WayForPay публикува резултата в serviceUrl. Прочетете необработеното тяло (това е JSON; някои настройки доставят и полета на формуляр, така че обработвайте и двете) и проверете подписа. За обратното извикване се изчислява върху: акаунт на търговец, справка за поръчка, сума, валута, код за оторизация, маска на картата, състояние на транзакция и код на причина.
$raw = file_get_contents('php://input');
$data = json_decode($raw, true) ?: $_POST;
$line = implode(';', [
$data['merchantAccount'] ?? '',
$data['orderReference'] ?? '',
$data['amount'] ?? '',
$data['currency'] ?? '',
$data['authCode'] ?? '',
$data['cardPan'] ?? '',
$data['transactionStatus'] ?? '',
$data['reasonCode'] ?? '',
]);
$expected = hash_hmac('md5', $line, $secretKey);
if (!hash_equals(strtolower($expected), strtolower((string) ($data['merchantSignature'] ?? '')))) {
http_response_code(400);
exit;
}
Използвайте hash_equals за сравнението, за да избегнете изтичане на времето. Едва след като подписът е валиден, трябва да потърсите поръчката и да сравните amount и currency с това, което сте съхранили. Ако се различават, регистрирайте го и не отбелязвайте поръчката като платена.
Стъпка 3. Актуализирайте реда и отговорете правилно
Полето transactionStatus ви казва какво се е случило: Approved означава, че парите са уловени; Declined, Expired и подобни стойности означават неуспех; Pending или InProcessing означава изчакване; Refunded и Voided означават, че парите са върнати. Актуализирайте поръчката си съответно в транзакция и я направете идемпотентна: ако поръчката вече е платена, не правете нищо и пак отговорете успешно.
WayForPay изисква подписан отговор, за да знае, че сте получили съобщението. Подписът е над препратката към поръчката, думата accept и текущия час:
$time = time();
$status = 'accept';
$reply = [
'orderReference' => $data['orderReference'],
'status' => $status,
'time' => $time,
'signature' => hash_hmac('md5', "{$data['orderReference']};{$status};{$time}", $secretKey),
];
header('Content-Type: application/json');
echo json_encode($reply);
Ако вашият сървър не отговори по този начин, WayForPay третира доставката като неуспешна и прави повторен опит, което означава, че ще получите едно и също обратно извикване няколко пъти. Това е друга причина за проверката за идемпотентност.
Стъпка 4. Потвърдете състоянието с CHECK_STATUS
Обратните повиквания могат да бъдат пропуснати: сървърът ви не работи, защитна стена блокира заявката, разгръщане прекъсна маршрута. WayForPay предлага извикване на API CHECK_STATUS, което връща текущото състояние на поръчка чрез нейната препратка. Използвайте го на две места: на страницата за връщане, ако обратното извикване не е пристигнало след няколко секунди, и в малка cron задача, която проверява повторно поръчки, останали в „чакане“ за повече от, да речем, петнадесет минути. Заявката е подписана с HMAC_MD5 през сметка на търговец и справка за поръчка.
$payload = [
'transactionType' => 'CHECK_STATUS',
'merchantAccount' => $merchantAccount,
'orderReference' => $orderReference,
'merchantSignature' => hash_hmac('md5', "{$merchantAccount};{$orderReference}", $secretKey),
'apiVersion' => 1,
];
$ch = curl_init('https://api.wayforpay.com/api');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
]);
$result = json_decode((string) curl_exec($ch), true);
Приложете същите проверки като за обратно извикване: проверете подписа на отговора, когато е налице, сравнете сумата и валутата и актуализирайте поръчката идемпотентно.
Тестване преди пускане на живо
- Използвайте тестовия режим и тестовите карти от документацията WayForPay, докато пълно плащане, отказ и възстановяване не се видят във вашите регистрационни файлове.
- Вашият
serviceUrlтрябва да бъде достъпен от интернет през HTTPS. На localhost използвайте тунел или разположете в етапен домейн. - Регистрирайте всяко обратно извикване с неговото необработено тяло, резултата от проверката и изхода. Никога не регистрирайте секретния ключ.
- Уверете се, че защитата на WAF или бот (например в Cloudflare) не предизвиква или блокира заявките за обратно извикване на WayForPay.
Често срещани грешки
- Доверяване на обратния URL адрес. Клиентът може да го отвори без да плаща.
- Повторно използване на препратка към поръчка. Всеки опит се нуждае от уникална препратка или ще объркате опити.
- Промяна на сумата след подписване или показване на една цена и подписване на друга.
- Без отговор на обратното повикване с подписан отговор за приемане.
- Маркирайте като платено, преди да проверите сумата и валутата.
- Няколко валути, една споделена тайна и без картографиране: запазете валутата в реда и я проверете при обратно извикване.
Как можем да помогнем
Изграждаме интеграции за плащане от край до край: WayForPay, Stripe, PayPal, периодично фактуриране, възстановяване на средства и отчети за съгласуване. Ако вече имате работещо плащане, но не сте сигурни за неговите режими на отказ, резервирайте анализ на архитектурата и ние ще прегледаме обработката на обратно извикване, идемпотентността и регистрирането. За подобно ръководство на друг доставчик вижте Stripe webhooks в PHP, а за цялостния подход прочетете нашия наръчник за интегриране при стартиране.
Често задавани въпроси
Какъв подпис използва WayForPay ?
HMAC_MD5 върху избрани полета, съединени с точка и запетая, с помощта на секретния ключ на търговеца. Редът на полетата е фиксиран и се различава за формата за плащане, обратното повикване и отговора за обратно повикване.
Какво трябва да върне крайната точка serviceUrl?
JSON отговор с orderReference, състоянието „accept“, текущото Unix време и подпис, изчислен като HMAC_MD5 на „orderReference;accept;time“. Без него WayForPay прави повторен опит за обратно извикване.
Мога ли да се доверя на страницата returnUrl, за да маркира поръчка като платена?
Не. Браузърът на клиента може да отвори този URL адрес, без да плаща. Маркирайте поръчките като платени само след потвърдено обратно извикване на serviceUrl или отговор CHECK_STATUS със статус Одобрено и съответстваща сума и валута.
Как да проверя статуса на поръчка WayForPay ?
Изпратете подписана заявка CHECK_STATUS до API WayForPay с вашия акаунт на търговец и препратка към поръчката. Използвайте го на страницата за връщане и в задание на cron, което проверява повторно поръчки, останали в очакване.
Защо WayForPay казва, че подписът е невалиден?
Обикновено форматът на сумата се различава между подписа и формуляра (50 срещу 50,00), името на продукта е променено след подписването, редът на полетата е грешен или е използван грешен таен ключ.
Безплатно обаждане за откриване
Донесете разхвърлян стек или блокирала интеграция - Integrono ще набележи най-умния първи ход.
Получавате експертна оценка на бюджетни капани, зависимости и дали да купите, слепите или изградите - преди да започне платената работа. CEO остава в контура.