Пополнение средств через Yookassa (Юкасса)

Введение

В этой статье рассмотрим как сделать прием платежей через Юкасса. 

Предполагается, что мы создали фин операцию заранее и переводим пользователя на страницу оплаты по этому финансу. 

Пользователь оплачивает через шлюз и по вебхуку изменяется состояние нашей фин операции на Оплачено

Как реализовать оплату через Юкасса

1. Регистрируемся на Юкасса, создаем тестовый магазин и получаем ShopId, SecretKey. 

Заносим эти настройки в /settings - коды настроек yookassa.payment_shopId, yookassa.payment_secretKey

Создание тестового магазина https://yookassa.ru/docs/support/merchant/payments/implement/test-store

2. Создаем страницу pay, где на входе идет financeGuid (доступ можно поставить all, если оплата должна быть доступна неавторизованным пользователям по уникальной ссылке).

Текст страницы pay: 

{sp-text}

JS страницы pay: 

as.formcallbacks["yookassapay_saveItem"] = function(data, params){
    try {
     // alert(JSON.parse(data.additionalData)[0].msg);
      let response = JSON.parse(JSON.parse(data.additionalData)[0].msg)
      location.assign(response.confirmation.confirmation_url)
    } catch(error) {
      console.log("yookassapay_saveItem Error", error)
      console.log("yookassapay_saveItem Data", data)
      return false
    }
}

SQL страницы pay (возможно изменение SQL под свои объекты): https://pastebin.com/KtC8ddsf

3. Создаем форму yookassapay - без столбцов, только кнопка Оплатить. 

GetItem формы: 

CREATE PROCEDURE [dbo].[fm_yookassapay_getItem]
    @itemID nvarchar(256),	
	@username nvarchar(256)
AS
BEGIN	
	-- SELECT 1
	select 1 
	-- SELECT 2
	select ' ' Title, '' Subtitle, 'h2' HeaderTag, 1 LineLabel	
END

SaveItem формы (вызываем внешнее действие Запрос API):

CREATE PROCEDURE [dbo].[fm_yookassapay_saveItem]
   @username nvarchar(256), 
   @itemID nvarchar(128),
   @parameters ExtendedDictionaryParameter readonly
AS
BEGIN
	declare @financeGuid uniqueidentifier = try_convert(uniqueidentifier, @itemID)
	declare @amount decimal(18,2) = (select [sum] from fin_finances where guid = @financeGuid)
    declare @data nvarchar(max) = '{"amount": ' + cast(@amount as nvarchar(128)) + ', "financeGuid":"' + @itemID + '"}'
    select 1 Result, ' ' Msg
    select 'apirequest' type, 'yookassa_payments' code, 'json_data' p1_name, @data p1_value
END

4. Реализуем исходящий запрос API yookassa_payments, который в итоге выдаст URL, на который мы перенаправим пользователя для оплаты (через JS на странице pay). 

Процедура Request: 

CREATE PROCEDURE [dbo].[api_yookassa_payments_request]
	@parameters ExtendedDictionaryParameter READONLY,  -- входящие параметры для внутренней обработки (используйте Key, Value2)
	@username nvarchar(32)  -- текущий пользователь.
AS
BEGIN
	declare @data nvarchar(max) = isnull((select value2 from @parameters where [Key] = 'json_data'), '0')
    declare @shopId nvarchar(max) = dbo.as_setting('yookassa.payment_shopId', '')
    declare @secretKey nvarchar(max) = dbo.as_setting('yookassa.payment_secretKey', '')
    declare @domain nvarchar(max) = dbo.as_setting('domain', 'site.ru')
    declare @return_url nvarchar(max) = 'https://' + @domain
    
    declare @url nvarchar(max) = 'https://api.yookassa.ru/v3/payments'
    declare @json nvarchar(max) = '{}'
    declare @Authorization nvarchar(max) = 'Basic ' + dbo.as_strToBase64(@shopId + ':' + @secretKey)
    declare @financeGuid uniqueidentifier = try_convert(uniqueidentifier,  json_value(@data, '$.financeGuid'))
    
    declare @financeID int, @amount int 
    select  top 1 @financeID = id, @amount = [sum] from fin_finances where guid = @financeGuid
 	declare @IdempotenceKey nvarchar(max) =  convert(nvarchar(max), @financeGuid) + 'x5'
	set @data = json_modify(@data, '$.paymentID', convert(nvarchar(max), @financeGuid))

    set @json = '{' +
                '"amount": { "value": "' + cast(@amount as nvarchar) + '", "currency": "RUB" }, ' +
                '"capture": true,' +
                '"confirmation": { "type": "redirect", "return_url": "' + @return_url + '" }, ' +
                '"description": "Оплата  '+cast(@amount as nvarchar)+' руб. по счету #'+cast(@financeID as nvarchar)+' на сайте ' + @domain + '", ' +
                '"metadata": ' + @data +
                '}'
	execute as_print @json
    
    select '' Msg, 1 Result, @url Url

    select 'Authorization' name, @Authorization value, 'header' [type]
    union
    select 'Idempotence-Key' name, @IdempotenceKey value, 'header' [type]
    union
    select 'json' name, @json value, 'json' [type]
END

Процедура Response: 

CREATE PROCEDURE [dbo].[api_yookassa_payments_response]
	@response nvarchar(max),
	@parameters ExtendedDictionaryParameter READONLY, 	
	@username nvarchar(32)
AS
BEGIN
	declare @error nvarchar(16) = json_value(@response, '$.type')

    if @error is not null begin
        select json_value(@response, '$.code') + ': ' + json_value(@response, '$.description') Msg, 0 Result, @response Response
        return
    end

    -- execute as_print @response
   
    select '' Msg, 1 Result, @response Response
END

5. Реализуем входящий метод API с кодом paynotifyyoo (вебхук, который будет вызывать Юкасса для отметка платежка как Проведен). 

В настройках юКассы мы должны указать вебхук уведомления о статусе платежа - в тестовом магазине раздел Интеграции / HTTP уведомления: https://{ мой сайт }/api/action/paynotifyyoo

О каких событиях уведомлять: payment.succeeded

Процедура API метода: 

CREATE PROCEDURE [dbo].[api_yoocassa_paynotifyyoo]
@parameters ExtendedDictionaryParameter READONLY,
@username nvarchar(256)
as
begin
    declare @InputStream nvarchar(max) = isnull((select Value2 from @parameters where [Key] = 'InputStream'), '{}')
    declare @ip nvarchar(max) = isnull((select Value2 from @parameters where lower([Key]) = 'userip'), '')
	exec as_print @str='api_yoocassa_paynotifyyoo'
	exec as_print @str = @ip
    
    /*
185.71.76.0/27
185.71.77.0/27
77.75.153.0/25
77.75.156.11
77.75.156.35
77.75.154.128/25
2a02:5180::/32
    */
    if(left(@ip,10) not in (
    	left('185.71.76.0', 10), left('185.71.77.0', 10), left('77.75.153.0', 10), left('77.75.156.11', 10), left('77.75.156.35', 10), 
      left('77.75.154.128', 10)
    ))begin 
    	select 0 Result, 'Yookassa notify from bad IP' Msg
        exec as_print @str='Yookassa notify from bad IP'
        return	
    end 
 
	execute as_print @InputStream
    declare @object nvarchar(max) = json_query(@InputStream, '$.object')
    declare @id nvarchar(64) = json_value(@object, '$.id')
    declare @status nvarchar(64) = json_value(@object, '$.status')
    declare @data nvarchar(max) = json_query(@object, '$.metadata')
    declare @financeGuid uniqueidentifier = try_convert(uniqueidentifier, json_value(@data, '$.financeGuid'))
    declare @sum decimal(18, 2) = isnull(try_cast(json_value(@object, '$.amount.value') as decimal(18, 2)), 0)
 
    if @status = 'succeeded' begin
        -- выполняем фиксацию факта Платеж подтвержен 
        declare @orderID int , @financeSum decimal(18,2), @clientID int, 
        		@orderInstanceID int, @currentFinanceStatus nvarchar(max), @financeID int
        select @financeID = id, @orderID = relatedItemID, @financeSum= [sum], 
        	@clientID = (select clientID from crm_orders where id = f.relatedItemID),
            @orderInstanceID = (select instanceID from crm_orders where id = f.relatedItemID), 
            @currentFinanceStatus = (select top 1 code from fin_financeStatuses where id = f.statusID)
        from fin_finances f where guid = @financeGuid
       
        if(@currentFinanceStatus in ('deleted', 'done', 'hold')) begin 
        	select 'Payment is already done or has deleted, hold status! ' Msg, 0 Result, 0 errorCode
        	execute as_print 'YooKassa Payment is already done or has deleted, hold status!'
        	return
        end 
        
        if(abs(@sum-@financeSum)>1) begin 
        	select 'Different payment sum! ' Msg, 0 Result, 0 errorCode
        	execute as_print 'YooKassa Different payment sum'
        	return
        end 
        
        declare @res  table(result bit, msg nvarchar(max))
		insert into @res 
    	exec [dbo].[crm_doneOrderPayment]
			@financeID = @financeID,	
			@username = @username, 
            @isGate = 1
		execute as_print 'Yookassa payments is confirmed'        
        
		-- SELECT 1 - вывод метаданных о результате операции метода API 
		select '' Msg, 1 Result, 0 errorCode
		-- SELECT 2
    	select 1       
    
    	
    	 
    	-- SELECT 3 Уведомить каких-то пользователей
	-- ...
        
        return
    end
    -- SELECT 1 - вывод метаданных о результате операции метода API 
    select '' Msg, 1 Result, 0 errorCode
end
	

Этот метод проверит IP источника запроса, а также обновит статус транзакции в случае если пришло уведомление о том, что платеж прошел. 

Проверка подлинности уведомления
https://yookassa.ru/developers/using-api/webhooks#object-status-verify

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

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

Falcon Space

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

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

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

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

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

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

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