Обновление стоимости и корзины

После создания платежной сессии иногда возникает необходимость обновить корзину.
Обычно обновление корзины — это асинхронное действие, которое по шагам выглядит так:

  1. Пользователь меняет состав корзины (или конечную сумму заказа).
  2. Продавец обновляет данные у себя на сервере.
  3. Продавец обновляет данные в платежной сессии Yandex Pay.

Проблема в том, что пользователь может начать процесс оплаты где-то между 1 и 2 пунктом, не дождавшись обновленных данных. Таким образом, он нажмет на кнопку и откроет форму Yandex Pay со старой корзиной.

Чтобы избежать подобной ситуации, нужно обновлять корзину с блокировкой кнопки Yandex Pay на время обновления.

Метод paymentSession.update принимает в качестве аргумента функцию (которая тут же выполняется), результатом работы которой ожидается Promise. Кнопка блокируется до резолва Promise.

Пример

function onYaPayLoad() {
    const YaPay = window.YaPay;
    let activeSession = null;

    // Данные платежа
    const paymentData = {
        env: YaPay.PaymentEnv.Sandbox,
        version: 3,
        currencyCode: YaPay.CurrencyCode.Rub,
        merchantId: '<YOUR_MERCHANT_ID>',
        cart: getNewCart(),
    };

    // Обработчик на получение платежного токена
    function onPaymentSuccess(event) {
        console.log(`OrderId — ${event.orderId}`);
    }

    // Обработчик на ошибки при оплате
    function onPaymentError(event) {
        console.log(`Payment error — ${event.reason}`);
    }

    // Обработчик на отмену оплаты
    function onPaymentAbort(event) {}

    // Создать платежную сессию.
    YaPay.createSession(paymentData, {
        onSuccess: onPaymentSuccess,
        onAbort: onPaymentAbort,
        onError: onPaymentError,
    })
        .then(function (paymentSession) {
            activeSession = paymentSession;
            paymentSession.mountButton(document.querySelector('#button_container'), {
                type: YaPay.ButtonType.Checkout,
                theme: YaPay.ButtonTheme.Black,
                width: YaPay.ButtonWidth.Auto,
            });
        })
        .catch(function (err) {
            // Не получилось создать платежную сессию.
        });

    // Эмуляция обновления корзины
    //   apiRequestPromise — Promise api-запроса на обновление корзины на сервисе
    onOrderUpdate(function (apiRequestPromise) {
        if (!activeSession) {
            return console.log('Payment session is not created');
        }

        const newCart = getNewCart();

        // Обновляем корзину с блокировкой кнопки
        activeSession.update(async function () {
            await apiRequestPromise;

            return {
                cart: newCart,
            };
        });
    });
}

/**
 * Сопроводительные функции для иллюстрации работы
 */

function getNewCart() {
    const randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;

    const price = 7990;
    const count = randomInt(1, 20);
    const total = (count * price).toFixed(2);

    document.querySelector('#order_amount').innerHTML = Intl.NumberFormat('ru-RU', {
        style: 'currency',
        currency: 'RUB',
    }).format(total);

    return {
        items: [{ productId: '3', total, quantity: { count } }],
    };
}

function onOrderUpdate(cb) {
    const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

    const updateButton = document.querySelector('#update_order_button');

    updateButton.addEventListener('click', function () {
        // Эмуляция Promise'a запроса на обновление корзины на сервере
        const apiRequestPromise = wait(2000);

        cb(apiRequestPromise);
    });
}