Выгода от использования Falcon Space
В 2-3 раза экономнее и быстрее, чем заказная разработка
Более гибкая, чем коробочные решения и облачные сервисы
Используйте готовые решения и изменяйте под свои потребности
Пополнение средств через 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
Дополнительные функции:
create FUNCTION [dbo].[as_strToBase64]
(
@inputString NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN (
SELECT
CAST(N'' AS XML).value(
'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
, 'NVARCHAR(MAX)'
) Base64Encoding
FROM (
SELECT dbo.as_NCharToUTF8Binary(@inputString, 0) AS bin
) AS bin_sql_server_temp
)
end
create function dbo.as_NCharToUTF8Binary(@txt NVARCHAR(max), @modified bit)
returns varbinary(max)
as
begin
-- Note: This is not the fastest possible routine.
-- If you want a fast routine, use SQLCLR
set @modified = isnull(@modified, 0)
-- First shred into a table.
declare @chars table (
ix int identity primary key,
codepoint int,
utf8 varbinary(6)
)
declare @ix int
set @ix = 0
while @ix < datalength(@txt)/2 -- trailing spaces
begin
set @ix = @ix + 1
insert @chars(codepoint)
select unicode(substring(@txt, @ix, 1))
end
-- Now look for surrogate pairs.
-- If we find a pair (lead followed by trail) we will pair them
-- High surrogate is \uD800 to \uDBFF
-- Low surrogate is \uDC00 to \uDFFF
-- Look for high surrogate followed by low surrogate and update the codepoint
update c1 set codepoint = ((c1.codepoint & 0x07ff) * 0x0800) + (c2.codepoint & 0x07ff) + 0x10000
from @chars c1 inner join @chars c2 on c1.ix = c2.ix -1
where c1.codepoint >= 0xD800 and c1.codepoint <=0xDBFF
and c2.codepoint >= 0xDC00 and c2.codepoint <=0xDFFF
-- Get rid of the trailing half of the pair where found
delete c2
from @chars c1 inner join @chars c2 on c1.ix = c2.ix -1
where c1.codepoint >= 0x10000
-- Now we utf-8 encode each codepoint.
-- Lone surrogate halves will still be here
-- so they will be encoded as if they were not surrogate pairs.
update c
set utf8 =
case
-- One-byte encodings (modified UTF8 outputs zero as a two-byte encoding)
when codepoint <= 0x7f and (@modified = 0 OR codepoint <> 0)
then cast(substring(cast(codepoint as binary(4)), 4, 1) as varbinary(6))
-- Two-byte encodings
when codepoint <= 0x07ff
then substring(cast((0x00C0 + ((codepoint/0x40) & 0x1f)) as binary(4)),4,1)
+ substring(cast((0x0080 + (codepoint & 0x3f)) as binary(4)),4,1)
-- Three-byte encodings
when codepoint <= 0x0ffff
then substring(cast((0x00E0 + ((codepoint/0x1000) & 0x0f)) as binary(4)),4,1)
+ substring(cast((0x0080 + ((codepoint/0x40) & 0x3f)) as binary(4)),4,1)
+ substring(cast((0x0080 + (codepoint & 0x3f)) as binary(4)),4,1)
-- Four-byte encodings
when codepoint <= 0x1FFFFF
then substring(cast((0x00F0 + ((codepoint/0x00040000) & 0x07)) as binary(4)),4,1)
+ substring(cast((0x0080 + ((codepoint/0x1000) & 0x3f)) as binary(4)),4,1)
+ substring(cast((0x0080 + ((codepoint/0x40) & 0x3f)) as binary(4)),4,1)
+ substring(cast((0x0080 + (codepoint & 0x3f)) as binary(4)),4,1)
end
from @chars c
-- Finally concatenate them all and return.
declare @ret varbinary(max)
set @ret = cast('' as varbinary(max))
select @ret = @ret + utf8 from @chars c order by ix
return @ret
end
Google поиск по нашей документации
- Шаг 1. Создать концепт проекта
- Шаг 2. Получить оценку бюджета (КП)
- Шаг 3. Заключить договор
- Шаг 4. Создать совместно техническое задание
- Шаг 5. Поэтапная реализация проекта