PayPal - одна з найвідоміших у світі платіжних систем, що застосовуються для розрахунків в Інтернет
Платежі через PayPal безпечні, якщо грамотно використовувати надані можливості
види платежів
У статті будуть розглянуті два останніх методу. Також я не розглядаю метод, при якому кошик формується на нашому сайті, а потім весь вміст кошика передається PayPal.
процес оплати
Купівля "в один клік"
Код найпростішої форми:
Опис основних параметрів
Код валюти. Можливі значення: "USD", "EUR", "GBP", "YEN", "CAD". За замовчуванням "USD"
Тепер потрібно перевірити суму і валюту платежу. Така перевірка необхідна, так як потенційному зловмиснику не складає труднощів змінити суму в формі, В разі передплати, слід перевіряти всі параметри підписки (наявність, тривалість і вартість пробних періодів, тривалість і вартість основного циклу підписки, і т.д.).
IPN для однієї і тієї ж транзакції може відправлятися більше одного разу. Наприклад, якщо платіж по будь-якої причини був затриманий, перший IPN буде переданий відразу після платежу. Після того, як платіж буде завершено або скасований, буде відправлений другий IPN. Якщо ваш IPN-скрипт не повернув HTTP статус 200, PayPal повторить відправку IPN через деякий час. Перший повтор буде через 10 секунд, потім при необхідності через 20, потім через 40, 80 і т.д. (до 24 годин). Якщо протягом 4 діб очікувана відповідь від вашого скрипта не буде отриманий, спроби будуть припинені. Це можна використовувати для того, щоб не втратити дані про транзакції в разі виникнення помилки в вашому IPN скрипті. Наприклад, якщо скрипту не вдалося підключитися до бази даних, в якій він зберігає дані про транзакції, він може повернути HTTP статус 500, і IPN буде повторений пізніше. Повторний IPN буде відправлений також, якщо IPN-скрипт не вдається до сервера PayPal для перевірки транзакції.
Як видно з опису параметрів return, rm і notify_url. IPN може передваться двом скриптів, зазначеним в параметрах return і notify_url. Між ними 2 відмінності:- IPN для return буде відправлений тільки одноразово, безпосередньо після оплати. notify_url може викликатися кілька разів (див. попередній параграф).
- Висновок скрипта return буде показаний користувачеві. Зауважте, що якщо у висновку містяться посилання, то вони повинні бути абсолютними. Висновок скрипта notify_url в браузер користувача не виводиться.
В отриманих POST змінних міститься інформація про транзакції. Найбільш часто використовувані змінні:
Тип транзакції. Можливі значення:
"Web_accept" - оплата була зроблена з використанням кнопки "Buy Now"
"Cart" - оплата була зроблена з використанням вбудованої кошика PayPal
"Send_money" - оплата була зроблена з використанням функції "Send money"
"Reversal" - гроші були повернуті покупцеві за його ініціативою
Стан платежу. Можливі значення:
"Completed" - транзакція завершена успішно, гроші переведені на рахунок продавця. У разі txn_type = "reversal" означає, що гроші були повернуті на рахунок покупця
"Pending" - платіж затриманий. Причина затримки - у змінній pending_reason. Після того, як платіж буде завершено або скасований, PayPal відправить ще одне повідомлення.
"Failed" - платіж не пройшов. Цей стан можливо тільки якщо платіж осуществяется з банківського рахунку
"Denied" - платіж був скасований продавцем. Цей стан виникає при скасуванні продавцем платежу, стан якого було Pending
"Refunded" - гроші були повернуті покупцеві. Цей стан виникає при скасуванні продавцем платежу, стан якого було Completed
приклади скриптів
Оплата товарів в "кошику"
Реалізацію власне "кошика" я тут описувати не буду. Зауважу лише, що в нашому випадку сесії для зберігання вмісту кошика непридатні, так як ми не зможемо відновити дані сесії в нашому IPN-скрипті. Для визначення я вважаю що $ _COOKIE [ 'cart_id'] містить ідентифікатор кошика, за яким ми відрізняємо кошик одного користувача від іншого. Нехай вміст кошика у нас зберігається в базі даних MySQL, в таблиці з наступною структурою: Після оплати кошик покупця повинна бути очищена, і внесені записи в таблицю замовлень. Загальна інформація про замовлення буде зберігатися в таблиці orders Деталі замовлення будемо зберігати в таблиці order_details
Код скрипта, що виводить форму замовлення (checkout)
/ *
тут код, що підключається до бази даних
і виводить вміст кошика
* /
Код скрипта payment_success.php
/ *
тут код, що підключається до бази даних
* /
/ ********
переконаємося в тому, що ця транзакція не
була оброблена раніше
******** /
$ R = mysql_query ( "SELECT order_id FROM orders WHERE txn_id = '". $ _POST [ "txn_id"]. "'");
list ($ duplicate) = mysql_fetch_row ($ r);
mysql_free_result ($ r);
if ($ duplicate) die ( "I feel like I met you before.");
/ ********
перевіряємо суму платежу
******** /
$ Cart_id = intval ($ _POST [ 'item_number']);
$ R = mysql_query ( "SELECT sum (price * quantity), COUNT (cart_id) FROM cart
WHERE cart_id = ". $ Cart_id);
list ($ total. $ nitems) = mysql_fetch_row ($ r);
mysql_free_result ($ r);
if (! $ nitems) // не вдалося відновити вміст кошика
<
mail ($ adminemail. "IPN error". "Unable to restore cart contents \ r \ nCart ID:".
$ Cart_id. "\ R \ nTransaction ID:". $ _POST [ "txn_id"]);
die ( "I can not recall what you paid for. Please contact". $ adminemail);
>
if ($ total! = $ _POST [ "mc_gross"] || $ _POST [ "mc_currency"]! = $ currency)
<
mail ($ adminemail. "IPN error". "Payment amount mismatch \ r \ nCart ID:"
. $ Cart_id. "\ R \ nTransaction ID:". $ _POST [ "txn_id"]);
die ( "Out of money? Please contact". $ adminemail);
>
/ ********
перевірки завершені. формуємо замовлення
******** /
$ Order_date = date ( "Y-m-d H: i: s". Strtotime ($ _POST [ "payment_date"]));
mysql_query ( "INSERT INTO orders SET
txn_id = ' ". $ _POST [" txn_id "]."',
order_date = '$ order_date',
order_total = $ total.
email = ' ". $ _POST [" payer_email "]."',
first_name = ' ". mysql_escape_string ($ _POST [" first_name "])."',
last_name = ' ". mysql_escape_string ($ _POST [" last_name "])."',
street = ' ". mysql_escape_string ($ _POST [" address_street "])."',
city = ' ". mysql_escape_string ($ _POST [" address_city "])."',
state = ' ". mysql_escape_string ($ _POST [" address_state "])."',
zip = ' ". mysql_escape_string ($ _POST [" address_zip "])."',
country = ' ". mysql_escape_string ($ _POST [" address_country "])."' ");
$ Order_id = mysql_insert_id ();
$ R = mysql_query ( "SELECT * FROM cart WHERE cart_id =". $ Cart_id);
while ($ row = mysql_fetch_assoc ($ r))
<
mysql_query ( "INSERT INTO order_details SET
order_id = $ order_id.
item_id = ". $ row [ 'item_id'].",
price = ". $ row [ 'price'].",
quantity = ". $ row [ 'quantity']);
>
mysql_free_result ($ r);
mysql_query ( "DELETE FROM cart WHERE cart_id =". $ cart_id);
mail ($ adminemail. "New order". "New order \ r \ nOrder ID:". $ order_id. "\ r \ nTransaction ID:"
. $ _POST [ "txn_id"]);
/ *
повідомляємо, що замовлення прийняте, дякуємо за покупку і
пропонуємо купити ще що-небудь * /
?>
Використання параметра return зручно тим, що дозволяє відразу після платежу видати результат перевірки користувачу. Однак це не дає 100% впевненості в тому, що гроші були дійсно зараховані на наш рахунок. Наприклад, якщо покупець платить електронним чеком (e-check), гроші будуть зараховані тільки після обробки чека банком, причому зарахування не гарантоване. notify-url позбавлений цього недоліку, так як дозволяє відстежити момент фактичного надходження грошей. У наступному прикладі я покажу, як можна обробляти повторні IPN на прикладі підписки на контент.
Нехай є якийсь ресурс, до якого ми хочемо надати платний доступ. Оплата становить $ 10 в місяць, надається тиждень безкоштовного обмеженого доступу.
Дані про передплатників зберігаються в таблиці subscribers Скрипт, який перевіряє ім'я користувача і пароль, повинен буде використовувати поле limited, щоб визначити, чи слід надати користувачеві повний або обмежений доступ.
Код скрипта, що виводить форму підписки
Генерацію імені користувача та пароля ми надамо PayPal (usr_manage = 1). Після підписки, скрипт ipn.php отримає IPN (txn_type = subscr_signup). При зміні статусу підписки будуть відправлені додаткові IPN, ми будемо обробляти лише subscr_payment і subscr_eot.
Код скрипта ipn.php
/ *
тут код, що підключається до бази даних
* /