Прием платежей через Яндекс.Кассу

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

Для подключения нам понадобятся ID тестового магазина и ключ api.

ID магазина есть в заголовке страницы магазина

Ключ - в разделе Интеграция => Ключи API

Яндекс.Касса_1

Для проведения платежей можно использовать виджет. 

<script src="https://kassa.yandex.ru/checkout-ui/v2.js"></script>

Алгоритм платежа следующий:

  • Делаем ajax запрос, в хранимой процедуре которого отправляем запрос к внешнему api на создание платежа к Яндекс.Кассе

Запрос содержит:

  • Тело запроса. JSON с суммой и описанием платежа.
  • Заголовок авторизации. Формируется на основе id и ключа магазина.
  • Заголовок с ключом идемпотентности. Ключ должен быть уникальным, запрос с тем же ключом считается повторным.

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

  • Делаем ajax запрос, который получает ответ api яндекс кассы. Используем токен в составе ответа для инициализации виджета. После проведения платежа нас перенаправит на страницу подтверждения платежа, адрес которой также нужно указать в параметрах виджета (обязательно нужно указывать абсолютный url).
  • Яндекс.Касса отправит уведомление с подтверждением платежа на наш внутренний api. Подлинность уведомления можно проверить по ip адресу https://kassa.yandex.ru/developers/using-api/webhooks.

 

Адрес для отправки уведомлений указывается в настройках магазина:

Яндекс.Касса_2.png

Внутренний api для подтверждения платежей не должен использовать токены. 

JavaScript компонент для проведения платежей

as.yaKassa = {
    response: {},
    checkout: {},
    initPayment: function () {
        as.sys.request("yaKassa", "initPayment", { //создаем платеж через запрос
            data: {
                value: $(".payment-price").val(), //размер платежа
                description: $(".payment-description").val() //описание платежа
            },
            onSuccess: function (data) {
                as.yaKassa.getPaymentStatus(); //получаем статус платежа
            }
        });
    },
    getPaymentStatus: function () {
        as.sys.request("yaKassa", "getPaymentResponse", {
            data: {},
            onSuccess: function (data) {
                as.yaKassa.response = JSON.parse(data.data[0].response); //парсим ответ яндекс кассы на запрос создания платежа
		    //запускаем виджет
                as.yaKassa.checkout = new window.YandexCheckout({
                    confirmation_token: as.yaKassa.response.confirmation.confirmation_token, //Токен на проведение платежа от Яндекс.Кассы
                    return_url: 'https://polygon2.web-automation.ru/yaKassaConfirm', //Ссылка на страницу завершения оплаты
                    error_callback(error) {
                        console.log("yaKassa error:")
                        console.log(error);
                        //Обработка ошибок инициализации
                    }
                });
                as.yaKassa.checkout.render('paymentForm');
            }
        });
    }
}

Хранимая процедура внешнего api для создания платежа - запрос

CREATE PROCEDURE [dbo].[api_yaKassaInitPayment_request]
    @parameters ExtendedDictionaryParameter READONLY,  -- входящие параметры для внутренней обработки (используйте Key, Value2)
    @username nvarchar(32)  -- текущий пользователь.
AS
BEGIN

    exec as_print @str='starting yaKassaInitPayment request...'

    -- SELECT 1  Msg, Result, Url (адрес, куда будет идти запрос)
    select '' Msg, 1 Result, 'https://payment.yandex.net/api/v3/payments' Url

    declare @json nvarchar(max) = isnull((select Value2 from @parameters where [Key]='json'),''), --тело запроса. В нашем примере оно приходит сюда в качестве параметра
            @shopID nvarchar(16) = (select value from as_settings where code='yaKassaShopID'), --id магазина из настроек
            @shopKey nvarchar(256) = (select value from as_settings where code='yaKassaShopKey'), --ключ магазина из настроек
            @idempotenceKey nvarchar(256) = isnull((select Value2 from @parameters where [Key]='idempotenceKey'),'') --ключ идемпотентности

    declare @authorizationHeader nvarchar(512) = 'Basic ' + dbo.str_toBase64(@shopID + ':' + @shopKey) --формируем заголовок авторизации

    declare @paramsTemp table ([Key] nvarchar(128), [Value2] nvarchar(max), [type] nvarchar(64)) --параметры запроса
    insert into @paramsTemp ([Key], [Value2], [type]) values
        ('Authorization', @authorizationHeader, 'header'),
        ('Idempotence-Key', @idempotenceKey, 'header'),
        ('body', @json, 'json')

    -- SELECT 2 PARAMETERS - параметры, которые будут передаваться во внешний источник
    select [Key] name, [Value2] value, [type] [type] -- form (в форме передается), header (в http headers), get запросы передавайте прямо в URL
    from @paramsTemp
END

Хранимая процедура внешнего api для создания платежа - ответ

CREATE PROCEDURE [dbo].[api_yaKassaInitPayment_response]
    @response nvarchar(max),
    @parameters ExtendedDictionaryParameter READONLY,
    @username nvarchar(32)
AS
BEGIN

    exec as_print @str='YaKassaInitPaymentResponse'

    --для примера сохраняем платеж в as_trace со специальным кодом
    --в реальности будет таблица с платежами
    insert into as_trace (text, username, code)
    values(@response, @username, 'yaKassaPayments')

    -- SELECT 1
    select '' Msg, 1 Result, @response Response

END

Хранимая процедура ajax-запроса для создания платежа

CREATE PROCEDURE [dbo].[request_yaKassa_initPayment]
    @parameters DictionaryParameter READONLY,
    @username nvarchar(32)
AS
BEGIN
    select '' Msg, 1 Result

    declare @description nvarchar(256) = (select Value from @parameters where [Key]='description'), --описание платежа
            @value nvarchar(16) = (select Value from @parameters where [Key]='value') --сумма платежа

    --тело запроса
    declare @json nvarchar(max) =
    '{
        "amount": {
          "value": "' + @value + '",
          "currency": "RUB"
        },
        "confirmation": {
          "type": "embedded"
        },
        "capture": true,
        "description": "' + @description + '"
    }'


    select @description description, @value value

    select 'apirequest' type, 'yaKassaInitPayment' code, --запрос к внешнему api
    'idempotenceKey' p1_name, @description p1_value, --в качестве ключа идемпотентности для примера берем описание
    'json' p2_name, @json p2_value
END

Хранимая процедура ajax-запроса для получения статуса платежа

CREATE PROCEDURE [dbo].[request_yaKassa_getPaymentResponse]
    @parameters DictionaryParameter READONLY,
    @username nvarchar(32)
AS
BEGIN
    select '' Msg, 1 Result

    --т.к. мы используем as_trace, просто достаем последний платеж
    --на практике можно передать id платежа и доставать по нему
    select top 1 [text] as response from as_trace where username=@username and code='yaKassaPayments' order by id desc
END

Хранимая процедура внутреннего api для подтверждения платежа

CREATE PROCEDURE [dbo].[api_payment_confirmPayment]
@parameters ExtendedDictionaryParameter READONLY,
@username nvarchar(256)
as
begin
       
    --проверяем, что уведомление пришло с верного ip адреса
    declare @ip nvarchar(50) = (select Value2 from @parameters where [Key]='remoteIP') --ip адрес
    
    --разрешенные адреса
    --185.71.76.0/27
    --185.71.77.0/27
    --77.75.153.0/25
    --77.75.154.128/25
    declare @allowedNets table (subnet nvarchar(50), maskNumber int)
    insert into @allowedNets (subnet, maskNumber)
    values ('185.71.76.0', 27), ('185.71.77.0', 27), ('77.75.153.0', 25), ('77.75.154.128', 25)
    
  
    --проверяем, что ip уведомления входит в разрешенные подсети
    declare @ipIsCorrect bit = case when 
           exists(select * from @allowedNets where dbo.as_GetSubnetForIP(@ip, maskNumber)=subnet) 
           then 1 else 0 end
    
    if (@ipIsCorrect = 1) begin
        --json уведомления находится в параметре InputStream
      declare @notification nvarchar(max) = (select Value2 from @parameters where [Key]='InputStream')

      --получаем статус платежа
      declare @paymentStatus nvarchar(128) = JSON_VALUE(@notification, '$.object.status')

      --обновляем статус платежа в системе
      if (@paymentStatus = 'succeeded') begin
          exec as_print @str='payment success!'
          --....
      end
    end
    
    -- SELECT 1 - вывод метаданных о результате операции метода API 
    select '' Msg, 1 Result, 0 errorCode
    
    -- SELECT 2 -  вывод самих данных в API (в случае проблем проверьте что этот запрос приходит непустой)
    select 0 where 1=0

Тестирование

Тестовые номера карт доступны по адресу https://kassa.yandex.ru/developers/using-api/testing

Пример уведомления о подтверждении платежа:

{
    "type": "notification",
    "event": "payment.succeeded",
    "object": {
        "id": "2669b9a3-000f-5000-9000-1bc95c3ceaac",
        "status": "succeeded",
        "paid": true,
        "amount": {
            "value": "103.00",
            "currency": "RUB"
        },
        "authorization_details": {
            "rrn": "018162691437",
            "auth_code": "690243"
        },
        "captured_at": "2020-06-03T13:30:46.160Z",
        "created_at": "2020-06-03T13:30:11.563Z",
        "description": "test2030",
        "metadata": {},
        "payment_method": {
            "type": "bank_card",
            "id": "2669b9a3-000f-5000-9000-1bc95c3ceaac",
            "saved": false,
            "card": {
                "first6": "555555",
                "last4": "4477",
                "expiry_month": "08",
                "expiry_year": "2020",
                "card_type": "MasterCard",
                "issuer_country": "US"
            },
            "title": "Bank card *4477"
        },
        "recipient": {
            "account_id": "700318",
            "gateway_id": "1712302"
        },
        "refundable": true,
        "refunded_amount": {
            "value": "0.00",
            "currency": "RUB"
        },
        "test": true
    }
}

Выплаты через яндекс.кассу

Официальная документация: https://kassa.yandex.ru/tech/payout/intro.html

 Есть два механизма выплат - через api и по списку.

Способ по списку подразумевает загрузку excel или csv файла в личном кабинете и подтверждение по смс.

Шаблон excel файла можно скачать в личном кабинете после регистрации. Формат csv файла описан на https://kassa.yandex.ru/tech/payout/payouts-via-mp-csv.html

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

 

Дополнительные функции SQL

CREATE FUNCTION [dbo].[as_BinaryToDecimal]
(
	@Input varchar(255)
)
RETURNS bigint
AS
BEGIN

	DECLARE @Cnt tinyint = 1
	DECLARE @Len tinyint = LEN(@Input)
	DECLARE @Output bigint = CAST(SUBSTRING(@Input, @Len, 1) AS bigint)

	WHILE(@Cnt < @Len) BEGIN
		SET @Output = @Output + POWER(CAST(SUBSTRING(@Input, @Len - @Cnt, 1) * 2 AS bigint), @Cnt)

		SET @Cnt = @Cnt + 1
	END

	RETURN @Output	

END

USE [polygon2]
GO
/****** Object:  UserDefinedFunction [dbo].[as_DecimalToBinary]    Script Date: 08.06.2020 23:10:08 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[as_DecimalToBinary]
(
	@Input bigint
)
RETURNS varchar(255)
AS
BEGIN

	DECLARE @Output varchar(255) = ''

	WHILE @Input > 0 BEGIN

		SET @Output = @Output + CAST((@Input % 2) AS varchar)
		SET @Input = @Input / 2

	END

	RETURN REVERSE(@Output)

END

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
alter FUNCTION as_GetSubnetForIP
(
	@ip varchar(16),
	@maskNumber int
	-- Add the parameters for the function here
	
)
RETURNS varchar(16)
AS
BEGIN

	declare @temp0 table (value varchar(8))
	declare @temp1 table (bin varchar(1), mask varchar(1))

	insert into @temp0
	select dbo.as_DecimalToBinary(Value) value from dbo.split(@ip, '.')

	update @temp0
	set value = isnull(replicate('0', 8-len(value)), '') + value

	declare @binaryString varchar(32) =  (SELECT
	   stuff( (SELECT value 
				   FROM @temp0
				   FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)')
				,1,0,''))

	--select * from #subnetTemp0

	--select @binaryString


	declare @mask varchar(32) = replicate('1', @maskNumber) + replicate('0', 32-@maskNumber)

	--select @mask

	insert into @temp1
	select substring(e.bin, v.number+1, 1) bin, substring(e.mask, v.number+1, 1) mask
	from (select @binaryString bin, @mask mask) e
	join master..spt_values v on v.number < len(e.bin)
	where v.type = 'P'

	declare @binaryResult varchar(32) =  (SELECT
	   stuff( (SELECT cast(bin as int) & cast(mask as int)
				   FROM @temp1
				   FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)')
				,1,0,''))

	--select @binaryResult

	declare @result varchar(16) = cast(dbo.as_BinaryToDecimal(substring(@binaryResult, 0, 9)) as nvarchar) + '.' + 
									cast(dbo.as_BinaryToDecimal(substring(@binaryResult, 9, 8)) as nvarchar) + '.' +
									cast(dbo.as_BinaryToDecimal(substring(@binaryResult, 17, 8)) as nvarchar) + '.' +
									cast(dbo.as_BinaryToDecimal(substring(@binaryResult, 25, 8)) as nvarchar)

	return @result 

END
GO

Falcon Space - функциональная веб-платформа разработки на узком стеке MS SQL/Bootstrap. Вводная по Falcon Space
Насколько полезной была статья?

Google поиск по нашей документации

Falcon Space

Это снижение стоимости владения

за счет меньшего количества людей для поддержки и узкого стека разработки. Про снижение стоимости владения продуктом

Это быстрое внесение изменений

по ходу эксплуатации программы. Как создается функционал на платформе

Это простой удобный интерфейс

адаптация под мобильные устройства. Про юзабилити платформы

Нужна бесплатная консультация?
Получить оценку проекта
Создайте концепцию проекта на основе нашего шаблона и получите оценку проекта в виде КП.
Демо-сайт решений
Базисные решения, которые можно гибко адаптировать под себя: менять внешний вид, бизнес-логику и даже структуру базы данных.