Вобщем поставили передо мной на работе задачу организовать прием платежей в наш проект через Paypal. От меня требовалось дать все необходимые данные для настройки аккаунта (сам аккаунт настраивался другим человеком), ну и, собственно, написать скрипт, который и будет принимать данные от paypal-а и начислять деньги юзерам.
Ничего толкового по этой теме на хабре я не нашел. Пришлось разбираться со всем самому. Ниже – результаты этих разбирательств:)
Paypal позволяет принимать несколько видов платежей. Вот некоторые из них:
1. Buy now buttons – единоразовый платеж, для оплаты одного или нескольких товаров (услуг). Позволяет задавать сумму платежа, описание товаров (услуг), количество товаров, адрес, доставки, вес покупки и т.д. Сумму платежа можно и не задавать, в этом случае Paypal даст юзеру возможность самостоятельно указать сумму, которую хочет заплатить.
2. Donate buttons – впринципе, по функционалу аналогична «Buy now», тоже единоразовый платеж, сумму можно задать как принудительно, так и оставить на усмотрение юзера. Отоичие в том, что не позволяет указать адрес доставки и все, что с ней (доставкой) связано.
3. Add to cart buttons – позволяет создавать корзину из ваших товаров, на стороне Paypal`а. На вашем сайте юзер может только добавлять товары в корзину. Чтобы просмотреть содержимое корзины или удалить оттуда какие-либо позиции, придется залогиниться на Paypal.
4. Subscribe buttons – позволяет организовать прием периодических платежей, например, оплата аккаунта, услуг.
5. Еслть еще подарочные сертификаты, но с ними даже не пытался разобраться.
В моем случае идеальным решением было использование платежей “Buy now”, о них и пойдет дальше речь.
Заюзать “Buy now” на своем сайте можно двумя способами:
1. создать кнопку c помощью инструментов Paypal’а.
2. создать свою собственную форму.
Лично я воспользовался вторым вариантом, хотя бы потому, что это позволяет использовать свой собственный дизайн. Хотя есть и еще пара плюшек, о них позже.
Рaypal, что естественно, жестко регламентирует названия полей формы. Полный список этих полей можно найти на cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_html_Appx_websitestandard_htmlvariables. То что использовано у нас:
— cmd = _xclick – указываем тип платежа “Buy now”;
— business – тут указываем email, аккаунта, куда будут приниматься платежи. Сам адрес должен быть подтвержденным (подтверждается где-то в настройках аккаунта);
— item_name – тут задаем описание товара/услуги. Будет отображаться в Paypal при проведении платежа;
— custom – здесь служебная инфа, понадобится потом нам же для идентификации юзера;
— amount – сумма платежа;
— currency – валюта платежа. Возможные варианты Здесь.
— no_shipping =1 – указываем, что доствка не осуществляется.
Далее, для приема платежей на свой аккаунт, этот самый аккаунт надо соответствующим образом настроить.
Paypal поддерживает два метода передачи данных о транзакции нашему скрипту: PDT (Payment Data Transfer) и IPN (Instant Payment Notification). Насколько я понял, разница в том, что при использовании PDT от Paypal’а приходит одно единственное сообщение уже после совершения юзером платежа (т.е. когда деньги от юзера уже находятся на пути к вашему аккаунту). При использовании IPN Paypal генерирует несколько сообщений, оповещая нас о каждой отдельной стадии совершения платежа. Для решения моей задачи вполне хватало PDT, который я и заюзал.
Процесс включения PDT описан Здесь. Там внизу есть раздел Activating PDT.
Суть PDT — при завершении платежа, Paypalпосылает указанному скрипту GET- зарпос, в котором передает номер транзакции, её статус, сумму и т.д. У самого Paypal’a существует механизм проверки подлинности транзакции — мы отправляем POST-запрос определенного вида на их адрес с полученным номером транзакции. В ответ приходит либо код ошибки, либо описание транзакции — статус, сумма и еще куча всяких служебных данных.
Собственно, что требуется от скрипта:
1. получить эти данные.
2. проверить тип транакции — если используется обычная оплата (buy now). то тип транзакции должен быть web_accept;
3. проверяем емайл получателя платежа и id аккаунта получателя. (поля bussiness, receiver_email, receiver_id);
4. в поле custom содержится служебная инфа полученная от нас, например
ид юзера — проверяем его.
5. в поле txn_id содежится номер транакции в системе paypal. проверяем
чтобы не было повторных платежей.
6. после этого, если поле payment_status = Complete, т.е. платеж
нормально завершен, то проводим платеж уже у себя, при любом другом
статусе — какие-то траблы.
Сумма и валюта будут в полях mc_gross и mc_currency.
Полный список все параметров передаваемых Paypal'ом при использовании PDT (да и при IPN тоже) Здесь.
Теперь о тех плюшках, которые обещал раньше. Относятся они к полю custom, которое мы сами и отправляем Paypal’y. Можно, конечно, просто записать туда id юзера и не париться, но мы ведь легких путей не ищем. В это поле вполне можно записать id записи с описанием платежа в нашей системе, а уже там сохраняться юзера, запоминать тип услуги, к которую приобрел юзер, сумму и т.д. Это позволит, например, отслеживать покупки, которые так и не были оплачены, а также, при получении данных от Paypal’a, можно сверять с сохраненными у нас, что дает небольшой + к секьюрности.
Ну и, напоследок, немного кода
Ничего толкового по этой теме на хабре я не нашел. Пришлось разбираться со всем самому. Ниже – результаты этих разбирательств:)
Виды платежей
Paypal позволяет принимать несколько видов платежей. Вот некоторые из них:
1. Buy now buttons – единоразовый платеж, для оплаты одного или нескольких товаров (услуг). Позволяет задавать сумму платежа, описание товаров (услуг), количество товаров, адрес, доставки, вес покупки и т.д. Сумму платежа можно и не задавать, в этом случае Paypal даст юзеру возможность самостоятельно указать сумму, которую хочет заплатить.
2. Donate buttons – впринципе, по функционалу аналогична «Buy now», тоже единоразовый платеж, сумму можно задать как принудительно, так и оставить на усмотрение юзера. Отоичие в том, что не позволяет указать адрес доставки и все, что с ней (доставкой) связано.
3. Add to cart buttons – позволяет создавать корзину из ваших товаров, на стороне Paypal`а. На вашем сайте юзер может только добавлять товары в корзину. Чтобы просмотреть содержимое корзины или удалить оттуда какие-либо позиции, придется залогиниться на Paypal.
4. Subscribe buttons – позволяет организовать прием периодических платежей, например, оплата аккаунта, услуг.
5. Еслть еще подарочные сертификаты, но с ними даже не пытался разобраться.
В моем случае идеальным решением было использование платежей “Buy now”, о них и пойдет дальше речь.
Заюзать “Buy now” на своем сайте можно двумя способами:
1. создать кнопку c помощью инструментов Paypal’а.
2. создать свою собственную форму.
Лично я воспользовался вторым вариантом, хотя бы потому, что это позволяет использовать свой собственный дизайн. Хотя есть и еще пара плюшек, о них позже.
Создание формы платежа
Рaypal, что естественно, жестко регламентирует названия полей формы. Полный список этих полей можно найти на cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_html_Appx_websitestandard_htmlvariables. То что использовано у нас:
— cmd = _xclick – указываем тип платежа “Buy now”;
— business – тут указываем email, аккаунта, куда будут приниматься платежи. Сам адрес должен быть подтвержденным (подтверждается где-то в настройках аккаунта);
— item_name – тут задаем описание товара/услуги. Будет отображаться в Paypal при проведении платежа;
— custom – здесь служебная инфа, понадобится потом нам же для идентификации юзера;
— amount – сумма платежа;
— currency – валюта платежа. Возможные варианты Здесь.
— no_shipping =1 – указываем, что доствка не осуществляется.
Настройка аккаунта
Далее, для приема платежей на свой аккаунт, этот самый аккаунт надо соответствующим образом настроить.
Paypal поддерживает два метода передачи данных о транзакции нашему скрипту: PDT (Payment Data Transfer) и IPN (Instant Payment Notification). Насколько я понял, разница в том, что при использовании PDT от Paypal’а приходит одно единственное сообщение уже после совершения юзером платежа (т.е. когда деньги от юзера уже находятся на пути к вашему аккаунту). При использовании IPN Paypal генерирует несколько сообщений, оповещая нас о каждой отдельной стадии совершения платежа. Для решения моей задачи вполне хватало PDT, который я и заюзал.
Процесс включения PDT описан Здесь. Там внизу есть раздел Activating PDT.
Суть PDT — при завершении платежа, Paypalпосылает указанному скрипту GET- зарпос, в котором передает номер транзакции, её статус, сумму и т.д. У самого Paypal’a существует механизм проверки подлинности транзакции — мы отправляем POST-запрос определенного вида на их адрес с полученным номером транзакции. В ответ приходит либо код ошибки, либо описание транзакции — статус, сумма и еще куча всяких служебных данных.
Алгоритм работы скрипта
Собственно, что требуется от скрипта:
1. получить эти данные.
2. проверить тип транакции — если используется обычная оплата (buy now). то тип транзакции должен быть web_accept;
3. проверяем емайл получателя платежа и id аккаунта получателя. (поля bussiness, receiver_email, receiver_id);
4. в поле custom содержится служебная инфа полученная от нас, например
ид юзера — проверяем его.
5. в поле txn_id содежится номер транакции в системе paypal. проверяем
чтобы не было повторных платежей.
6. после этого, если поле payment_status = Complete, т.е. платеж
нормально завершен, то проводим платеж уже у себя, при любом другом
статусе — какие-то траблы.
Сумма и валюта будут в полях mc_gross и mc_currency.
Полный список все параметров передаваемых Paypal'ом при использовании PDT (да и при IPN тоже) Здесь.
Теперь о тех плюшках, которые обещал раньше. Относятся они к полю custom, которое мы сами и отправляем Paypal’y. Можно, конечно, просто записать туда id юзера и не париться, но мы ведь легких путей не ищем. В это поле вполне можно записать id записи с описанием платежа в нашей системе, а уже там сохраняться юзера, запоминать тип услуги, к которую приобрел юзер, сумму и т.д. Это позволит, например, отслеживать покупки, которые так и не были оплачены, а также, при получении данных от Paypal’a, можно сверять с сохраненными у нас, что дает небольшой + к секьюрности.
Ну и, напоследок, немного кода
- <?php
- //данная функция юзается для получения данных о транакции от Paypal'a
- //код самой функции взят с сайта Paypal'a там где-то есть примеры на различных языках
- function get_paypal_data($tx_token, $auth_token){
- $req = 'cmd=_notify-synch';
- $req .= "&tx=$tx_token&at=$auth_token";
- // post back to PayPal system to validate
- $header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
- $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
- $header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
- $fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);
- if (!$fp) {
- // HTTP ERROR
- return false;
- } else {
- fputs ($fp, $header . $req);
- // read the body data
- $res = '';
- $headerdone = false;
- while (!feof($fp)) {
- $line = fgets ($fp, 1024);
- if (strcmp($line, "\r\n") == 0) {
- // read the header
- $headerdone = true;
- }elseif ($headerdone){
- // header has been read. now read the contents
- $res .= $line;
- }
- }
- }
- return $res;
- }
- //из GET'a берем только id транакции, все остальное нас не интересует, эти данные мы получим от Paypal'a в ответе на наш запрос
- $tx = @$_GET['tx'];
- if (!$tx){
- error_log('[paypal] ERROR: Tx are absent!!!');
- return false;
- }
- //отправляем запрос Paypal'y для получения данных о транзакции
- //PAYPAL_IDENTITY_TOKEN - константа с токеном, полученным при активации PDT
- $res = get_paypal_data($tx, PAYPAL_IDENTITY_TOKEN);
- $history .= ('[paypal] post request result for '.$tx."\n".$res);
- if (!$res){
- $history .= ('[paypal] http error for '.$tx);
- exit();
- }
- //парсим ответ
- $strs = explode("\n", $res);
- if ($strs[0] == 'FAIL'){
- //если первая строка FAIl - значит какая-то трабла
- //обрабатываем ошибку как вам требуется и выходим
- exit();
- } elseif ($strs[0] == 'SUCCESS'){
- //вроде все нормально - парсим остальные параметры
- $res_vars = array();
- //parse paypal answer to array where key it's varname
- for($i=1; $i<count($strs);$i++){
- $strs[$i] = trim($strs[$i]);
- if (!$strs[$i]) continue;
- $vars = explode('=', $strs[$i]);
- if (!$vars || count($vars) != 2){
- //какая-то трабла - переходим к следующему параметру
- continue;
- }
- $res_vars[$vars[0]] = $vars[1];
- }
- //в итоге в массиве res_vars у нас находятся все параметры транзакции
- //в поле txn_type - содердится тип транзакции. нас интересует только web_accept
- switch ($res_vars['txn_type']){
- case 'web_accept':
- //тут мы проверяем все остальные поля: bussiness, receiver_id...
- // при необходимости парсим поле custom
- // если все нормально, то проверяем состояние платежа
- switch (@$res_vars['payment_status']){
- case 'Completed':
- //все нормально - отправляем юзеру товар, начисляем деньги на виртуальный счет и т.д.
- break;
- }
- break;
- }
- }
- ?>