Интеграция Midtrans и Laravel 8 с помощью Snap: Часть 2

Прежде чем продолжить чтение, пожалуйста, прочитайте часть 1: Интеграция Midtrans и Laravel 8 с помощью Snap: Часть 1

В первой части мы успешно интегрировали платежный интерфейс Midtrans с помощью Snap. До этого момента клиенты могли осуществлять платежи через различные доступные платежные каналы. В этой статье мы продолжим создавать обратные вызовы платежей.

Что такое обратные вызовы? В первой части клиент может совершить платеж. Например, используя виртуальный счет. После того как клиент совершит успешный платеж, Midtrans отправит на наш сайт уведомление о том, что клиент совершил платеж. Или, если клиент не совершит платеж в течение указанного срока, Midtrans отправит уведомление о том, что транзакция истекла. Наша задача, получить уведомление и обработать его.

Например, после того как клиент успешно произвел оплату, мы получим уведомление об успешной оплате. Из уведомления мы можем изменить статус оплаты заказа с ожидающего оплаты на уже оплаченный. С этой системой нет необходимости вручную подтверждать и изменять статус оплаты.

Создание класса услуг

Sama seperti sebelumnya, logic utama akan kita pisahkan di service layer. Sebelumnya, kita sudah membuat file Midtrans.php, CreateSnapTokenService.php dan CallbackService.php, untuk callback akan kita tulis di file CallbackService.php.

<?php

namespace AppServicesMidtrans;

use AppModelsOrder;
use AppServicesMidtransMidtrans;
use MidtransNotification;

class CallbackService extends Midtrans
{
    protected $notification;
    protected $order;
    protected $serverKey;

    public function __construct()
    {
        parent::__construct();

        $this->serverKey = config('midtrans.server_key');
        $this->_handleNotification();
    }

    public function isSignatureKeyVerified()
    {
        return ($this->_createLocalSignatureKey() == $this->notification->signature_key);
    }

    public function isSuccess()
    {
        $statusCode = $this->notification->status_code;
        $transactionStatus = $this->notification->transaction_status;
        $fraudStatus = !empty($this->notification->fraud_status) ? ($this->notification->fraud_status == 'accept') : true;

        return ($statusCode == 200 && $fraudStatus && ($transactionStatus == 'capture' || $transactionStatus == 'settlement'));
    }

    public function isExpire()
    {
        return ($this->notification->transaction_status == 'expire');
    }

    public function isCancelled()
    {
        return ($this->notification->transaction_status == 'cancel');
    }

    public function getNotification()
    {
        return $this->notification;
    }

    public function getOrder()
    {
        return $this->order;
    }

    protected function _createLocalSignatureKey()
    {
        $orderId = $this->order->number;
        $statusCode = $this->notification->status_code;
        $grossAmount = $this->order->total_price;
        $serverKey = $this->serverKey;
        $input = $orderId . $statusCode . $grossAmount . $serverKey;
        $signature = openssl_digest($input, 'sha512');

        return $signature;
    }

    protected function _handleNotification()
    {
        $notification = new Notification();

        $orderNumber = $notification->order_id;
        $order = Order::where('number', $orderNumber)->first();

        $this->notification = $notification;
        $this->order = $order;
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

В этом сервисе мы перехватываем уведомление, отправленное Midtrans, с помощью метода _handleNotification(), затем результат уведомления будет обработан снова. Здесь нам также необходимо создать локальный ключ подписи. Этот ключ является локальным ключом для того, чтобы можно было получать уведомления. При отправке уведомлений Midtrans также будет отправлять ключ подписи, ключ будет сравниваться с локальным ключом подписи, чтобы проверить, является ли запрос действительным или нет.

Создание контроллера обратного вызова

Для получения уведомлений мы создадим пользовательский контроллер с именем PaymentCallbackController. В терминале введите следующую команду:

php artisan make:controller PaymentCallbackController
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем напишите следующий код в контроллере.

<?php

namespace AppHttpControllers;

use AppModelsOrder;
use IlluminateHttpRequest;
use AppServicesMidtransCallbackService;

class PaymentCallbackController extends Controller
{
    public function receive()
    {
        $callback = new CallbackService;

        if ($callback->isSignatureKeyVerified()) {
            $notification = $callback->getNotification();
            $order = $callback->getOrder();

            if ($callback->isSuccess()) {
                Order::where('id', $order->id)->update([
                    'payment_status' => 2,
                ]);
            }

            if ($callback->isExpire()) {
                Order::where('id', $order->id)->update([
                    'payment_status' => 3,
                ]);
            }

            if ($callback->isCancelled()) {
                Order::where('id', $order->id)->update([
                    'payment_status' => 4,
                ]);
            }

            return response()
                ->json([
                    'success' => true,
                    'message' => 'Notification successfully processed',
                ]);
        } else {
            return response()
                ->json([
                    'error' => true,
                    'message' => 'Signature key not verified',
                ], 403);
        }
    }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

В этом контроллере мы должны проверить, проверен ли ключ подписи или нет. Кроме того, в обратном вызове сервиса у нас есть 3 метода, а именно isSuccess() для проверки успешности транзакции, isExpire() для проверки истечения срока действия транзакции, isCancelled() для проверки отмены транзакции. Если транзакция успешна, то обновите данные о статусе заказа на 2 (уже оплачен), если истек, то обновите данные о статусе заказа на 3 (истек), а если истек, то обновите данные о статусе заказа на 3. Вы также можете добавить другую логику по мере необходимости.

Замечание по технике безопасности
Созданный мной программный поток вполне безопасен для использования. Даже если кто-то знает URL обратного вызова, этот человек не сможет “выстрелить” в него напрямую, потому что он должен включать заголовок аутентификации в виде ключа подписи, который представляет собой комбинацию ID заказа, кода статуса, общей цены и ключа сервера. Поэтому не позволяйте никому знать ваш серверный ключ Midtrans.

Создание маршрутов

После создания контроллера мы должны создать новый маршрут. Позже Midtrans отправит пост-запрос на этот маршрут. В маршрут web.php добавьте следующий маршрут.

use AppHttpControllersPaymentCallbackController;

Route::post('payments/midtrans-notification', [PaymentCallbackController::class, 'receive']);
Вход в полноэкранный режим Выйти из полноэкранного режима

Midtrans будет отправлять уведомления на адрес: http://mydomain.com/payments/midtrans-notification. Но здесь мы не будем развертывать для тестирования, а воспользуемся tunel.

Создание исключения CSRF

Поскольку он находится на веб-маршруте, каждый пост-запрос будет защищен CSRF. Это означает, что маршрут, который мы создали выше, будет недоступен для Midtrans. Для этого нам нужно исключить маршрут из защиты CSRF. В файле app/Http/Middleware/VerifyCsrfToken.php добавьте вышеуказанный маршрут в исключаемые, чтобы он выглядел следующим образом.

protected $except = [
    'payments/midtrans-notification',
];
Вход в полноэкранный режим Выйти из полноэкранного режима

Если это так, следующим шагом будет тестирование.

Установка Ngrok для туннелирования

Поскольку наш Laravel все еще находится на localhost (все еще в автономном режиме), конечно, Midtrans не может отправлять уведомления. Для этого нам нужно подключить Laravel к сети. Вы можете сделать это, загрузив его на сервер, чтобы он был онлайн и доступен. Но здесь я не буду загружать на сервер, а буду использовать localhost. Хитрость заключается в использовании Ngrok.

Что такое Ngrok? Ngrok – это приложение для туннелирования, которое может “online localhost”. Ngrok может заставить наш Laravel на localhost (offline) быть online и быть доступным любому в интернете, просто введя URL, предоставленный Ngrok. Поэтому мы будем использовать Ngrok для тестирования обратного вызова Midtrans.

Примечания
Ngrok используется только для автономного тестирования, если он готов к использованию и загружен на сервер, конфигурацию менять не нужно. Просто замените адрес ngrok tunel на ваш домен.

Загрузка и установка Ngrok

Чтобы загрузить, перейдите на веб-сайт ngrok.com и перейдите на страницу загрузки. Затем загрузите в соответствии с вашей операционной системой. Для установки, пожалуйста, следуйте руководству для каждой операционной системы. Если вы работаете в Windows, просто щелкните мышью, как обычно.

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

Пожалуйста, запишите токен. Затем в CMD / терминале введите следующую команду

ngrok authtoken NGROK_AUTH_TOKEN
Войти в полноэкранный режим Выйти из полноэкранного режима

В случае успеха мы перейдем к следующему этапу.

Создание локальных серверов и туннелей

Для создания локального сервера мы воспользуемся командой artisan serve из laravel с портом 8080 (не 8000).

php artisan serve --port=8080
Войдите в полноэкранный режим Выйдите из полноэкранного режима

После этого будет создан новый локальный сервер с портом 8080. (http://localhost:8080)

Сделать tunel ngrok

Чтобы запустить созданный ранее localhost, в cmd введите следующую команду:

ngrok http 8080
Войти в полноэкранный режим Выйти из полноэкранного режима

Эта команда направит все запросы на порт 8080 и порт Laravel, который был создан ранее. В случае успеха это будет выглядеть следующим образом.

Обратите внимание на URL переадресации, предоставленный Ngrok, достаточно одного из них. Здесь я получаю URL: http://b674-110-137-75-3.ngrok.io/. Этот URL может быть доступен любому человеку в интернете. Этот URL будет передан Midtrans для отправки уведомлений. Чтобы просмотреть историю входящих запросов, перейдите по ссылке: http://localhost:4040/.

Настройка URL-адреса уведомления на приборной панели Midtrans

Откройте приборную панель Midtrans, в разделе Settings > Configuration, в поле Payment Notification URL, заполните следующее.

Пожалуйста, скорректируйте его с вашим ngrok URL. Если да, то теперь он должен быть готов к тестированию.

Примечания
Ngrok используется только для автономного тестирования, если он готов к использованию и загружен на сервер, конфигурацию менять не нужно. Просто замените адрес ngrok tunel на ваш домен. Например, если он загружен на домен https://jurnalmms.web.id, просто измените на: https://jurnalmms.web.id/payments/midtrans-notification.

Выполнение теста

Чтобы протестировать платеж, мы можем использовать симулятор, предоставленный Midtrans. С помощью симулятора мы можем осуществлять платежи, не прибегая к реальным платежам. Доступ к Midtrans Mock можно получить здесь.

Получение номера счета назначения

Сначала нам нужно получить номер счета получателя. Хитрость заключается в том, чтобы открыть страницу заказа, затем нажать кнопку “Оплатить сейчас”, выбрать канал оплаты и обратить внимание на отображаемый номер счета.

Поскольку номер является виртуальным счетом BRI, на сайте Mock Payment Midtrans выберите “Виртуальный счет BRI” в выпадающем списке Payment Page.

Затем в поле ввода введите номер счета, полученный ранее. Затем нажмите кнопку Inquire (Запрос). На следующей странице нажмите кнопку Pay для оплаты. Если вы успешно перешли на страницу успеха, это означает, что имитация платежа была успешно проведена.

Чтобы просмотреть историю, перейдите на сайт http://localhost:4040 и просмотрите историю входящих запросов через Ngrok.

На данный момент мы успешно получили уведомления от Midtrans. Статус платежа в базе данных также изменится автоматически.

Когда вы начнете тестирование, может возникнуть проблема с непроверенным ключом подписи. Один из способов решения этой проблемы – убедиться, что цена, уплаченная клиентом, совпадает с колонкой total_price в базе данных.

Весь код программы можно посмотреть в следующем репозитории GitHub:
https://github.com/mulyosyahidin/laravel-midtrans

Прочитать этот пост на индонезийском языке: https://jurnalmms.web.id/laravel/integrasi-midtrans-dan-laravel-8-menggunakan-snap-bagian-2/

Оцените статью
Procodings.ru
Добавить комментарий