Pull to refresh

Знакомимся с PayPal Standard Checkout

Reading time 8 min
Views 45K
Подключение PayPal Standard Checkout

В данном руководстве последовательно описан мой опыт внедрения PayPal Standard Checkout с использованием языка Java на платформе Google App Engine. Данная статья рассчитана на людей уже имеющих опыт работы с облачной платформой GAE.

Задача

Потребовалось мне интегрировать платёжную систему PayPal на сайт собственного проекта который будет предоставлять сервис по подписке. Начав работу с PayPal Express Checkout API через некоторое время пришло осознание того что система приёма платежа становится слишком громоздкой, в то время как у готовых кнопок Standard Checkout отсутствует необходимая гибкость, которая требуется в случае интеграции сайта с другими платёжными системами.
Выход был найден в использовании инструментов Standard Checkout которые предоставляет PayPal разработчикам сторонних “корзин” для сайта.



Изменение типа счёта

Для того чтобы начать принимать платежи с помощью платёжной системы PayPal нам первым делом требуется произвести изменение типа счёта. При первой регистрации в PayPal нам даётся стандартный тип счёта — “Personal”, по нему можно лишь оплачивать товары и услуги. Для автоматизации приёма платежей необходим тип счёта “Premier” либо “Business”. Чем отличаются типы счёта можно посмотреть в таблице сравнения. В целом отличие в том что счёт “Business” предоставляет использование счёта разными пользователями. Поскольку данная особенность необходима лишь на больших предприятиях выбираем тип “Premier”. Тип счёта указан в левом верхнем углу под словами “Добро пожаловать...”

Уведомление о поступившем платеже

Существуют два способа уведомления о платеже — Payment Data Transfer (PDT) и Instant Payment Notification (IPN). Поскольку у IPN есть такие преимущества как асинхронная работа, то выбираем именно этот способ. Более подробная информация находится в Order Management Integration Guide

Отключение PDT:

“PayPal” — “Профиль” — “Мои инструменты продаж” — “Настройки веб-сайта” — “Передача сведений о платеже (необязательно)” — Выкл.

Активация IPN:

“PayPal” — “Профиль” — “Мои инструменты продаж” — “Уведомления о мгновенных платежах” — “Изменить параметры” — “”Принимать IPN-сообщения (Включено)” и добавить “URL-адрес для уведомления””

Возвращение покупателя на сайт продавца

После оплаты покупки в мерчанте PayPal, покупателя рекомендуется автоматически направить на страницу уведомления об успешно проведённом платеже или на сайт продавца. “PayPal” — “Профиль” — “Мои инструменты продаж” — “Настройки веб-сайта”.
“Автоматический возврат” — Вкл. и указать “url возврата”.

Создание счетов в PayPal песочнице

Для тестирования процесса приёма платежей существует так называемая песочница — sandbox. За операциями в песочнице не стоят реальные денежные средства, мы оперируем просто цифрами на созданных виртуальных тестовых счетах. После регистрации на PayPal Sandbox переходим в раздел “Test accounts” и создаём два “Preconfigured” счёта — один представляет собой покупателя (Buyer), другой представляет собой продавца (Seller). При создании счёта Seller его типом по умолчанию является тип “Business”. В тестировании разница между Premier и Business отсутствует. При создании счёта рекомендуется вписать любую сумму в валюте открываемого счёта. Эта сумма будет расходоваться или добавляться в зависимости от операций между созданными счетами. Автоматически созданный email и пароль рекомендуется записать поскольку эти реквизиты являются доступом к счетам в песочнице по адресу www.sandbox.paypal.com

Указание типа кодировки

Работу с данными производим в кодировке UTF-8. Для смены подировки переходим по адресу https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_profile-language-encoding Далее выбираем язык вебсайта, сохраняем его, возвращаемся назад и после нажатия кнопки «Дополнительные возможности» («More Options») выбираем необходимую кодировку.

Получение документации

Работа с мерчантом PayPal возможна двумя способами:
С использованием PayPal Express Checkout API и с использованием Standard Checkout.
Последний проще и лучше всего подходит для получения платежей за небольшой набор фиксированных товаров или услуг. его и выбираем. Для более детального ознакомления с данным способом желательно загрузить Standard Checkout Integration Guide

Кроме того, абсолютно вся документация в формате PDF и HTML находится по ссылке

Получения платежа

Принцип работы PayPal Standard Chechout проиллюстрирован на изображении


  1. На странице JSP находится форма POST запроса с указанием с указанием как минимум четырёх параметров:
    • amount_1 — цена одной единицы товара
    • business — email адрес PayPal счёта продаца (Seller счёт в песочнице)
    • item_name_1 — наименование товара
    • upload — уведомление что данная “корзина” создана сторонним поставщиком

  2. После нажатия на кнопку формы покупатель переходит на сайт песочницы PayPal по адресу https://www.sandbox.paypal.com/cgi-bin/webscr где ему требуется ознакомиться с выставленным счётом и авторизоваться (Buyer счёт в песочнице)
  3. Покупателю предлагается оплатить счёт нажатием на соответствующую кнопку
  4. Покупатель автоматически переходит на сайт продавца по url указанном в профиле продавца или по url указанном в параметре return формы POST
  5. Мерчант PayPal посылает IPN POST запрос по url указанному в профиле


Уведомления о полученном платеже (IPN)

Принцип работы IPN проиллюстрирован на изображении

  1. Ожидание POST запроса от мерчанта PayPal
  2. Создаём запрос к PayPal который содержит абсолютно те же IPN переменные с пришедшими значениями добавляя к запросу заголовок cmd=_notify-validate
  3. Отправляем запрос на sandbox.paypal.com
  4. Мерчант PayPal должен в ответ послать сообщение VERIFIED или INVALID
  5. Проверяем статус ответа который должен представлять собой код 200
  6. В случае если ответом является слово VERIFIED делаем следующие проверки:
  7. Если верифицированный ответ от мерчанта PayPal прошёл все проверки, обрабатываем действие базирующееся на значении переменной txn_type если она существует. В противном случае обрабатываем действие базирующуюся на значении переменной reason_code. Значения которые могут принимать данные переменные указаны в Order Management Integration Guide
  8. В случае если ответом является INVALID или код ответа не является 200 сохраняем сообщение для дальнейшего разбирательства


Работа с GAE

Код JSP страницы order.jsp:

<%@ page contentType="text/html; charset=UTF-8" language="java" %>

// information about values of parameters please see at url
// https://www.paypal.com/en_US/pdf/PP_WebsitePaymentsStandard_IntegrationGuide.pdf

<%	
//	final String strMerchantUrl = "https://www.paypal.com/cgi-bin/webscr";
	final String strMerchantUrl = "https://www.sandbox.paypal.com/cgi-bin/webscr";
	final String strEmail = "email_of_seller";
	final String strDescription = "my item description";
	final String strAmount = "1.3";
	final String strCurrencyCode = "USD";
//	your counter of invoice
	String strInvoice = "234234234";
	final String returnUrl = "site_of_seller";
%>

<form action=<%= strMerchantUrl %> method="post" accept-charset="UTF-8">
<input type="hidden" name="cmd" value="_cart" />
<input type="hidden" name="upload" value="1" />
<input type="hidden" name="business" value="<%= strEmail %>" />
<input type="hidden" name="item_name_1" value="<%= strDescription %>" />
<input type="hidden" name="amount_1" value="<%= strAmount %>" />
<input type="hidden" name="currency_code" value="<%= strCurrencyCode %>" />
<input type="hidden" name="no_shipping" value="1" />
<input type="hidden" name="invoice" value="<%= strInvoice %>" />
<input type="hidden" name="return" value="<%= returnUrl %>" />
<input type="image" value="PayPal" 
src="https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif"
alt="Submit button" align="left" style="margin-right:7px;" />
</form>


Код IPN обработчика пакете payment.paypal.Ipn.java

package payment.paypal;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.logging.Logger;
import java.util.Enumeration;
import java.net.URLEncoder;
import java.net.URL;
import java.net.URLConnection;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
public class Ipn extends HttpServlet {
	private static final Logger log = Logger.getLogger(Ipn.class.getName());
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {

		try {
			// read post from PayPal system and add 'cmd'
			Enumeration<?> en = req.getParameterNames();
			String str = "cmd=_notify-validate";
			while (en.hasMoreElements()) {
				String paramName = (String) en.nextElement();
				String paramValue = req.getParameter(paramName);
				str = str + "&" + paramName + "=" + URLEncoder.encode(paramValue, "UTF-8");
			}
			// test log IPN string
			log.info("[Paypal IPN string] " + str);

			// post back to PayPal system to validate
			// URL url = new URL("https://www.paypal.com/cgi-bin/webscr");
			URL url = new URL("https://www.sandbox.paypal.com/cgi-bin/webscr");
			URLConnection conn = url.openConnection();
			conn.setDoOutput(true);
			conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			OutputStreamWriter wr = new OutputStreamWriter(
					conn.getOutputStream());
			wr.write(str);
			wr.flush();

			// response from PayPal - VERIFIED or INVALID
			BufferedReader br = new BufferedReader(new InputStreamReader(
					conn.getInputStream()));
			String line = br.readLine();
			
			// test log check string
			log.info("[PayPal check string] " + line);
			wr.close();
			br.close();

			// assign posted variables to local variables
			String itemName = req.getParameter("item_name");
			String itemNumber = req.getParameter("item_number");
			String paymentStatus = req.getParameter("payment_status");
			String paymentAmount = req.getParameter("mc_gross");
			String paymentCurrency = req.getParameter("mc_currency");
			String txnId = req.getParameter("txn_id");
			String receiverEmail = req.getParameter("receiver_email");
			String payerEmail = req.getParameter("payer_email");

			// check notification validation
			if (line.equals("VERIFIED")) {
				// check that txnId has not been previously processed
				// check that receiverEmail is your Primary PayPal email
				// check that paymentAmount/paymentCurrency are correct
				// process payment
			} else if (line.equals("INVALID")) {
				// log for investigation
				log.warning(line);
			} else {
				// error
			}

		} catch (Exception e) {
			log.warning("[ipn] " + e);
		}
	}
}


Маппинг пути в файле /war/WEB-INF/web.xml

<!-- payment paypal -->
        <servlet>
        <servlet-name>Ipn</servlet-name>
        <servlet-class>payment.paypal.Ipn</servlet-class>
        </servlet>
<!--  -->
<!-- payment paypal mapping -->
        <servlet-mapping>
                <servlet-name>Ipn</servlet-name>
                <url-pattern>/payment/paypal/ipn</url-pattern>
        </servlet-mapping>
<!--  -->


Создаём конфигурацию лога в файле /war/WEB-INF/appengine-web.xml

  <!-- Configure java.util.logging -->
  <system-properties>
    <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
  </system-properties>


Статья была написана по мотивам топика http://habrahabr.ru/blogs/php/128198/, а также после долгих поисков по интернету простого и удобного способа получения оплаты с помощью PayPal. Разумеется охвачена не вся информация, к примеру в коде отсутствует проверка суммы оплаты. Без данной проверки злонамеренный пользователь может сохранить html страницу формы, отредактировать цену в сторону уменьшения и получить скажем цифровой товар или подписку по цене ниже покупной. Проверки зависят от реализации сервиса и требуют индивидуального подхода при интеграции платёжной системы на сайт продавца.
В работе использовался образец кода Java/JSP предоставленный PayPal

Данный способ можно использовать для оплаты услуг, продаже подписок на сервис, товаров, а также пополнения баланса учётной записи пользователя на сайте.

upd: указание кодировки, спасибо за мысль winbackgo
Tags:
Hubs:
+20
Comments 8
Comments Comments 8

Articles