- Клонировать проект
- Установить зависимости через composer
- Запустить миграцию через команду
php artisan migrate
- Запустить сидер через команду
php artisan db:seed
URL (POST): http://localhost/api/v1/calculate-price Тело запроса:
{
"product": id из таблицы products,
"taxNumber": tax_format из таблицы countries,
"couponCode": code из таблицы coupons
}
URL (POST): http://localhost/api/v1/purchase Тело запроса:
{
"product": id из таблицы products,
"taxNumber": tax_format из таблицы countries,
"couponCode": code из таблицы coupons
"paymentProcessor": "paypal" ИЛИ "stripe"
}
Необходимо написать 2 эндпоинта:
- POST: для расчёта цены
http://127.0.0.1:80/calculate-price
Пример json тела запроса:
{
"product": 1,
"taxNumber": "DE123456789",
"couponCode": "D15"
}
- POST: для выполнения покупки
Пример json тела запроса:
{
"product": 1,
"taxNumber": "IT12345678900",
"couponCode": "D15",
"paymentProcessor": "paypal"
}
При успешном выполнении запроса вернуть HTTP ответ с кодом 200.
При неверных входных данных или ошибках оплаты вернуть HTTP ответ с кодом 400 и json объект с ошибками.
Предполагается, что продукты хранятся в БД, для примера можно взять 3 продукта:
- Iphone (100 евро)
- Наушники (20 евро)
- Чехол (10 евро)
При наличии купона покупатель может применить его к покупке.
Купон может быть двух типов:
- фиксированная сумма скидки
- процент от суммы покупки
Мы подразумеваем, что купоны создаются продавцом и где-то хранятся (ну или захардкодены, на ваше усмотрение)
т.е. не должно быть ситуации, когда код купона определяет скидку и покупатель может подставить любой код купона.
Например, при наличии (в БД или в виде хардкода) купонов P10 (скидка 10%) и P100 (скидка 100%) у покупателя не должно быть возможности применить купон P50, если он не хранится явным образом.
Код купона не обязательно должен соответствовать какому-либо формату. Вы можете выбрать его на своё усмотрение.
При покупке продукта получатель сверх цены продукта должен уплатить налог, относительно страны налогового номера:
- Германии - 19%
- Италии - 22%
- Франции - 20%
- Греции - 24%
В итоге для покупателя Iphone из Греции цена составляет 124 евро (цена продукта 100 евро + налог 24%).
Если у покупателя есть купон на 6% скидку на покупку, то цена будет 116.56 евро (цена продукта 100 евро - 6% скидка + налог 24%).
DEXXXXXXXXX - для жителей Германии,
ITXXXXXXXXXXX - для жителей Италии,
GRXXXXXXXXX - для жителей Греции,
FRYYXXXXXXXXX - для жителей Франции
где:
- первые два символа - это код страны,
- X - любая цифра от 0 до 9,
- Y - любая буква
Обратите внимание, что длина налогового номера разная для разных стран.
Форматы налоговых номеров могут меняться, что случается редко. (Это зависит от законодательства.)
При выполнении задания нужно:
- реализовать валидацию всех полей (в том числе корректность tax номера согласно формату) в теле запросов, используя Symfony validator
- рассчитать итоговую цену покупки вместе с купоном (если указан) и налогом
- использовать для проведения платежа
PaypalPaymentProcessor::pay()
илиStripePaymentProcessor::processPayment()
Эти классы представлены в этом проекте, использовать следует именно их. В методах оплаты они принимают цену как в разных юнитах (как в центах, так и в долларах).- ИЛИ скопируйте их себе в проект. Для простоты представьте, что эти два класса входят в два разных сторонних SDK, и у вас нет возможности править эти классы или какую-либо логику внутри них.
- ИЛИ добавьте
systemeio/test-for-candidates
как зависимость через Composer.
- приложить в README.md примеры HTTP-запросов к двум эндпоинтам: path и тело запроса (для ручного тестирования) в формате curl команды
CRUD для сущностей писать не нужно, будем считать что он "есть" и данные в БД валидны, т.е. проверок в сервисах, что процентная скидка по купону больше 0, меньше 1000 и прочих подобных делать не нужно.
При написании тестового используйте git, после выполнения пришлите ссылку на репозиторий.
Необходимо учесть возможность добавления новых PaymentProcessors.
Если вы чувствуете, что определённая часть задания требует у вас много времени на выполнение, вы можете выбрать наиболее простое решение и комментарием указать возможные варианты реализации, которые вы рассматриваете
- использование контейнеризации для php, postgres/mysql
- наличие PHPUnit tests
- соответствие кода принципам SOLID (без фанатизма)
- покоммитное оформление этапов реализации приветствуется
- продемонстрированное умение НЕ! использовать подходы вроде onion-based/DDD/CQS/гексагональной архитектуры при выполнении задания: мы куда больше ценим его корректность и полноту; такие сложные концепции в нашем задании скорее не уместны
Мы не ограничиваем вас по срокам выполнения задания, но при этом ожидаем, что в тестовом задании вы раскроете принципы, которых придерживаетесь в работе.