Пополнение средств через 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

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