1. Обзор
Stripe — это облачный сервис, который позволяет предприятиям и частным лицам получать платежи через Интернет и предлагает как клиентские библиотеки (JavaScript и собственные мобильные устройства), так и серверные библиотеки (Java, Ruby, Node.js и т. д.).
Stripe обеспечивает уровень абстракции, упрощающий получение платежей. В результате нам не нужно иметь дело с данными кредитной карты напрямую — вместо этого мы имеем дело с токеном, символизирующим разрешение на списание средств .
В этом руководстве мы создадим пример проекта Spring Boot, который позволит пользователям вводить данные кредитной карты, а затем будет взимать с карты определенную сумму с помощью Stripe API для Java .
2. Зависимости
Чтобы использовать Stripe API для Java в проекте, мы добавляем соответствующую зависимость в наш pom.xml
:
<dependency>
<groupId>com.stripe</groupId>
<artifactId>stripe-java</artifactId>
<version>4.2.0</version>
</dependency>
Его последнюю версию мы можем найти в репозитории Maven Central .
Для нашего примера проекта мы будем использовать spring-boot-starter-parent
:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.1</version>
</parent>
Мы также будем использовать Lombok для сокращения стандартного кода, а Thymeleaf будет механизмом шаблонов для доставки динамических веб-страниц.
Поскольку мы используем spring-boot-starter-parent
для управления версиями этих библиотек, нам не нужно включать их версии в pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
Обратите внимание: если вы используете NetBeans, вы можете захотеть явно использовать Lombok с версией 1.16.16 , поскольку ошибка в версии Lombok, поставляемой с Spring Boot 1.5.2, приводит к тому, что NetBeans генерирует множество ошибок.
3. Ключи API
Прежде чем мы сможем общаться с Stripe и выполнять платежи по кредитным картам, нам необходимо зарегистрировать учетную запись Stripe и получить секретные/публичные ключи Stripe API .
После подтверждения учетной записи мы войдем в систему, чтобы получить доступ к панели управления Stripe . Затем мы выбираем «ключи API» в меню слева:
Будет две пары секретных/открытых ключей — одна для теста и одна для работы . Давайте оставим эту вкладку открытой, чтобы мы могли использовать эти клавиши позже.
4. Общий поток
Списание средств с кредитной карты будет выполняться в пять простых шагов, включая интерфейс (запуск в браузере), серверную часть (наше приложение Spring Boot) и Stripe:
- Пользователь переходит на страницу оформления заказа и нажимает «Оплатить картой».
- Пользователю предоставляется наложенное диалоговое окно Stripe Checkout, где он вводит данные кредитной карты.
- Пользователь подтверждает с помощью «Оплатить <сумма>», что будет:
- Отправить кредитную карту в Stripe
- Получите токен в ответе, который будет добавлен к существующей форме
- Отправьте эту форму с суммой, открытым ключом API, адресом электронной почты и токеном в нашу серверную часть.
- Наш сервер связывается с Stripe, чтобы передать токен, сумму и секретный ключ API.
- Серверная часть проверяет ответ Stripe и предоставляет пользователю обратную связь об операции.
Мы рассмотрим каждый шаг более подробно в следующих разделах.
5. Форма оформления заказа
Stripe Checkout — это настраиваемый, адаптированный для мобильных устройств и локализуемый виджет , который отображает форму для ввода данных кредитной карты. Благодаря включению и настройке « checkout.js
» он отвечает за:
- Отрисовка кнопки «Оплатить картой»
- Отображение диалогового окна наложения платежа (запускается после нажатия «Оплатить картой»)
- Проверка кредитной карты
- Функция «Запомнить меня» (связывает карту с номером мобильного телефона)
- Отправка кредитной карты в Stripe и замена ее токеном во вложенной форме (срабатывает после нажатия «Оплатить <сумма>»)
Если нам нужно больше контролировать форму оформления заказа, чем это дает Stripe Checkout, мы можем использовать Stripe Elements .
Далее мы разберем контроллер, который подготавливает форму, а затем и саму форму.
5.1. Контроллер
Начнем с создания контроллера для подготовки модели с необходимой информацией, необходимой для формы оформления заказа .
Во- первых, нам нужно скопировать тестовую версию нашего открытого ключа с панели управления Stripe и использовать ее для определения STRIPE_PUBLIC_KEY в качестве переменной среды. Затем мы используем это значение в поле stripePublicKey
.
Мы также устанавливаем здесь валюту
и сумму
(выраженную в центах) вручную только для демонстрационных целей, но в реальном приложении мы можем установить идентификатор продукта/продажи, который можно использовать для получения фактических значений.
Затем мы перейдем к представлению оформления заказа, которое содержит форму оформления заказа:
@Controller
public class CheckoutController {
@Value("${STRIPE_PUBLIC_KEY}")
private String stripePublicKey;
@RequestMapping("/checkout")
public String checkout(Model model) {
model.addAttribute("amount", 50 * 100); // in cents
model.addAttribute("stripePublicKey", stripePublicKey);
model.addAttribute("currency", ChargeRequest.Currency.EUR);
return "checkout";
}
}
Что касается ключей Stripe API, вы можете определить их как переменные среды для каждого приложения (тестовые или живые).
Как и в случае с любым паролем или конфиденциальной информацией, секретный ключ лучше не использовать в вашей системе контроля версий.
5.2. Форма
Кнопка «Оплатить картой» и диалог оформления заказа включаются путем добавления формы со скриптом внутри, правильно настроенным с атрибутами данных:
<form action='/charge' method='POST' id='checkout-form'>
<input type='hidden' th:value='${amount}' name='amount' />
<label>Price:<span th:text='${amount/100}' /></label>
<!-- NOTE: data-key/data-amount/data-currency will be rendered by Thymeleaf -->
<script
src='https://checkout.stripe.com/checkout.js'
class='stripe-button'
th:attr='data-key=${stripePublicKey},
data-amount=${amount},
data-currency=${currency}'
data-name='ForEach'
data-description='Spring course checkout'
data-image
='https://www.foreach.com/wp-content/themes/foreach/favicon/android-chrome-192x192.png'
data-locale='auto'
data-zip-code='false'>
</script>
</form>
Сценарий « checkout.js
» автоматически инициирует запрос к Stripe прямо перед отправкой, который затем добавляет токен Stripe и адрес электронной почты пользователя Stripe в качестве скрытых полей « stripeToken
» и « stripeEmail
».
Они будут отправлены в наш сервер вместе с другими полями формы. Атрибуты данных скрипта не отправляются.
Мы используем Thymeleaf для отображения атрибутов « ключ данных
», « количество данных
» и « валюта данных
».
Сумма (« количество данных
») используется только для целей отображения (наряду с « валютой данных
»). Его единицей измерения являются центы используемой валюты, поэтому мы делим его на 100, чтобы отобразить.
Открытый ключ Stripe передается Stripe после того, как пользователь попросит заплатить. Не используйте здесь секретный ключ, так как он отправляется в браузер.
6. Операция зарядки
Для обработки на стороне сервера нам нужно определить обработчик запроса POST, используемый формой оформления заказа. Давайте взглянем на классы, которые нам понадобятся для операции зарядки.
6.1. Объект ChargeRequest
Давайте определим POJO ChargeRequest
, который мы будем использовать в качестве бизнес-объекта во время операции оплаты:
@Data
public class ChargeRequest {
public enum Currency {
EUR, USD;
}
private String description;
private int amount;
private Currency currency;
private String stripeEmail;
private String stripeToken;
}
6.2. обслуживание
Давайте напишем класс StripeService
для передачи фактической операции оплаты в Stripe :
@Service
public class StripeService {
@Value("${STRIPE_SECRET_KEY}")
private String secretKey;
@PostConstruct
public void init() {
Stripe.apiKey = secretKey;
}
public Charge charge(ChargeRequest chargeRequest)
throws AuthenticationException, InvalidRequestException,
APIConnectionException, CardException, APIException {
Map<String, Object> chargeParams = new HashMap<>();
chargeParams.put("amount", chargeRequest.getAmount());
chargeParams.put("currency", chargeRequest.getCurrency());
chargeParams.put("description", chargeRequest.getDescription());
chargeParams.put("source", chargeRequest.getStripeToken());
return Charge.create(chargeParams);
}
}
Как было показано в CheckoutController
, поле secretKey
заполняется из переменной среды STRIPE_SECRET_KEY, которую мы скопировали из панели управления Stripe .
После инициализации службы этот ключ используется во всех последующих операциях Stripe.
Объект, возвращаемый библиотекой Stripe, представляет операцию оплаты и содержит полезные данные, такие как идентификатор операции.
6.3. Контроллер
Наконец, давайте напишем контроллер, который будет получать POST-запрос, сделанный формой оформления заказа, и отправлять оплату в Stripe через наш StripeService
.
Обратите внимание, что параметр « ChargeRequest
» автоматически инициализируется параметрами запроса « amount
», « stripeEmail
» и « stripeToken
», включенными в форму:
@Controller
public class ChargeController {
@Autowired
private StripeService paymentsService;
@PostMapping("/charge")
public String charge(ChargeRequest chargeRequest, Model model)
throws StripeException {
chargeRequest.setDescription("Example charge");
chargeRequest.setCurrency(Currency.EUR);
Charge charge = paymentsService.charge(chargeRequest);
model.addAttribute("id", charge.getId());
model.addAttribute("status", charge.getStatus());
model.addAttribute("chargeId", charge.getId());
model.addAttribute("balance_transaction", charge.getBalanceTransaction());
return "result";
}
@ExceptionHandler(StripeException.class)
public String handleError(Model model, StripeException ex) {
model.addAttribute("error", ex.getMessage());
return "result";
}
}
В случае успеха мы добавляем статус, идентификатор операции, идентификатор платежа и идентификатор транзакции баланса в модель, чтобы мы могли показать их позже пользователю (раздел 7). Это сделано для того, чтобы проиллюстрировать часть содержимого объекта charge .
Наш ExceptionHandler
будет иметь дело с исключениями типа StripeException
, которые возникают во время операции зарядки.
Если нам нужна более детальная обработка ошибок, мы можем добавить отдельные обработчики для подклассов StripeException
, таких как CardException
, RateLimitException
или AuthenticationException
.
Представление « результат
» отображает результат операции начисления.
7. Отображение результата
HTML-код, используемый для отображения результата, представляет собой базовый шаблон Thymeleaf, отображающий результат операции начисления платы. Пользователь отправляется сюда ChargeController
независимо от того, была ли операция зарядки успешной или нет:
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xmlns:th='http://www.thymeleaf.org'>
<head>
<title>Result</title>
</head>
<body>
<h3 th:if='${error}' th:text='${error}' style='color: red;'></h3>
<div th:unless='${error}'>
<h3 style='color: green;'>Success!</h3>
<div>Id.: <span th:text='${id}' /></div>
<div>Status: <span th:text='${status}' /></div>
<div>Charge id.: <span th:text='${chargeId}' /></div>
<div>Balance transaction id.: <span th:text='${balance_transaction}' /></div>
</div>
<a href='/checkout.html'>Checkout again</a>
</body>
</html>
В случае успеха пользователь увидит некоторые детали операции зарядки:
В случае ошибки пользователю будет представлено сообщение об ошибке, возвращенное Stripe:
8. Заключение
В этом руководстве мы показали, как использовать API-интерфейс Stripe Java для списания средств с кредитной карты. В будущем мы могли бы повторно использовать наш серверный код для обслуживания нативного мобильного приложения.
Чтобы проверить весь поток заряда, нам не нужно использовать настоящую кредитную карту (даже в тестовом режиме). Вместо этого мы можем положиться на тестовые карты Stripe .
Операция оплаты — одна из многих возможностей, предлагаемых Stripe Java API. Официальный справочник API проведет нас через весь набор операций.
Пример кода, используемый в этом руководстве, можно найти в проекте GitHub .