Sign In Создать аккаунт








Tariffs
Limits
Home

Динамическая библиотека Unitransfer.DLL


В данном разделе описывается реализация информационного обмена между IM-клиентом и сервером платежной системы «Delta Key» через динамическую библиотеку.

1.Основные принципы информационного обмена
2.Описание интерфейсов
2.1.Описание интерфейса IApi
  • Общие сведения
  • Настройка параметров
  • Информатор-OnLine
  • Дополнительные функции интерфейса IApi
  • Описание вспомогательных структур, используемых в интерфейсе IApi
    2.2.Интерфейс IResultCMD
    2.3.Интерфейс ICMD
  • Прямая отправка данных на сервер по протоколу DKCP
  • Получение курса конвертации валют
  • Получение ставок комиссии по операции
  • Авторизация пользователя на сервере платежной системы Delta Key
  • Перевод средств другому пользователю системы
  • Моментальная оплата
  • Финансовая статистика
  • Получение сведений о балансе счета
  • Ввод средств (пополнение счета)
  • Вывод средств
  • Электронные чеки
  • Пластиковые карты
  • Платежные требования
  • Безопасность
  • Тарифы
    3.Финансовая модель партнерства

    1. Основные принципы информационного обмена.


    Реализация сервиса ICQMoney основана на упрощенном варианте DKCP-протокола. Обмен данными происходит по протоколу HTTPS путем отправки запросов клиентским приложением на скрипты сервера. Для удобства работы с сервисом Департаментом IT-технологий системы «Delta Key» реализована DLL-библиотека, которая инкапсулирует в себе транспортную часть по обмену данными с сервером. Для использования библиотеки внешними разработчиками в среде программирования Delphi предоставляется также модуль, содержащий описание интерфейса для доступа к функциям класса динамической библиотеки.

    Интерфейс содержит в себе описание нескольких классов и структур данных, которые можно разделить на три группы:

    1.Вспомогательные функции и доступ к объектам других интерфейсов. Содержит описание интерфейса IApi. Объект типа IApi должен быть создан перед использованием функций библиотеки. При создании данного объекта будут автоматически созданы объекты других групп, и вызов их осуществляется через обращения к данным объектам как к полям интерфейса IApi.

    2.Отправка данных на сервер. Содержит следующие интерфейсы:
    a.ICMD – интерфейс для отправки и расшифровки команд сервером;
    b.IResultCMD – интерфейс для хранения, парсировки и выборки сведений из ответа сервера на запрос;
    c.TResultFields, TResultAnswer, TResultAnswers – структуры для хранения данных ответа сервера.

    3.Форматирование и представление данных. Описывает интерфейс IFace. Использование данного интерфейса позволяет облегчить отображение данных, например, в компонентах TStringGrid, TComboBox и других. Использование данного интерфейса не является обязательным в приложении.

    К содержанию

    2. Описание интерфейсов.


    Вся работа с DLL реализуется через объект типа IApi, поэтому при входе в программу пользователя или при первом обращении к серверу этот объект должен быть создан. Для этого в секции uses необходимо подключить модуль uApiInterface, и объявить доступную глобально переменную типа IApi, к примеру, в секции public главной формы приложения. Глобальность переменной необходима для использования одного объекта во всех формах, однако при желании разработчика данная рекомендация может быть проигнорирована.

    Создание объекта интерфейса IApi происходит с помощью функции, экспортируемой из DLL функции CreateApiInterface. В эту функцию необходимо передать два параметра: код программы разработчика (program_code) и путь к файлу сохранения истории Информатора-OnLine (history_path).

    function CreateApiInterface(program_code: Integer; history_path: WideString): IApi; stdcall; external 'ICQMoney';

    Рассмотрим пример подключения модуля и создания объекта:

    unit MainUnit;
    
    interface
    
    uses
      uApiInterface, …;
    
    type
      TMainForm = class(TForm)
      ...
      public
        api: IApi;
        ...
      end;
    
    var
      function CreateApiInterface(program_code: Integer; history_path: 
           WideString): IApi; stdcall; external 'ICQMoney'; 
      ...
    
    implementation
    
    ...
    
    procedure TMainForm.FormCreate(Sender: TObject);
    begin
    ...
    
      MainForm.api := CreateApiInterface(3001, 
              ExtractFilePath(Application.ExeName));
      
      // чтобы без входа пользоваться сервисами – раскомментируйте данную строку
      //api.SetUin('96969696');
    end;
    
    procedure TMainForm.ICQWrapLogin(Sender: TObject);
    begin
      // Авторизация пользователя на сервере ICQ
      ...
      MainForm.api.SetUin(ICQClient.ScreenName);
      MainForm.api.CMD.Authorize;
      ...
    end;
    
    ...
    end.

    К содержанию

    2.1. Описание интерфейса IApi.


    Общие сведения


    Данный интерфейс предоставляет доступ к основным свойствам и функциям DLL. Все обращения к серверу и DLL необходимо производить через объект этого интерфейса.

    type
    
    IApi = interface(IInterface)
    
        procedure SetUin(Uin: WideString); stdcall; 
        procedure SetProxyParams(basicAuthentication: Boolean=False;
          ProxyServer: WideString=''; ProxyPort: Integer=0;
          ProxyUsername: WideString=''; ProxyPassword: WideString=''); stdcall;
        procedure SetReturnError(return_error: Boolean=False); stdcall;
    
        function RandomPwd(PWLen: integer): WideString; stdcall;
        function SplitString(Source, Divider: WideString; const
          DeletingSymbol: WideString=''; TrimSpaces: Boolean=False): TStringArr; 
          stdcall;
        function SplitInteger(Source, Divider: WideString): TIntegerArr; stdcall;
        function InArray(arr: TStringArr; str: WideString): Boolean; overload; 
          stdcall;
        function InArray(arr: TIntegerArr; int: Integer): Boolean; overload; 
          stdcall;
    
        function md5(Const data: WideString): WideString; stdcall;
    
        function DateToMysqlformat(dt: TDate): WideString; stdcall;
        function DateTimeToMysqlformat(dt: TDate; tm: TTime): WideString; 
          stdcall;
        function DateFromMysqlformat(dt: WideString): TDate; stdcall;
        function DateTimeFromMysqlformat(dt: WideString): WideString; stdcall;
    
        function PADR(Src: WideString; Lg: Integer): WideString; stdcall;
        procedure StrToClipbrd(StrValue: WideString); stdcall;
    
        function GetMessages(fromServer: Boolean=True): TInforArray; stdcall;
        function SetMessageRead(num: Int64): Boolean; stdcall;
        function SetMessageReadAll: Boolean; stdcall;
        function DelMessage(num: Int64): Boolean; stdcall;
        function DelMessages(): Boolean; stdcall;
    
        function CheckNewMessages(var balance: double): Boolean; stdcall;
    
        function GetCMD: ICMD; stdcall;
        function GetFace: IFace; stdcall;
        function GetUserConnected: Boolean; stdcall;
        
        property CMD: ICMD read GetCMD; stdcall;
        property Face: IFace read GetFace; stdcall;
        property UserConnected: Boolean read GetUserConnected; stdcall;
        
      end;

    Свойства

  • property CMD: ICMD read GetCMD;


  • Позволяет получить доступ к объекту ICMD (см. описание данного интерфейса).

  • property Face: IFace read GetFace;


  • Позволяет получить доступ к объекту IFace (см. описание данного интерфейса).

  • property UserConnected: Boolean read GetUserConnected;


  • Позволяет проверить состояние авторизации пользователя в системе ICQMoney. Следует отметить, что проверка данного свойства не обязательна при вызове функций, отправляющих запрос на сервер Delta Key, так как при необходимости пользователю автоматически будет предложено пройти авторизацию.

    Методы

    Кроме того, что интерфейс IApi реализует доступ к другим объектам DLL, он также реализует собственный набор сервисов, работа с которыми происходит путем обращения к методам данного интерфейса.

    На одну из функций следует обратить особое внимание: SetUin. Для того, чтобы пользователь мог авторизоваться на сервере Delta Key и пользоваться сервисами ICQMoney, сразу после создания объекта и коннекта к серверу ICQ следует вызвать эту функцию и передать ей в качестве параметра UIN пользователя.

  • procedure SetUin(Uin: WideString); stdcall;


  • К содержанию

    Настройка параметров


  • procedure SetUin(Uin: WideString); stdcall;


  • Для того, чтобы пользователь мог авторизоваться на сервере Delta Key и пользоваться сервисами ICQMoney, сразу после создания объекта и коннекта к серверу ICQ следует вызвать эту функцию и передать ей в качестве параметра UIN пользователя.

  • procedure SetProxyParams(basicAuthentication: Boolean=False; ProxyServer: WideString=''; ProxyPort: Integer=0; ProxyUsername: WideString=''; ProxyPassword: WideString=''); stdcall;


  • Данная процедура устанавливает параметры прокси-сервера для подключения к сети Internet. В эту функцию передаются адрес прокси-сервера, порт, при необходимости – логин и пароль пользователя для авторизации на сервере прокси.

  • procedure SetReturnError(return_error: Boolean=False); stdcall;


  • Функция устанавливает флаг необходимости возвращать неуспешный результат запроса к серверу вместо генерации исключения.

    К содержанию

    Информатор-OnLine


    Информатор-OnLine позволяет отслеживать новые события пользователя, такие как поступление средств, выставленных счетов, сообщений с сервера и от других пользователей.


    Для того, чтобы в приложении реализовать работу с Информатором, разработчику предлагается запрашивать список новых сообщений через определенные промежутки времени, для чего идеальным является позиционирование на главной форме приложения объекта типа TTimer.

  • function CheckNewMessages(var balance: double): Boolean; stdcall;


  • Данная функция проверяет наличие новых сообщений для пользователя на сервере. В параметре balance возвращается текущий баланс счета пользователя. Рекомендуется вызывать функцию через определенные промежутки времени (рекомендуемая частота – один вызов в 1-10 минут), и/или после проведения финансовых операций пользователем (например, перевод пользователю; в этом случае таким образом можно обновить состояние баланса счета с сервера).

  • function GetMessages(fromServer: Boolean=True): TInforArray; stdcall;


  • Получает сообщения с сервера (Информатор On-Line). Параметр fromServer определяет, необходимо ли запрашивать данные с сервера, или получить их из локального файла. Получение данных с локального сервера целесообразно, если перед ней была вызвана функция CheckNewMessages, которая получила последние данные с сервера, и записала их в локальный файл. Возвращает структуру TInforArray (см. описание).

  • function SetMessageRead(num: Int64): Boolean; stdcall;


  • Функция SetMessageRead устанавливает флаг прочтения сообщения (после установки данного флага сообщение может считаться прочтенным и быть выделено менее ярко, чем новые сообщения). Не производит запрос к серверу, работает с локальным файлом. В качестве параметра num передается уникальный номер сообщения. В случае успеха возвращает True.

  • function SetMessageReadAll: Boolean; stdcall;


  • Функция SetMessageReadAll помечает все сообщения как прочтенные. Не производит запрос к серверу, работает с локальным файлом. В случае успеха возвращает True.

  • function DelMessage(num: Int64): Boolean; stdcall;


  • Удаляет все сообщения из локального файла. В качестве параметра num передается уникальный номер сообщения. В случае успеха возвращает True.

  • function DelMessages(): Boolean; stdcall;


  • Удаляет все сообщения из локального файла. В случае успеха возвращает True.

    В приведенном ниже примере показана реализация работы приложения с Информатором-OnLine.

    type
      TMainForm = class(TForm)
        tmrBalance: TTimer;
        ...
      private
        procedure UpdateMessages;
        procedure UpdateBalance(balance: double);
        ...
      end;
    
    ...
    
    procedure TMainForm.tmrBalanceTimer(Sender: TObject);
    begin
      if not MainForm.api.UserConnected then
        Exit;
      UpdateMessages;
    end;
    
    procedure TMainForm.UpdateMessages;
    var
      balance: double;
      has_message: Boolean;
    
      infor: TInforArray;
      DA: TJvDesktopAlert;
      Row: Integer;
      msg: WideString;
    begin
      try
        tmrBalance.Enabled := False;
        Application.ProcessMessages;
        has_message := MainForm.api.CheckNewMessages(balance);
        UpdateBalance(balance);
    
        if has_message then
        begin
    
          infor := MainForm.api.GetMessages(False);
    
          msg := '';
          for Row:=0 to Length(infor)-1 do 
          begin
            msg := msg + infor[Row].title + #13;
          end;
    
          DA := TJvDesktopAlert.Create(Self);
          DA.Image.LoadFromFile(mypath + 'Skins\Images\moneta.ico');
          DA.Options := FOptions;
          DA.AutoFree := true;
          DA.AutoFocus := false;
    
          DA.HeaderText := 'Сервисное сообщение:';
          DA.MessageText := msg;
          DA.StyleHandler.DisplayDuration := 4000;
          DA.Location.Position := TJvDesktopAlertPosition(3);
          DA.Execute;
        end;
      finally
        tmrBalance.Enabled := True;
      end;
    
    end;
    
    procedure TRoasterForm.UpdateBalance(balance: double);
    begin
      // Обновление баланса
      lblBalance.Caption := 'Баланс: ' + VarToStr(balance) + ' UNI';
    end;

    К содержанию

    Дополнительные функции интерфейса IApi


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

  • function RandomPwd(PWLen: integer): WideString; stdcall;


  • Генерирует случайную последовательность символов заданной в параметре PWLen длины. Может использоваться для генерации кода протекции при переводе средств другому пользователю.

  • function SplitString(Source, Divider: WideString; const DeletingSymbol: WideString=''; TrimSpaces: Boolean=False): TStringArr; stdcall;


  • Разбивает строку (Source) на части и заносит их в массив строк. При этом разделителем является строка, переданная в параметре Divider. Например, строка 'one,two,three' при разделителе «запятая» будет занесена в массив из трех элементов.

  • function SplitInteger(Source, Divider: WideString): TIntegerArr; stdcall;


  • То же, что и функция SplitString, только разбивает строку, содержащую в себе целые числа (например, '1,2,3'), и возвращает массив целых чисел.

  • function InArray(arr: TStringArr; str: WideString): Boolean; overload; stdcall;


  • Проверяет, является ли параметр str идентичным одному из элементов массива arr.

  • function InArray(arr: TIntegerArr; int: Integer): Boolean; overload; stdcall;


  • То же, что и функция InArray, с той лишь разницей, что работает с целочисленным массивом.

  • function md5(Const data: WideString): WideString; stdcall;


  • Возвращает хэш строки data, вычисленный по алгоритму md5.

  • function DateToMysqlformat(dt: TDate): WideString; stdcall;


  • Форматирует дату dt в формат MySQL (ГГГГ-ММ-ДД). Может быть использована при передаче параметра в запросе к серверу Delta Key.

  • function DateTimeToMysqlformat(dt: TDate; tm: TTime): WideString; stdcall;


  • Форматирует дату и время (dt и tm) в формат MySQL (ГГГГ-ММ-ДД чч:мм:сс). Может быть использована при передаче параметра в запросе к серверу Delta Key.

  • function DateFromMysqlformat(dt: WideString): TDate; stdcall;


  • Преобразует строковый параметр dt в дату. Параметр dt должен быть представлен строкой в формате ГГГГ-ММ-ДД.

  • function DateTimeFromMysqlformat(dt: WideString): WideString; stdcall;


  • Преобразует строковый параметр dt в строку, соответствующую формату ДД.ММ.ГГГГ чч:мм:сс. Параметр dt должен быть представлен строкой в формате ГГГГ-ММ-ДД чч:мм:сс.

  • function PADR(Src: WideString; Lg: Integer): WideString; stdcall;


  • Дополняет строку Src пробелами справа до заданной в параметре Lg длины.

  • procedure StrToClipbrd(StrValue: WideString); stdcall;


  • Копирует строку в буфер обмена.

    К содержанию

    Описание вспомогательных структур, используемых в интерфейсе IApi


  • TInformRecord = record
        num, subj, subj_param: WideString;
        corr, id_corr, dt, read: WideString;
        title, messages: WideString;
      end;

  • Представляет информацию об одном сообщении с сервера, и содержит следующие данные:
  • num – уникальный номер сообщения. Данный номер может использоваться для установки пометки о прочтении сообщения, для удаления сообщения из списка сообщений.
  • subj – тип сообщения. Может быть представлено одним из следующих значений: 1 – счет на оплату, 2 – сообщение от пользователя, 3 – приход средств, 4 – информация от системы, 5 – прочее. В зависимости от типа сообщения пользователю должно быть предложено одно из возможных действий. Например, если это поступление средств, то пользователь может посмотреть все параметры прихода (когда, от кого и т.п., а в случае платежа с протекцией еще и ввести код протекции для завершения платежа). Если тип сообщения – выставленный счет на оплату, то у пользователя должна быть возможность оплатить или отказаться от оплаты счета.
  • subj_param – параметр типа сообщения. Здесь содержится некое целое число, которое в зависимости от типа сообщения несет разную смысловую нагрузку. Например, для выставленного счета это будет номер выставленного счета, для прихода средств – номер транзакции.
  • corr – имя корреспондента. Например, если это приход средств, то здесь будет содержаться имя отправителя.
  • id_corr – ID корреспондента. Уникальный идентификатор пользователя, участвующего в операции.
  • dt – дата наступления события. Формат даты – ГГГГ-ММ-ДД чч:мм:сс.
  • read – признак прочтения сообщения (1 – сообщение прочитано ранее, 0 – новое сообщение).
  • title – заголовок сообщения. Например, «Выставлен счет на оплату №19953» или «Сообщение от пользователя N».
  • messages – текст сообщения. Присутствует, если тип сообщения – 2,4 или 5, при выставленном счете и приходе средств чаще всего остутствует.

  • TInforArray = array of TInformRecord;


  • Массив переменных типа TInformRecord. Данная структура возвращается при вызове функции получения сообщений сервера (GetMessages), и содержит в себе полный список всех сообщений сервера в Информаторе-OnLine.

  • TStringArr = array of WideString;


  • Массив строк, используется в функциях SplitString, InArray и других.

  • TIntegerArr = array of Integer;


  • Массив целых чисел, используется в функциях SplitInteger, InArray и других.

    Также в интерфейсе IApi содержатся свойства типа ICMD, IFace, которые описаны ниже в отдельных параграфах. Через эти свойства происходит обращение к объектам указанных типов.

    К содержанию

    2.2. Интерфейс IResultCMD


    В результате каждого запроса на сервер (и в качестве результата выполнения большинства функций интерфейса ICMD) согласно спецификациям протокола DKCP возвращается структура данных IResultCMD. Описывается эта структура следующим образом:

    type 
    
      // элемент answer_params
      TResultAnswer = record
        Name:  WideString;
        Value: WideString;
      end;
    
      TResultAnswers = array of TResultAnswer;
    
      // строка таблицы
      TResultFields = record
        Fields:   array of array of WideString;
        ColName:  array of WideString;
        ColRName: array of WideString;
        RowCount: Integer;
        ColCount: Integer;
      end;
    
      // ответ сервера
      IResultCMD = interface(IInterface)
        function GetAnswerValue(Const AName: WideString):WideString; stdcall;
        function GetFieldValue(Const AField: WideString; Row: Integer=0): 
          WideString; stdcall; 
        function GetFieldRName(Const AField: WideString): WideString; stdcall; 
        function FindFieldValue(Const ASearchField, AResultField, ASearchValue: 
          WideString): WideString; stdcall; 
        function GetTransact: Int64; stdcall;
        function GetExt_transact: Int64; stdcall;
        function GetDate: TDateTime; stdcall;
        function GetStatus: Smallint; stdcall;
        function GetResult: Smallint; stdcall;
        function GetResult_text: WideString; stdcall;
        function GetAnswer: TResultAnswers; stdcall;
        function GetFields: TResultFields; stdcall;
        function GetAll: WideString; stdcall;
    
        procedure SetTransact(pTransact: Int64); stdcall;
        procedure SetExt_transact(pExt_transact: Int64); stdcall;
        procedure SetDate(pDate: TDateTime); stdcall;
        procedure SetStatus(pStatus: Smallint); stdcall;
        procedure SetResult(pResult: Smallint); stdcall;
        procedure SetResult_text(pResult_text: WideString); stdcall;
        procedure SetAnswer(pAnswer: TResultAnswers); stdcall;
        procedure SetFields(pFields: TResultFields); stdcall;
        procedure SetAll(pAll: WideString); stdcall;
    
        property transact:     Int64 read GetTransact write SetTransact;     
        property ext_transact: Int64 read GetExt_transact write SetExt_transact; 
        property date:         TDateTime read GetDate write SetDate;         
        property status:       Smallint read GetStatus write SetStatus;       
        property result:       Smallint read GetResult write SetResult;       
        property result_text:  WideString read GetResult_text write SetResult_text;  
        property Answer:       TResultAnswers read GetAnswer write SetAnswer;       
        property Fields:       TResultFields read GetFields write SetFields;       
        property All:          WideString read GetAll write SetAll;           
      end;

    Таким образом в результате каждой операции возвращаются следующие данные:

  • transact – номер транзакции сервера, созданная в результате команды, или обращение к которой выполнялось в процессе выполнения команды. Представлена длинным целым числом. Может быть равной 0 в случае, если команда не обращается к какой-либо транзакции и не создает ее (получение переменных сервера), либо задействует более одной транзакции (получение статистики операций);
  • ext_transact – номер внешней транзакции; при каждой операции DLL автоматически генерирует уникальный номер, который передает на сервер; в результирующей структуре в параметре ext_transact содержится отправленный во время команды номер;
  • date – дата завершения выполнения команды сервером;
  • status – код статус выполнения операции. Представлен целым числом: 1 – в обработке, 2 – завершена;
  • result – результат выполнения команды. Представлен числом: 0 – успешное выполнение команды, любое другое число – код ошибки (см. справочник ошибок);
  • result_text – текстовая расшифровка результата;
  • Answer – дополнительные сведения, возвращаемые командой (к примеру, рассчитанные комиссии, статистика и прочее). Может быть пустой или содержать неограниченное количество элементов типа TResultAnswer.
  • Fields – в результирующем ответе сервера может содержаться таблица, описываемая структурой TResultFields;
  • All – XML ответа сервера.

    Успешно выполненная операция всегда имеет статус равный двум, и результат равный нулю.

    Все остальные комбинации статусов и результатов следует считать ошибкой. При этом в большинстве случаев статус равен двум, что говорит о том, что сервер закончил обработку запроса пользователя (не важно – успехом или ошибкой), а уже результат показывает пользователю успех операции. Например, пользователь пытается выставить счет незарегистрированному в системе пользователю, в этом случае сервер вернет status=2, result=3005, result_text=’Пользователь не зарегистрирован в ICQMoney’.

    Большинство операций (команд) можно подвести под две категории: команды, которые получают какие-либо сведения (статистику, список карт, счетов, информацию о пользователе и т.д.) и команды, которые изменяют данные на сервере (проведение платежей, заказ карт, установка информации о пользователе, активация электронных чеков и т.д.).

    Операции первой категории возвращают пользователю какую-либо информацию, которая может содержаться либо в табличной части, либо в свойстве Answer. При выполнении операций второй категории обычно необходимо знать только о результате выполнения операции, для чего достаточно лишь проанализировать свойства status и result, а пользователю можно показать result_text.

    Остановимся подробнее на операциях получения информации. Рассмотрим следующий пример:

    Перед совершением операции перевода средств пользователь хочет знать, какая комиссия будет удержана в результате операции. Для того, чтобы получить эту информацию на сервер отправляется команда получения комиссии с помощью вызова функции GetCmdComiss (функция будет подробно описана в соответствующем разделе), которая в результирующей структуре в свойстве Answer будет иметь данные:

    (('pay_minlimit', '0.01'), ('pay_plusmoney', '0.00'), ('min_sum', '0.09'), ('max_sum', '1000000.00'), ('shop_plusmoney', '0.00'), ('shop_maxlimit', '1000000.00'), ('pay_persent', '0.75'), ('shop_persent', '0.00'), ('rate2', '2.793'), ('rate1', '72.75'), ('shop_minlimit', '0.00'), ('pay_maxlimit', '1000000.00'))

    Свойство Answer описано как массив элементов типа TResultAnswer, и каждый из этих элементов определяет один из параметров комиссии. В приведенном примере минимальный лимит комиссии с плательщика составляет 0.01, минимальная сумма платежа – 0.09, процент с плательщика – 0.75 и т.д.

    Для более удобной работы с массивом интерфейс IResultCMD имеет функцию GetAnswerValue, которая по имени переменной находит ее название:

    function GetAnswerValue(Const AName: WideString):WideString; stdcall;

    Например, для отображения значения комиссии с плательщика может быть использован следующий код:

    var
      Res: IResultCMD;
    begin
      Res := MainForm.api.CMD.GetCmdComiss(PChar(cmdName), 0, 999, 810, 840);
      edtMinSum.Text := Res.GetAnswerValue('min_sum');
    end;

    Табличная часть, сохраненная в свойстве Fields, возвращается тогда, когда возвращаемые параметры не могут быть представлены в виде массива с элементами Параметр=Значение.

    Структура IResultFields содержит в себе следующие поля:

  • Fields – двумерный массив значений;
  • ColName – массив названий столбцов;
  • ColRName – массив русскоязычный названий столбцов;
  • RowCount – количество строк;
  • ColCount – количество столбцов.

    Примером такой операции может стать выборка финансовой статистики.

    Например, при выборке статистики параметр Fields может содержать следующее значение:

    ((('1240', '09.10.2007 00:27:32', '-', 'Перевод другому пользователю', '1.00', 'UNI', 'В обработке', '0'), ('1239', '08.10.2007 19:11:40', 'Приход', 'Перевод другому пользователю', '1.00', 'UNI', 'Операция успешно завершена', '1')), ('transact', 'date', 'typelocal', 'code_text', 'sum', 'curr', 'result', 'done'), ('Транзакция', 'Дата', 'Пр./расх.', 'Операция', 'Сумма', 'Валюта', 'Результат', 'Завершена'), 2, 8)

    Данные значения можно представить еще и так:

    Fields ColName ColRName RowCount ColCount
    (('1240', '09.10.2007 00:27:32', '-', 'Перевод другому пользователю', '1.00', 'UNI', 'В обработке', '0'), ('1239', '08.10.2007 19:11:40', 'Приход', 'Перевод другому пользователю', '1.00', 'UNI', 'Операция успешно завершена', '1')) ('transact', 'date', 'typelocal', 'code_text', 'sum', 'curr', 'result', 'done') ('Транзакция', 'Дата', 'Пр./расх.', 'Операция', 'Сумма', 'Валюта', 'Результат', 'Завершена') 2 8

    Более понятным же для понимания является следующая таблица:

    transact /
    Транзакция
    date /
    Дата
    typelocal /
    Пр./расх.
    code_text /
    Операция
    sum /
    Сумма
    curr /
    Валюта
    result /
    Результат
    done /
    Завершена
    1240 09.10.2007 00:27:32 - Перевод другому пользователю 1.00 UNI В обработке 0
    1239 08.10.2007 19:11:40 Приход Перевод другому пользователю 1.00 UNI Операция успешно завершена 1

    Отображение результата очень удобно производить в компонент TStringGrid, для этого достаточно в цикле пройтись по массиву Fields, например, следующим образом:

    procedure TfmFinStat.ShowStat;
    var
      ResShow: IResultCMD;
      Row, Col: Integer;
    begin
      ResShow := MainForm.api.CMD.GetlistFinoperationLight(dtpDateStart.Value, 
        dtpDateEnd.Value, StrToInt(cbTypeLocal.value), cbCode.value);
    
      aGrid.ColCount := Res.Fields.ColCount;
      aGrid.RowCount := Res.Fields.RowCount + 1;
      for Row:=0 to Res.Fields.RowCount-1 do
        for Col:=0 to Res.Fields.ColCount-1 do
          aGrid.Cells[Col, Row+1] := Res.Fields.Fields[Row, Col];
    end;

    Для удобной навигации по структуре интерфейс IResultCMD содержит три функции: GetFieldValue, GetFieldRName, FindFieldValue.

    Функция GetFieldValue позволяет получить значение ячейки таблицы по заголовку столбца и номеру строки:

  • function GetFieldValue(Const AField: WideString; Row: Integer=0): WideString; stdcall;


  • Здесь параметр AField – название столбца, Row – номер строки.

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

    procedure TfmFinStat.ShowStat;
    var
      ResShow: IResultCMD;
      Row, Col: Integer;
    begin
      ResShow := MainForm.api.CMD.GetlistFinoperationLight(dtpDateStart.Value, 
        dtpDateEnd.Value, StrToInt(cbTypeLocal.value), cbCode.value);
    
      aGrid.ColCount := Res.Fields.ColCount;
      aGrid.RowCount := Res.Fields.RowCount + 1;
      for Row:=0 to Res.Fields.RowCount-1 do
      begin
        if Res.Fields.GetFieldValue('done', Row) = '1' then // операция успешная
          for Col:=0 to Res.Fields.ColCount-1 do
            aGrid.Cells[Col, Row+1] := Res.Fields.Fields[Row, Col];
      end;
    end;

    Следующая функция – GetFieldRName – позволяет получить русскоязычный заголовок столбца:

  • function GetFieldRName(Const AField: WideString): WideString; stdcall;


  • В качестве единственного параметра (AField) в нее передается заголовок столбца.

    Например, следующий код выведет результат в грид с заголовками:

    procedure TfmFinStat.ShowStat;
    var
      ResShow: IResultCMD;
      Row, Col: Integer;
    begin
      ResShow := MainForm.api.CMD.GetlistFinoperationLight(dtpDateStart.Value, 
        dtpDateEnd.Value, StrToInt(cbTypeLocal.value), cbCode.value);
    
      aGrid.ColCount := Res.Fields.ColCount;
      aGrid.RowCount := Res.Fields.RowCount + 1;
    
      for Col:=0 to Res.Fields.ColCount-1 do
        aGrid.Cells[Col, 0] := Res.GetFieldRName(Res.Field.ColName[Col]);
    
      for Row:=0 to Res.Fields.RowCount-1 do
      begin
        if Res.Fields.GetFieldValue('done', Row) = '1' then // операция успешная
          for Col:=0 to Res.Fields.ColCount-1 do
            aGrid.Cells[Col, Row+1] := Res.Fields.Fields[Row, Col];
      end;
    end;

    И последняя функция – FindFieldValue – описывается следующим образом:

  • function FindFieldValue(Const ASearchField, AResultField, ASearchValue: WideString): WideString; stdcall;


  • Эта функция позволяет найти значение одной ячейки по известному значению другой. Например, зная номер транзакции из результирующего запроса можно получить сумму следующим образом:

    procedure TfmFinStat.aGridDblClick(Sender: TObject);
    begin
      ShowMessage('Сумма операции: ' + 
           ResShow.FindFieldValue('transact', 'sum', aGrid.Cells[1, aGrid.Row]) +' '+
           ResShow.FindFieldValue('transact', 'curr', aGrid.Cells[1, aGrid.Row]) ) 
    end;

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

    См. также описание функции ShowResInGrid интерфейса IFace, которая позволяет полностью отобразить табличную часть в компоненте TStringGrid.

    К содержанию

    2.3. Интерфейс ICMD


    Интерфейс ICMD предоставляет доступ к командам, посылаемым на сервер. Описывается интерфейс следующим образом:

    type
      ICMD = interface(IInterface)
        function SendCMD(Form: TMultipartForm; const cmd_name: WideString;
          errorBreak: Boolean=true; PersentLast: Word=100; 
          PersentFirst: Word=0): IResultCMD; stdcall;
        function GetRate(curr_from, curr_to: Word; summ: double=-1): double; stdcall;
        function GetCmdComiss(cmd_name: PChar; subj_param: Integer=-1;
          curr_from: Word=999; curr_to1: Word=810; curr_to2: Word=840): IResultCMD; 
          stdcall;
        function Authorize: double; stdcall;
        function PayId(user: Widestring; sum: double; comment: PChar; 
          protect: Boolean=False; protect_code: PChar=nil; 
          protect_day: Byte=3): IResultCMD; stdcall;
        function CreatePayrequest(user: Widestring; sum: double; 
          description: PChar; system_num: Integer): IResultCMD; stdcall;
        function GetTreeMomental(): IResultCMD; stdcall;
        function GetFormFields(Payform: Integer): IResultCMD; stdcall;
        function PayMomental(Form: TMultipartForm; payform: Integer; 
          sum: double): IResultCMD; stdcall;
        function GetResultFinoperation(transact: Int64): IResultCMD; stdcall;
        function GetlistFinoperation(date_start, date_end: WideString;
          time_start: WideString='00:00:00'; time_end: WideString='23:59:59';
          opertype: Shortint=-1; subj: Shortint=-1; 
          object_num: Shortint=-1; status: Shortint=-1; 
          check_num: Int64=-1; optdata: WideString='-1'; 
          subj_param: WideString='-1'; keyt: Int64=-1; num_point: WideString='-1';
          archive: Byte=0; is_limit: Boolean=True; num_limt: Integer=100;
          done: WideString='None'; transact_num: WideString='-1'; 
          curr: WideString='-1'; list_field: WideString='transact,typelocal,
                id_pay,keyt_pay,id_shop,keyt_shop,num_term,num_point,date_pay,curr,sum,
                comiss,itogo,status,result,summ_start,summ_end,date_in,date_out,subj,
                subj_param,object,chek,name_corr,code,done,num_param,
                param,ip'): IResultCMD; stdcall;
        function GetlistFinoperationLight(date_start: TDate=-1; 
          date_end: TDate=-1; typelocal: Shortint=-1; code: WideString='-1';
          transact: WideString='-1'; sum: WideString='-1'; done: WideString='None';
          param: WideString='-1'): IResultCMD; stdcall;
        function GetparamsFinoperation(transact: Integer; 
          only_data: Boolean=True): IResultCMD; stdcall;
        function GetparamsFinoperationLight(transact: Integer):IResultCMD; stdcall;
        function GetBalance(): double; stdcall;
        function GetListBank(country: Smallint=-1): IResultCMD; stdcall;
        function GetBankRequisite(bank_code: Integer): IResultCMD; stdcall;
        function CreateBankOrder(bank_code: Integer; sum: Real): IResultCMD; stdcall;
        function ActiveEcheck(serial: WideString; number, code: Int64): IResultCMD; 
          stdcall;
    
        procedure FromDeltaKey; stdcall;
        procedure FromWebMoney; stdcall;
        procedure FromMoneymail; stdcall;
        procedure FromUkrMoney; stdcall;
        procedure FromMobileWallet; stdcall;
    
        procedure FromRobox; stdcall;
        procedure FromMusicCard; stdcall;
        procedure FromRupay; stdcall;
    
        procedure FromExchanger; stdcall;
    
        function OutDeltakey(keyt: WideString; sum: double): IResultCMD; stdcall;
        function OutWebmoney(wallet: WideString; sum: double): IResultCMD; stdcall;
        function OutYandex(account: WideString; sum: double): IResultCMD; stdcall;
        function OutRupay(account: WideString; sum: double): IResultCMD; stdcall;
        function OutMoneyMail(email: WideString; sum: double): IResultCMD; stdcall;
        function OutMonetaRU(account: WideString; sum: double): IResultCMD; stdcall;
        function OutMusic(account: WideString; sum: double): IResultCMD; stdcall;
        function OutMobile(phone: WideString; sum: double): IResultCMD; stdcall;
        function Out1Wallet(account: WideString; sum: double): IResultCMD; stdcall;
        function OutEPort(number, control_code: WideString; sum: double): IResultCMD; 
          stdcall;
        function CreateEcheck(nominal: double; count: Integer): IResultCMD; stdcall;
        function ChangeLogin(newLogin, newLoginConfirm: WideString): IResultCMD; 
          stdcall;
        function ChangePassword(newPassword, 
          newPasswordConfirm: WideString): IResultCMD; stdcall;
        function ActiveCard(serial, number: Integer; code: Int64): IResultCMD; 
          stdcall;
    
        function GetTypesMCard(): IResultCMD; stdcall;
        function OrderMCard(mcard_type: Smallint): IResultCMD; stdcall;
        function GetListMCard(): IResultCMD; stdcall;
        function RenameMCard(order_num: Integer; new_name: WideString): IResultCMD; 
          stdcall;
        function ActiveMCard(order_num: Integer; cvc: WideString): IResultCMD; 
          stdcall;
        function BlockMCard(order_num: Integer): IResultCMD; stdcall;
        function PayMCard(mcard_num: Int64; sum: double): IResultCMD; stdcall;
        function GetListMCardRequest(): IResultCMD; stdcall;
    
        function PayPayrequest(payrequest_num: Integer): IResultCMD; stdcall;
        function RefusePayrequest(payrequest_num: Integer): IResultCMD; stdcall;
        function GetparamsPayrequest(payrequest_num: Integer): IResultCMD; stdcall;
        function GetlistOutPayrequest(date_start, date_end: TDate; 
          status: Shortint=-1): IResultCMD; stdcall;
    
        function GetlistOutbankCountries: IResultCMD; stdcall;
        function GetlistOutbankRequisite(country: Smallint): IResultCMD; stdcall;
        function OrderBankout(Form: TMultipartForm; country, curr: Smallint; 
          sum: double; full_statement: WideString): IResultCMD; stdcall;
        function EndPayProtect(transact_num: Int64; 
          protect_code: WideString): IResultCMD; stdcall;
    
        procedure GoToCabinetURL; stdcall;
        procedure GoToGeoURL; stdcall;
        procedure GoToMCardURL; stdcall;
    
      end;

    Большинство функций возвращают в качестве параметра объект типа IResultCMD. В одном из следующих параграфов будет подробно описано использование данного интерфейса.

    К содержанию

    Прямая отправка данных на сервер по протоколу DKCP


    Все методы интерфейса можно разделить на две группы: первая группа, которая содержит функцию SendCMD, которая позволяет напрямую отправить запрос на сервер Delta Key, по соответствующему описанию команды в протоколе DKCP-ICQMoney. Все остальные функции скрывают за своей реализацией обращение к серверу через отправку одной команды и получение ответа. По сути, любая команда может быть отправлена через функцию SendCMD, однако использование, к примеру, функций ActiveCard или PayId позволяет избежать изучения тонкостей протокола DKCP и следить за изменениями в реализации прототокла, а также избежать ошибок при форматировании отправляемых на сервер данных.

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

    Способ первый. Отправка данных через функцию SendCMD.

    procedure TfmSendMoney.btnSendClick(Sender: TObject);
    var
      Form: TMultipartForm;
      Res: IResultCMD;
      s: WideString;
    begin
      // Отправка денег
      if sum <= 0 then
        Raise EExternalException.Create('Сумма некорректна!');
    
      if Protect then
        if (protect_code = '') or (protect_day <= 0) then
          Raise EExternalException.Create('Неверные параметры протекции!');
    
      Form := TMultipartForm.Create;
      Form.AddFormField('user', VarToStr(user));
      s := AnsiReplaceStr(VarToStr(sum), DecimalSeparator, '.');
      Form.AddFormField('sum', s);
      Form.AddFormField('date_pay', TdmApi(ownerApi).DateToMysqlformat(date));
      Form.AddFormField('comment', Comment);
      Form.AddFormField('protect', IfThen(protect, '1', '0'));
      if protect then
      begin
        Form.AddFormField('protect_code', protect_code);
        Form.AddFormField('protect_day', IntToStr(protect_day));
      end;
    
      // Запрос платежного пароля
      GetPayPassword;
    
      Res := SendCMD(Form, 'pay_id');
      ShowMessage(Res.result_text);
    end;

    Способ второй. Использование функции PayId.

    procedure TfmSendMoney.btnSendClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      res := MainForm.api.CMD.PayId(StrToInt(user), edtSum.Value,  
                PAnsiChar(mComment.Text), cbProtect.Checked,       
                PAnsiChar(edtProtectCode.Text), edtProtectCount.IntValue);
      ShowMessage(res.result_text);
    end;

    Из приведенных примеров видно, что использование функции PayId хотя и приводит к аналогичным результатам, однако требует гораздо меньшего кода, а также позволяет избежать ошибок с отправкой несоответствующих типов данных и некорректно отформатированных параметров на сервер.

    Однако в некоторых случаях может быть использовано и прямое обращение к функции SendCMD. Через данную функцию можно отправить абсолютно любую команду, описанную в протоколе DKCP-ICQMoney. Это может быть полезно в случае, когда некоторые новые команды уже реализованы разработчиками в протоколе, однако нет возможности либо необходимости обновлять клиентскую библиотеку. В этом случае можно воспользоваться прямой отправкой запроса на сервер платежной системы.

    Именно поэтому начнем рассмотрение с функции SendCMD.

    Описание функции выглядит следующим образом:

  • function SendCMD(Form: TMultipartForm; const cmd_name: WideString; errorBreak: Boolean=true; PersentLast: Word=100; PersentFirst: Word=0): IResultCMD;


  • Передаваемые параметры:

  • Form: TMultipartForm; На самом деле тип TMultipartForm – это синоним типа TIdMultiPartFormDataStream, однако использование синонима позволяет избегать включение модуля IdMultipartFormData во все модули, использующие данную функцию. Тип TIdMultiPartFormDataStream позволяет правильно эмулировать данные, введенные с формы, как если бы это было сделано при заполнении формы в браузере на HTML-странице.
  • Const cmd_name: WideString; Название команды, отправляемой на сервер. Например, команда pay_id выполняет перевод средств другому пользователю системы, create_payrequest выставляет платежное требование, а getlist_countries загружает список стран. Все возможные команды описаны в протоколе DKCP-ICQMoney.
  • errorBreak: Boolean=true; Необязательный параметр, по умолчанию включенный, который определяет, необходимо ли прерывать работу приложения при возврате сервером ошибки (True), или нет (False). Например, при попытке передать средства другому пользователю сервер может вернуть ошибку «Сумма некорректна». Если установлен флаг errorBreak, то пользователю будет показан текст ошибки, выполнение приложения будет прервано, а если флаг не установлен, то будет возвращена структура с описанием ошибки сервера.
  • PersentLast: Word=100; При показе окна со статусом выполнения команды здесь указывается, какой процент выполнения операции будет показан последним. Данный параметр может быть использован, если предполагается, что на расшифровку ответа может потребоваться значительное время, и прогресс-бар должен не доходить до конца при получении ответа от сервера.
  • PersentFirst: Word=0; При показе окна со статусом выполнения команды здесь указывается, какой процент выполнения операции будет показан при начале отправки запроса.

    К содержанию

    Получение курса конвертации валют


    Получение курса конвертации валют может быть реализовано одной из двух функций: GetRate и GetCmdComiss. Вызов функции GetCmdComiss целесообразен при одновременном вычислении курса конвертации и получении ставок комиссии на операцию (описание функции GetCmdComiss см. ниже).

  • function GetRate(curr_from, curr_to: Word; summ: double=-1): double; stdcall;


  • Функция GetRate используется для получения сведений о том, какой курс конвертации установлен на текущий момент в системе. Использование данной функции особенно актуально в свете того факта, что в системе ICQMoney используется валюта «юнит», и пользователям зачастую просто необходимо знать курс данной валюты к одной из международных валют – рубль, доллар, евро и любые другие международные валюты.

    Функция возвращает число с плавающей точкой, характеризующее отношение валюты, из которой конвертируется, в валюту, в которую конвертируется.

    При использовании данной функции необходимо учесть, что у любой валюты системы есть два курса – курс покупки и курс продажи. Таким образом, зная курс отношения доллара к юниту зачастую практически невозможно вычислить обратный курс – юнита к доллару, поэтому необходимо правильно передавать валюты в параметрах.

    Параметры curr_from, curr_to представляют из себя числа до трех цифр, определяющих международный код валюты. Для того, чтобы получить сопоставление названия валют к их числовым кодам, можно послать на сервер команду getlist_curr, которая вернет структуру, содержащую в себе как название валюты, так и ее числовой код. Например, валюта «Российский рубль» имеет числовой код 810, «Доллар США» - 840, «Юнит» - 999 и др.

    Следует также отметить, что платежная система Дельта Кей является мультивалютной, и позволяет пользователям создавать счета в любой валюте, однако сервис ICQMoney предусматривает операции только с одной валютой – юнитом.

    К содержанию

    Получение ставок комиссии по операции


    Получение ставок комиссионных удержаний системы с отдельной операции производится с помощью функции GetCmdComiss. С помощью нее можно не только вычислить сумму комиссии с плательщика и получателя по операции, но и дополнительно получить курс конвертации из одной валюты в двум другим.

  • function GetCmdComiss(cmd_name: PChar; subj_param: Integer=-1; curr_from: Word=999; curr_to1: Word=810; curr_to2: Word=840): IResultCMD;


  • Функция GetCmdComiss позволяет получить комиссию, которая будет удержана в результате выполнения операции. Также функция может вернуть курсы валют, объединив таким образом свой функционал с функцией GetRate (причем функция способна вернуть курс конвертации сразу к двум валютам).

    Передаваемые параметры:

  • cmd_name: PChar; Название команды, например, pay_id, pay_momental и др. Имеет смысл только для финансовых команд.
  • subj_param: Integer=-1; Параметр субъекта. Для некоторых команд для исчисления комиссии нужен также параметр. Например, для моментальной оплаты нужен код платежной формы (код оператора), так как вывод средств на оператора «Билайн GSM» и «Яндекс.Деньги» может иметь разную комиссию.
  • curr_from: Word=999; Для конвертации – код валюты, из которой выполняется конвертация. По-умолчанию – «юнит».
  • curr_to1: Word=810; Для конвертации – код валюты, в которую выполняется конвертация. По-умолчанию – «Рубль России».
  • curr_to2: Word=840; Для конвертации – код валюты, в которую дополнительно выполняется конвертация. По-умолчанию – «Доллар США».

    Возвращаемое значение. В ответ на выполнение операции возвращается структура типа IResultCMD, все значимые данные будут содержаться в поле Answer (см. описание интерфейса IResultCMD и структур TResultAnswer, TResultAnswers):
  • min_sum – минимальная сумма платежа;
  • max_sum – максимальная сумма платежа;
  • pay_persent – процент комиссии с плательщика;
  • pay_plusmoney – дополнительная фиксированная сумма, взимаемая с плательщика помимо процента;
  • pay_minlimit – минимальный лимит комиссии с плательщика;
  • pay_maxlimit – максимальный лимит комиссии с плательщика;
  • shop_persent – процент комиссии с получателя средств;
  • shop_plusmoney – дополнительная фиксированная сумма, взимаемая с получателя помимо процента;
  • shop_minlimit – минимальный лимит комиссии с получателя;
  • shop_maxlimit – максимальный лимит комиссии с получателя;
  • rate1 – курс конвертации валюты, переданной в параметре curr_from, к валюте, переданной в параметре curr_to1;
  • rate2 – курс конвертации валюты, переданной в параметре curr_from, к валюте, переданной в параметре curr_to2.

    Структура Answer типа IResultAnswers все параметры содержит в виде строковых значений, поэтому для использования результатов функции параметры должны быть преобразованы в строки. Разделителем целой и дробной части следует считать точку.

    Ниже приведен пример использования функции.

    Листинг 2.2 – I. Модуль для отображения параметров комиссии с операции.

    unit uRate;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
      Forms, Dialogs, StdCtrls, Math, ExtCtrls, uApiInterface, StrUtils;
    
    type
      TfmRate = class(TForm)
        Label1: TLabel;
        lblNominalCaption: TLabel;
        lblCount: TLabel;
        lblRUR: TLabel;
        lblRate: TLabel;
        Bevel1: TBevel;
        lblItogoCaption: TLabel;
        Label5: TLabel;
        lblItogo: TLabel;
        lblComiss: TLabel;
        Bevel2: TBevel;
        Label2: TLabel;
        Label4: TLabel;
        lblUSD: TLabel;
        procedure FormClick(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormDeactivate(Sender: TObject);
        procedure FormShow(Sender: TObject);
      private
        { Private declarations }
        was_show: Boolean;
      public
        { Public declarations }
        count: double;
        cmdName: WideString;
        subj_param: WideString;
        comiss_field: WideString;
        minus: Boolean;
    
        min_sum, max_sum, user_persent, user_plusmoney, user_maxlimit, 
          user_minlimit, rate1, rate2: double;
      end;
    
    var
      fmRate: TfmRate;
    
    implementation
    
    uses MainUnit;
    
    {$R *.dfm}
    
    procedure TfmRate.FormClick(Sender: TObject);
    begin
      Close;
    end;
    
    procedure TfmRate.FormCreate(Sender: TObject);
    begin
      count := 0;
      was_show := False;
      subj_param := '-1';
      comiss_field := 'pay';
    end;
    
    procedure TfmRate.FormDeactivate(Sender: TObject);
    begin
      Close;
    end;
    
    procedure TfmRate.FormShow(Sender: TObject);
    var
      Res: IResultCMD;
    
    begin
      // Встроенная возможность получения курса, если он не передан
      if not Was_Show then
      begin
        Res := MainForm.api.CMD.GetCmdComiss(PChar(cmdName), 
              StrToInt(subj_param), 999, 810, 840);
        min_sum := StrToFloat(AnsiReplaceStr(Res.GetAnswerValue('min_sum'), '.', 
              DecimalSeparator));
        max_sum := StrToFloat(AnsiReplaceStr(Res.GetAnswerValue('max_sum'), '.', 
              DecimalSeparator));
        user_persent := StrToFloat(AnsiReplaceStr(Res.GetAnswerValue(
              comiss_field+'_persent'), '.', DecimalSeparator));
        user_plusmoney := StrToFloat(AnsiReplaceStr(Res.GetAnswerValue(
              comiss_field+'_plusmoney'), '.', DecimalSeparator));
        user_maxlimit := StrToFloat(AnsiReplaceStr(Res.GetAnswerValue(
              comiss_field+'_maxlimit'), '.', DecimalSeparator));
        user_minlimit := StrToFloat(AnsiReplaceStr(Res.GetAnswerValue(
              comiss_field+'_minlimit'), '.', DecimalSeparator));
        rate1 := StrToFloat(AnsiReplaceStr(Res.GetAnswerValue('rate1'), '.', 
              DecimalSeparator));
        rate2 := StrToFloat(AnsiReplaceStr(Res.GetAnswerValue('rate2'), '.', 
              DecimalSeparator));
      end;
    
      lblCount.Caption := VarToStr(count);
      lblRate.Caption := '1 юнит = '+VarToStr(rate1)+'руб. = $'+VarToStr(rate2) ;
      lblRUR.Caption := VarToStr(RoundTo(count*rate1, -2)) + ' руб.';
      lblUSD.Caption := '$' + VarToStr(RoundTo(count*rate2, -2));
    
      lblComiss.Caption := VarToStr(RoundTo( Max(user_minlimit, 
              Min(user_maxlimit,(count*user_persent/100)+user_plusmoney)),-2 )) +
              ' (' +VarToStr(user_persent)+ '%)';
      if minus then
        lblItogo.Caption := VarToStr( RoundTo(count - Max(user_minlimit, 
              Min(user_maxlimit,(count*user_persent/100)+user_plusmoney)),-2 ) )
      else
        lblItogo.Caption := VarToStr( RoundTo(count + Max(user_minlimit, 
              Min(user_maxlimit,(count*user_persent/100)+user_plusmoney)),-2 ) );
    
      was_show := True;
      Bevel1.Width := 1;
      Bevel2.Width := 1;
      Bevel1.Width := fmRate.Width;
      Bevel2.Width := fmRate.Width -19;
    end;
    
    end.

    Сама форма, используемая в данном примере, выглядит следующим образом:


    А вызываться может форма таким вот образом:


    Здесь на событие нажатия кнопки конвертации написан следующий код:

    procedure TfmSendMoney.btnRateClick(Sender: TObject);
    var
      p: TPoint;
    begin
      if not rate_create then
      begin
        fmRate := TfmRate.Create(Self);
        fmRate.cmdName := 'pay_id';
        rate_create := True;
      end;
      
      fmRate.count := edtSum.Value;
    
      p.X := btnRate.Left + btnRate.Width;
      p.Y := btnRate.Top;
      p := ClientToScreen(p);
    
      fmRate.Left := p.X;
      fmRate.Top := p.Y;
      fmRate.Show;
    end;

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

    К содержанию

    Авторизация пользователя на сервере платежной системы Delta Key


    Операция авторизации может производится по одной из схем: авторизация на сервере после соединения с сервером ICQ, либо авторизация при первом обращении к серверу Delta Key.

  • function Authorize: double;


  • Функция выдает пользователю окно с просьбой ввести логин и пароль. Перед вызовом функции можно убедиться в отсутствии авторизации пользователя с помощью проверки свойства UserConnected объекта типа IApi.

    Вызов данной функции не обязателен перед отправкой какого-либо запроса на сервер, так как в случае отсутствия авторизации пользователю автоматически будет предложено авторизоваться путем ввода логина и пароля, однако при желании разработчик может включить авторизацию на сервере Delta Key после соединения пользователя с сервером ICQ.

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

    К содержанию

    Перевод средств другому пользователю системы


    Данная операция является одной из самых распространенных и позволяет пользователю передать средства со своего счета на счет другого пользователя. При этом если пользователь, которому передаются средства, не зарегистрирован в системе ICQMoney, то система все равно вернет положительный результат. В этом случае если в течении десяти дней получатель средств зарегистрируется в системе, на его счету окажутся переданные ему средства, в противном случае средства будут возвращены пользователю на счет через 10 дней.

  • function PayId(user: Widestring; sum: double; comment: PChar; protect: Boolean=False; protect_code: PChar=nil; protect_day: Byte=3): IResultCMD; stdcall;


  • Функция принимает следующие параметры:

  • user – UIN пользователя, которому переводятся средства;
  • sum – сумма перевода;
  • comment – комментарий получателю средств;
  • protect – необходимоть защитить сделку протекцией;
  • protect_code – код протекции (строка от 3 до 60 символов или пустая);
  • protect_day – количество дней действия протекции.

    Возвращает строку – результат выполнения запроса на сервер. В случае неуспешного запроса генерируется исключительная ситуация и пользователю выдается текст ошибки сервера.

    Пример вызова:

    procedure TfmSendMoney.btnSendClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      Res := MainForm.api.CMD.PayId(StrToInt(user), edtSum.Value, 
             PAnsiChar(mComment.Text), cbProtect.Checked, 
             PAnsiChar(edtProtectCode.Text), edtProtectCount.IntValue);
      ShowMessage(Res.result_text);
      Close;
    end;

    В случае, если перевод другому пользователю совершается с протекцией сделки, то при получении информации о приходе средств получатель должен ввести код протекции для завершении операции. Такое подтверждение операции выполняется с помощью функции EndPayProtect:

  • function EndPayProtect(transact_num: Int64; protect_code: WideString): IResultCMD; stdcall;


  • Данная функция проверяет, является ли введенный код протекции верным, и в случае успеха завершает операцию. Необходимо отметить, что в случае перевода без протекциями система производит одновременное списывание средств со счета плательщика и зачисление их получателю. В случае использования протекции средства с плательщика списываются в момент отправки средств, а получателю зачисляются в момент подтверждения сделки кодом протекции.


    Для получения параметров см. раздел «Финансовая статистика», а в приведенном ниже примере будет показано завершение операции с протекцией сделки:

    procedure TfmFinParams.btnOkClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      // Завершение операции с протекцией сделки
      Res := MainForm.api.CMD.EndPayProtect(transact, edtProtectCode.Text);
      ShowMessage(Res.result_text);
    end;

    К содержанию

    Моментальная оплата


    Моментальная оплата (платеж оператору) позволяет производить платежи в адрес операторов сотовой связи, оплачивать услуги доступа в Интернет, коммунальные платежи, совершать благотворительные взносы и др. Для реализации моментальной оплаты имеются несколько функций, позволяющих оперировать данным сервисом.

  • function GetTreeMomental(): IResultCMD; stdcall;


  • Функция GetTreeMomental позволяет загрузить дерево операторов. Список операторов называется деревом потому, что все они разбиты на группы, и одна группа теоретически может быть вложена в другую. Возвращаемая структура IResultCMD будет содержать таблицу следующего формата:

  • type (Тип) – определяет, является ли данная строка собственно оператором (1) или описанием группы (0);
  • code (Код) – код оператора (или группы). Синонимом кода оператора, зачастую встречаемым в данной документации, является термин «платежная форма».
  • root (Группа) – группа, к которой относится оператор (либо группа при наличии иерархии в группах); значение None указывает на то, что данная строка описывает корневую группу;
  • name (Название) – название оператора или группы.

    Например, возвращенная таблица может быть следующего вида:

    Тип Код Группа Название
    0100002NoneСотовая связь
    126100002Мегафон Москва
    1158100002Киевстар
    0100001NoneФинансовые сервисы
    133100001Yandex.Money
    159100001Star Trek ОСМП
    19100002Матрикс Мобайл
    156100001WebMoney

    Приведенную выше структуру можно расшифровать таким образом. Имеются две группы (Сотовая связь и Финансовые сервисы). В группе сотовая связь (код 100002) находятся платежные формы «Мегафон Москва», «Киевстар», «Матрикс Мобайл», а в группе финансовые сервисы – «Yandex.Money», «Star Trek ОСМП» и «WebMoney».


    Следует отметить, что порядок следования строк может быть любым, и его не следует учитывать при построении дерева.

    В приведенном ниже листинге будет расписан пример рекурсивного обхода данной структуры для построения дерева операторов.

    После построения дерева пользователь сможет легко перемещаться по списку операторов, однако при выборе какого-то отдельного оператора для совершения платежа необходимо запросить определенные параметры, которые зависят от платежной формы. Например, если выбран сотовый оператор «Билайн GSM», то необходимо запросить пополняемый номер телефона, если выбрана форма «WebMoney» - то номер кошелька, а для платежа в адрес оператора E-port запрашивается номер карты и контрольный код. На каждую платежную форму запрашиваются разные данные и в разном количестве. Например, для платежа в какую-либо благотворительную организацию параметры могут вообще не запрашиваться, а для оплаты коммунальных услуг оператор может запросить данные о номере квитанции, периоде оплаты (месяц, год), адресе оплачиваемой квартиры и даже фамилии квартиросъемщика. Таким образом, разработчику следует учесть, что ограничений на количество заполняемых полей – нет.

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

    Для получения списка полей для совершения платежа в адрес оператора используется функция GetFormFields:

  • function GetFormFields(Payform: Integer): IResultCMD; stdcall;


  • Функция в табличной части результирующей структуры возвращает все поля формы в следующем виде:

  • num_field (Номер поля) – номер поля; при передаче значений полей используется именно номер поля;
  • eng_name (Англ. название) – английское название поля (кодовое название, не имеет пробелов);
  • rus_name (Рус. название) – русскоязычный синоним;
  • re (Регулярное выражение) – регулярное выражение, проверка введенного значения не является обязательным и выполняется по желанию разработчика; в любом случае введенное значение будет проверено по регулярке на сервере;
  • mask (Маска ввода) – маска ввода, не обязательна к использованию;
  • obligatory (Обязательное) – определяет, является ли поле обязательным к заполнению (1) или нет (0);
  • sort (Порядок сортировки) – порядок, в котором должны выдаваться поля для заполнения пользователем;
  • type_field (Тип поля) – тип вводимого значения, 1 – однострочный элемент ввода, 2 – многострочный элемент ввода; не обязателен к использованию;
  • type_num_val (Список номеров значений списка выбора);
  • type_text_val (Список текстовых значений списка выбора).

    Для некоторых операторов значение одного из полей может быть выбрано из списка; в этом случае значения полей type_num_val и type_text_val будут содержать строки с кодами значений и текстовыми их представлениями соответственно. Например, при оплате на Дальсвязь пользователь по своему усмотрению может выбрать тип платежа – Дальсвязь, Аудиотеле, Ростелеком, Ростелеком (ИСС), МТТ (ИСС) или МТТ. В этом случае полученная структура будет такой:

    Номер поля 11 2500
    Англ. название phone type_pay
    Рус. название Номер телефона Тип платежа
    Регулярное выражение ^[8]{0,1}[0-9]{10}$
    Маска ввода 8-XXX-XXX-XX-XX
    Обязательное 1 0
    Порядок сортировки 0 1
    Тип поля 1 1
    Список номеров значений списка выбора 1|10|2|21|3|4
    Список текстовых значений списка выбора Дальсвязь|Аудиотеле|Ростелеком|Ростелеком (ИСС)|МТТ (ИСС)|МТТ

    Данную таблицу можно расшифровать следующим образом: первое поле – номер телефона, имеет код 11, является обязательным к заполнению, должно содержать в себе 10 цифр, при этом в начале может быть незначащая восьмерка. Второе поле – тип платежа, является списком выбора, и должно выбираться из списка.

    При этом при отправке данных на сервер при вводе в качестве номера телефона 9376665554 и выборе параметра «Ростелеком», вместо слова «Ростелеком» должен быть передан его код – 2.


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

  • function PayMomental(Form: TMultipartForm; payform: Integer; sum: double): IResultCMD; stdcall;


  • Так как количество передваемых параметров функции не известно, первым параметром передается объект типа TMultipartForm. Об этом объекте было подробно написано в параграфе Прямая отправка данных на сервер по протоколу DKCP. Этот объект должен содержать все поля, требуемые платежной формой. Также в функцию передается номер платежной формы (параметр payform) и сумма (параметр sum) в виде десятичной дроби с двумя знаками после запятой.

    В приведенном ниже листинге функция btnPayClick реализует вызов вышеописанной функции.

    Результатом выполнения PayMomental является структура ответа сервера. В случае, если сервер откажется принять данные, будет сгенерирована исключительная ситуация, а пользователю будет показан текст ошибки, возвращенный сервером.

    Следует отметить, что успешное принятие сервером платежа не всегда означает успех финансовой операции. Дело в том, что когда сервер дает успех при моментальной оплате, он ставит платеж в очередь на обработку. Сама очередь продвигается очень быстро, и максимальное время, которое сервер может затратить на обработку платежа составляет одну минуту. В штатном же режиме обработка платежа совершается в течение 3-5 секунд. Однако если у оператора технические проблемы либо профилактические работы, то обработка платежа может затянуться на более продолжительный период. Также возможны ситуации, когда оператор отказывается принять платеж, например, в случае, если плательщик ошибся номером телефона или счета (и т.п.). В этом случае в результате выполнения операции транзакция будет помечена как отказанная, а деньги за платеж – возвращены плательщику.

    Из вышесказанного следует сделать вывод, что плательщику крайне удобно не выходя из окна моментальной оплаты получить статус проведенного платежа. Операция получения статуса платежа выполняется с помощью функции GetResultFinoperation:

  • function GetResultFinoperation(transact: Int64): IResultCMD; stdcall;


  • В качестве единственного параметра функция принимает номер транзакции. Таким образом, разработчику достаточно запомнить номер последней транзакции (или нескольких транзакций), возвращенному в результате выполнения функции PayMomental, чтобы предоставить возможность пользователю получить результат обработки платежа.


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

    unit uMomental;
    ...
    
    type
      TfmMomental = class(TForm)
        ...
      private
        { Private declarations }
        ResTree: IResultCMD;
        last_transact: WideString;
        rate_create: Boolean;
        procedure ShowFields(Payform: Integer; PayCaption: WideString);
      public
        repay: Boolean; //если true, то это перепроводка
        transact_old: WideString; //номер перепроводимой транзакции
      end;
    
    ...
    
    implementation
    
    ...
    
    procedure TfmMomental.FormCreate(Sender: TObject);
    var
      Row, SubRow: Integer;
      Sibling: tTreeNode;
    begin
      // Дерево операторов
      sPageControl1.ActivePageIndex := 0;
      last_transact := '';
      repay := False;
    
      ResTree := MainForm.api.CMD.GetTreeMomental;
    
      // Заполняем левое дерево
      treeMomental.Items.Clear;
      for Row := 0 to ResTree.Fields.rowcount-1 do
      begin
        if ResTree.Fields.fields[Row, 0] = '0' then
        begin
          if (ResTree.Fields.fields[Row, 2] = '0') or 
                  (ResTree.Fields.fields[Row, 2] = 'None') then
          begin
            Sibling := treeMomental.Items.Add(nil, ResTree.Fields.fields[Row, 3]);
            Sibling.StateIndex := StrToInt(ResTree.Fields.fields[Row, 1]);
    
            //просматриваем на наличие подгрупп
            for SubRow := 0 to ResTree.Fields.rowcount-1 do
              if (ResTree.Fields.fields[SubRow, 0] = '0') and 
                    (ResTree.Fields.fields[SubRow, 2] = ResTree.Fields.fields[Row, 1]) 
              then
                treeMomental.Items.AddChild(Sibling, ResTree.Fields.fields[SubRow,3]);
          end;
        end;
      end;
      sgListMomental.Cells[0, 0] := '№ п/п';
      sgListMomental.Cells[1, 0] := '№ формы';
      sgListMomental.Cells[2, 0] := 'Название';
    
      sgCheck.Cells[0,0] := '№';
      sgCheck.Cells[1,0] := 'Транз.';
      sgCheck.Cells[2,0] := 'Дата';
      sgCheck.Cells[3,0] := 'Название';
      sgCheck.Cells[4,0] := 'Сумма';
      sgCheck.Cells[5,0] := 'Статус';
      sgCheck.Cells[6,0] := 'Результат';
    end;
    
    procedure TfmMomental.treeMomentalClick(Sender: TObject);
    var
      Row: Smallint;
    begin
      // отображение списка форм выбранной группы
      sgListMomental.RowCount := 1;
      cbList.Items.Clear;
      cbList.Values.Clear;
      for Row := 0 to ResTree.Fields.rowcount - 1 do
      begin
        if (ResTree.Fields.fields[Row, 2] = IntToStr(treeMomental.Selected.StateIndex)) and
          (ResTree.Fields.fields[Row, 0] = '1') then
        begin
          sgListMomental.RowCount := sgListMomental.RowCount + 1;
          sgListMomental.Cells[0, sgListMomental.RowCount-1] := 
                  IntToStr(sgListMomental.RowCount-1); //номер п/п
          sgListMomental.Cells[1, sgListMomental.RowCount-1] := 
                  ResTree.Fields.fields[Row, 1]; //номер формы
          sgListMomental.Cells[2, sgListMomental.RowCount-1] := 
                  ResTree.Fields.fields[Row, 3]; //название формы
          cbList.AddItemValue(ResTree.Fields.fields[Row, 3], 
                   ResTree.Fields.fields[Row, 1]);
        end;
      end;
      if sgListMomental.RowCount > 1 then
        sgListMomental.FixedRows := 1;
      pPayMomental.Visible   := False;
      sgListMomental.Visible := True;
      pCombo.Visible := False;
    end;
    
    procedure TfmMomental.btnStatusClick(Sender: TObject);
    var
      Res: IResultCMD;
      st: WideString;
    begin
      // Статус последнего платежа
      st := TimeToStr(time) + ' - ';
      if last_transact='' then
      begin
        mStatus.Lines.Insert(0, st+'Вы еще не производили оплату');
        Exit;
      end;
      // Запрос статуса последнего платежа
    
      Res := MainForm.api.CMD.GetResultFinoperation(StrToInt(last_transact));
    
      mStatus.Lines.Insert(0, st + 'Транзакция: ' + last_transact + 
              ' Статус обработки: ' + Res.GetFieldValue('status_text') +' / '+ 
              Res.GetFieldValue('result_text') );
    end;
    
    procedure TfmMomental.sgListMomentalDblClick(Sender: TObject);
    
    begin
      // Отображение полей на форму
      if sgListMomental.Cells[1, sgListMomental.Row] = '' then
        Abort;
    
      ShowFields(StrToInt(sgListMomental.Cells[1, sgListMomental.Row]), 
           sgListMomental.Cells[2, sgListMomental.Row]);
    
    end;
    
    procedure TfmMomental.btnPayClick(Sender: TObject);
    var
      Form: TMultiPartForm;
      Res: IResultCMD;
      Col, Row: Integer;
      i: Integer;
      oper_text: WideString;
    begin
      //оплата
      if edtSum.Text = '' then
      begin
        ShowMessage('Необходимо указать сумму платежа');
        if edtSum.CanFocus then edtSum.SetFocus;
        Abort;
      end;
    
      //отправка
      Form:=TMultiPartForm.Create;
    
      //если это перепроводка, то добавим номер перепроводимой транзакции
      if repay then
      begin
        Form.AddFormField('transact_old', transact_old);
        Form.AddFormField('2410', transact_old);//для транзакт-даты
      end;
    
      //дополнительные параметры
      for i:=0 to pFields.ControlCount-1 do
      begin
        if pFields.Controls[i] is TEdit then
          Form.AddFormField(IntToStr(pFields.Controls[i].Tag), 
                   TEdit(pFields.Controls[i]).Text);
        if pFields.Controls[i] is TRzComboBox then
          Form.AddFormField(IntToStr(pFields.Controls[i].Tag),       
                   TRzComboBox(pFields.Controls[i]).Value);
      end;
    
      Res := MainForm.api.CMD.PayMomental(Form, lblPayFormName.Tag, edtSum.Value);
      ShowMessage(Res.result_text);
    
      last_transact := IntToStr(Res.transact);
    
      // Заполняем список операций
      oper_text := lblPayFormName.Caption +' - '+ edtSum.Text + 'юн.';
    
      if repay then
        Close
      else
      begin
        for Row:=0 to pFields.ControlCount-1 do
        begin
          if pFields.Controls[Row] is TEdit then
          begin
            oper_text := oper_text + ' - ' + TEdit(pFields.Controls[Row]).Text;
            TEdit(pFields.Controls[Row]).Text := '';
            if TEdit(pFields.Controls[Row]).CanFocus then 
              TEdit(pFields.Controls[Row]).SetFocus;
          end;
        end;
      end;
    
      cbOperation.AddItemValue(oper_text, last_transact);
    end;
    
    procedure TfmMomental.btnRateClick(Sender: TObject);
    var
      p: TPoint;
    begin
      // Запрос курса конвертации и комиссий
      if not rate_create then
      begin
        fmRate := TfmRate.Create(Self);
        fmRate.cmdName := 'pay_momental';
        fmRate.subj_param := IntToStr(lblPayFormName.Tag);
        rate_create := True;
      end;
    
      fmRate.count := edtSum.Value;
    
      p.X := btnRate.Left + btnRate.Width + sPageControl1.Left;
      p.Y := btnRate.Top + pPayOptions.Top + pPayMomental.Top + 
                tsPay.Top + sPageControl1.Top;
      p := ClientToScreen(p);
    
      fmRate.Left := p.X;
      fmRate.Top := p.Y;
      fmRate.Show;
    end;
    
    procedure TfmMomental.ShowFields(Payform: Integer; PayCaption: WideString);
    var
      Form :TIdMultiPartFormDataStream;
      Res: IResultCMD;
      Col, Row, SubRow, i: Integer;
      lbl: TLabel;
      edt: TWinControl;
      strArrNum, strArrText: TStringArr;
    begin
      // поля
      pPayMomental.Visible := False;
      lblPayFormName.Caption := PayCaption;
    
      cbList.Value := IntToStr(Payform);
      lblPayFormName.Tag := PayForm; //номер формы
    
      Res := MainForm.api.CMD.GetFormFields(PayForm);
    
      if not ((Res.status=2) and (Res.result=0)) then
      begin
        MessageBox(Handle, PChar(Res.result_text), 'Ошибка', MB_OK+MB_ICONEXCLAMATION);
        Abort;
      end;
    
      // удаляем все объекты на форме
      for Row:=pFields.ControlCount-1 downto 0 do
      begin
        if (pFields.Controls[Row] is TEdit) or 
                (pFields.Controls[Row] is TRzComboBox) or
                ( (pFields.Controls[Row] is TLabel) and 
                (pFields.Controls[Row].Name <> 'lblPayFormName') ) then
          pFields.Controls[Row].Free;
      end;
    
      for Row := 0 to Res.Fields.rowcount-1 do
      begin
        lbl := TLabel.Create(Self);
        lbl.AutoSize := False;
        lbl.Width := 300;
        lbl.Font.Color := clNavy;
        lbl.Font.Size := 8;
        lbl.Left := 10;
        lbl.Top := Row * 40 + 25;
        lbl.Caption := Res.Fields.fields[Row, 2]; //русское название
        if Res.Fields.fields[Row, 5] = '1' then
          lbl.Caption := lbl.Caption + ' *';
        lbl.Parent := pFields;
    
        if Res.GetFieldValue('type_num_val', Row) = '' then
          edt := TEdit.Create(Self)
        else
        begin
          edt := TRzComboBox.Create(Self);
          edt.Parent := pFields;
          TRzComboBox(edt).Style := csDropDownList;
    
    
          strArrNum := MainForm.api.SplitString(Res.GetFieldValue(
                  'type_num_val', Row), '|');
          strArrText := MainForm.api.SplitString(Res.GetFieldValue(
                  'type_text_val', Row), '|');
          for i:=0 to Length(strArrNum)-1 do
            TRzComboBox(edt).AddItemValue(strArrText[i], strArrNum[i]);
    
          TRzComboBox(edt).Value := strArrNum[0];
        end;
    
        edt.Left := 10;
        edt.Tag := StrToInt(Res.Fields.fields[Row, 0]); //номер поля
        edt.HelpKeyword := Res.Fields.fields[Row, 2];
        edt.Top := lbl.Top + 13;
        edt.Width := 249;
        edt.Parent := pFields;
    
        if Row=0 then
          if edt.CanFocus then edt.SetFocus;
    
      end;
      if edt <> nil then
        pFields.Height := edt.Top + 30;
      pPayMomental.Visible := True;
      pFields.Repaint;
      pFields.Refresh;
      pPayMomental.VertScrollBar.Range := pFields.Height + 
                 pPayOptions.Constraints.MinHeight;
    
      sgListMomental.Visible := False;
      pCombo.Visible := True;
    
      if pFields.CanFocus then
        pFields.SetFocus;
    
      for Row:=0 to pFields.ControlCount-1 do
        if (pFields.Controls[Row] is TEdit) or (pFields.Controls[Row] is TRzComboBox) then
        begin
          if TEdit(pFields.Controls[Row]).CanFocus then 
             TEdit(pFields.Controls[Row]).SetFocus;
          break;
        end;
      rate_create := False;
    end;
    
    ...
    
    end.

    К содержанию

    Финансовая статистика


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


    Для выборки списка финансовых операций реализовано две функции: GetlistFinoperation и GetlistFinoperationLight. Пример использования первой функции был представлен в параграфе Моментальная оплата. Однако рекомендованной является функция GetlistFinoperationLight, так как загружает меньше данных и более оптимизирована на пользователей, не являющимися дилерами или другими привилегированными группами пользователей системы.

  • function GetlistFinoperationLight(date_start: TDate=-1; date_end: TDate=-1; typelocal: Shortint=-1; code: WideString='-1'; transact: WideString='-1'; sum: WideString='-1'; done: WideString='None'; param: WideString='-1'): IResultCMD; stdcall;


  • Данная функция принимает несколько параметров:

  • date_start – стартовая дата, с которой вернуть статистику; передаваемое по умолчанию значение (-1) предписывает выбирать все данные по движению средств с начала пользования системой; так как количество возвращаемых операций может быть очень большим, рекомендуется ограничивать стартовую дату;
  • date_end – конечная дата, по которую вернуть статистику; передаваемое по умолчанию значение (-1) предписывает выбирать все данные по движению средств включая последнюю операцию;
  • typelocal – тип операции – приход (1) или расход (2); передаваемое по умолчанию значение (-1) предписывает выбирать и операции пополнения счета (приход средств), и вывод средств (расход);
  • code – код операции; каждая операция имеет код, определенный в справочнике команд DKCP; если необходимо отобрать по нескольким операциям, то данные передаются через запятую (например, «52013,52023,52063» предписывает отобрать операции, связанные с переводом другому пользователю – прямой перевод, платеж на сайте и др.); передаваемое по умолчанию значение (-1) запрещает отбор по коду операции;
  • transact – номер транзакции или несколько через запятую (для поиска по номеру транзакции); передаваемое по умолчанию значение (-1) запрещает отбор по номеру транзакции;
  • sum – поиск по сумме платежа, при этом сама сумма представлена числом с плавающей точкой (разделитель дробной части - точка), а перед ней устанавливается знак сравнения (= или >= или <= или > или < или != или <>); передаваемое по умолчанию значение (-1) запрещает отбор по сумме;
  • done – отбор операций по признаку завершенности; возможные значения: None - любые, 0 - в обработке, 1 - успешные, -1 – отказанные, -2 – аннулированные; передаваемое по умолчанию значение (None) запрещает отбор по статусу завершения операции;
  • param – поиск операции по параметру платежа; например, если пользователю необходимо выбрать все платежи на номер телефона «9167774666», то в параметре передается именно это строка; для поиска по части строки можно использовать знак процента, например, «916777%».

    Кроме получения списка транзакций пользователь может запросить подробности отдельной операции, где данные по ней будут предоставлены более подробно. Для этого используется функции GetparamsFinoperation или GetparamsFinoperationLight. Как и для функций получения списка операций, рекомендованной является использование облегченной функции GetparamsFinoperationLight:

  • function GetparamsFinoperationLight(transact: Int64): IResultCMD; stdcall;


  • В качестве единственного параметра эта функция принимает номер транзакции, по которой необходимо вернуть подробности операции.


    В приведенном ниже листинге представлена реализация модулей финансовой статистики и параметров операции.

    unit uFinStat;
    
    interfacetype
      TfmFinStat = class(TForm)
      ...
      private
        { Private declarations }
        ResShow: IResultCMD;
        WasShow: Boolean;
        need_update: Boolean;
        
        procedure ShowStat;
        procedure ShowStatFind(useMain: Boolean; transact: WideString='-1'; 
          sum: WideString='-1'; done: WideString='None'; param: WideString='-1');
      public
        { Public declarations }
      end;
    
    ...
    
    implementation
    
    ...
    
    procedure TfmFinStat.FormCreate(Sender: TObject);
    begin
      dtpDateStart.Value := date;
      dtpDateEnd.Value := date;
      WasShow := False;
      ShowStat;
    end;
    
    procedure TfmFinStat.ShowStat;
    begin
      // просто статистика
    
      ResShow := MainForm.api.CMD.GetlistFinoperationLight(dtpDateStart.Value, 
           dtpDateEnd.Value, StrToInt(cbTypeLocal.value), cbCode.value);
      MainForm.api.Face.ShowResInGrid(aGrid, ResShow, 'finoperation_light');
    
      aGrid.ColCount := 8;
      ResizeGrid;
      WasShow := True;
      need_update := False;
    end;
    
    procedure TfmFinStat.ShowStatFind(useMain: Boolean; transact: WideString='-1'; 
      sum: WideString='-1'; done: WideString='None'; param: WideString='-1');
    var
      date_start, date_end: TDate;
      typelocal: Shortint;
      code: WideString;
    begin
      // статистика с поиском
      
      if useMain then
      begin
        date_start := dtpDateStart.Value;
        date_end := dtpDateEnd.Value;
        typelocal := StrToInt(cbTypeLocal.value);
        code := cbCode.value;
      end
      else
      begin
        date_start := -1;
        date_end := -1;
        typelocal := -1;
        code := '-1';
      end;
    
      ResShow := MainForm.api.CMD.GetlistFinoperationLight(date_start, date_end, 
              typelocal, code, transact, sum, done, param);
      MainForm.api.Face.ShowResInGrid(aGrid, ResShow, 'finoperation_light');
    
      WasShow := True;
      need_update := False;
    end;
    
    
    procedure TfmFinStat.aGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
      State: TGridDrawState);
    Const
      clrGreen1 = $00d3ffd8;
      clrRed1 = $00a9a9ff;
      clrYellow1 = $00cbfff7;
      clrGray1 = $00d4d4d4;
    begin
      // Прорисовка сетки в зависимости от статуса транзакции
      if (not WasShow) or (ARow = 0) or (ACol = 0) then Exit;
    
      (Sender as TStringGrid).Canvas.Brush.Color := clrGray1;
    
      if (ARow>=1) and (ResShow.Fields.RowCount>0) then
      begin
        if ResShow.GetFieldValue('done', ARow-1)='1' then
          (Sender as TStringGrid).Canvas.Brush.Color := clrGreen1;
        if ResShow.GetFieldValue('done', ARow-1)='-1' then
          (Sender as TStringGrid).Canvas.Brush.Color := clrRed1;
        if ResShow.GetFieldValue('done', ARow-1)='0' then
          (Sender as TStringGrid).Canvas.Brush.Color := clrYellow1;
      end;
    
      (Sender as TStringGrid).Canvas.Font.Color := clBlack;
    
      //активная строка
      if ARow = (Sender as TStringGrid).Row then
        (Sender as TStringGrid).Canvas.Brush.Color :=
          (Sender as TStringGrid).Canvas.Brush.Color - $00111111;
    
      (Sender as TStringGrid).Canvas.FillRect(Rect);
      (Sender as TStringGrid).Canvas.TextOut(Rect.Left+2, Rect.Top+2,
        (Sender as TStringGrid).Cells[ACol,ARow]);
       
    end;
    
    ...
    
    end.

    Параметры операции:

    unit uFinParams;
    
    ...
    
    type
      TfmFinParams = class(TForm)
        ...
      public
        { Public declarations }
        Transact: Int64;
      end;
    
    ...
    
    implementation
    
    ...
    
    procedure TfmFinParams.FormShow(Sender: TObject);
    var
      Res: IResultCMD;
      lbl: TLabel;
      i, tt, mw: Integer;
      tl: WideString;
    begin
      // Получение параметров транзакции
      Res := MainForm.api.CMD.GetparamsFinoperationLight(Transact);
    
      tl := Res.GetAnswerValue('typelocal');
      if tl = '-' then
        tl := 'Расход и приход';
      lblCaption.Caption := tl + ', транзакция ' + Res.GetAnswerValue('transact');
    
      lblCodeText.Caption := Res.GetAnswerValue('code_text');
      lblCorrC.Top := lblCodeText.Top + lblCodeText.Height + 7;
      lblCorr.Top := lblCorrC.Top;
    
      lblCorr.Caption := Res.GetAnswerValue('uin_corr') + ' (' + 
                Res.GetAnswerValue('name_corr') + ')';
      lblDateC.Top := lblCorr.Top + lblCorr.Height + 7;
      lblDate.Top := lblDateC.Top;
    
      lblDate.Caption := Res.GetAnswerValue('date');
      lblSumC.Top := lblDate.Top + lblDate.Height + 7;
      lblSum.Top := lblSumC.Top;
      lblComissC.Top := lblSumC.Top;
      lblComiss.Top := lblSumC.Top;
    
      lblSum.Caption := Res.GetAnswerValue('itogo') + ' ' + 
                Res.GetAnswerValue('curr');
      lblComiss.Caption := Res.GetAnswerValue('comiss') + ' ' + 
                Res.GetAnswerValue('curr');
      lblResultC.Top := lblSum.Top + lblSum.Height + 7;
      lblResult.Top := lblResultC.Top;
    
      lblResult.Caption := Res.GetAnswerValue('status_text') + ' / ' + 
                Res.GetAnswerValue('result_text');
      grpBalance.Top := lblResult.Top + lblResult.Height + 7;
    
      lblBalanceStart.Caption := Res.GetAnswerValue('balance_start') + ' ' + 
                Res.GetAnswerValue('curr');
      lblBalanceEnd.Caption := Res.GetAnswerValue('balance_end') + ' ' + 
                Res.GetAnswerValue('curr');
    
      lblCommentC.Top := grpBalance.Top + grpBalance.Height + 7;
      mComment.Top := lblCommentC.Top + lblCommentC.Height + 7;
      mComment.Text := Res.GetAnswerValue('comment');
    
      tt := mComment.Top + mComment.Height + 7;
      if (Res.GetAnswerValue('temp_block')='1') and 
                  (Res.GetAnswerValue('status') <> '2') then
      begin
        // с протекцией
        pProtect.Visible := True;
        pProtect.Top := tt;
        tt := pProtect.Top + pProtect.Height + 7;
      end;
    
      if Res.Fields.RowCount > 0 then
      begin
        lblParamsC.Visible := True;
        lblParamsC.Top := tt;
        tt := lblParamsC.Top + lblParamsC.Height + 7;
    
        // Параметры
        for i:=0 to Res.Fields.RowCount-1 do
        begin
          lbl := TLabel.Create(Self);
          lbl.Top := tt;
          lbl.Caption := Res.GetFieldValue('param', i);
          lbl.Font.Name := 'Times New Roman';
          lbl.Font.Style := [fsItalic];
          lbl.Font.Size := 10;
          lbl.Constraints.MinWidth := 118;
          lbl.Constraints.MaxWidth := 118;
          lbl.WordWrap := True;
          mw := lbl.Height;
          lbl.Left := 20;
          lbl.Parent := fmFinParams;
    
          lbl := TLabel.Create(Self);
          lbl.Top := tt;
          lbl.Caption := Res.GetFieldValue('value', i);
          lbl.Constraints.MinWidth := 196;
          lbl.Constraints.MaxWidth := 196;
          lbl.WordWrap := True;
          if lbl.Height>mw then
            mw := lbl.Height;
          lbl.Left := 144;
          lbl.Parent := fmFinParams;
    
          tt := tt + mw + 7;
        end;
    
      end;
    
      btnClose.Top := tt;
      fmFinParams.ClientHeight := btnClose.Top + btnClose.Height + 7;
    end;
    
    procedure TfmFinParams.btnOkClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      // Завершение операции с протекцией сделки
      Res := MainForm.api.CMD.EndPayProtect(transact, edtProtectCode.Text);
      ShowMessage(Res.result_text);
    end;
    
    ...
    
    end.

    К содержанию

    Получение сведений о балансе счета


    Баланс счета пользователя сервиса ICQMoney может быть получен отдельной командой, а также возвращается при проверке новых сообщений в Информаторе-OnLine.

    Информатор-OnLine рассмотрен в отдельной главе, а получение баланса с помощью отдельной функции выглядит следующим образом:

  • function GetBalance(): double; stdcall;


  • procedure TRoasterForm.mBalanceClick(Sender: TObject);
    begin
      ShowMessage('Баланс Вашего счета составляет '+ 
              FloatToStr(MainForm.api.CMD.GetBalance()) + ' юнитов');
    end;

    Также все операции возвращают в параметре Answer в строке syskeyt_balance баланс счета пользователя.

    К содержанию

    Ввод средств (пополнение счета)


    Сервис ICQMoney и платежная система Delta Key поддерживают следующие возможности ввода средств:

  • Пополнение счета через банк;
  • Пополнение счета электронныч чеков;
  • Пополнение счета картой оплаты;
  • Пополнение счета через терминал оплаты;
  • Пополнение счета переводом из других платежных систем.

    В этой главе будут рассмотрены все это возможности кроме электронных чеков, так как они полностью рассматриваются в главе Электронные чеки.

    Пополнение счета через банк

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

    1. Выбор страны и банка, на которые удобнее всего совершить платеж;
    2. Выставление счета на оплату; распечатав этот счет, пользователь может оплатить его в любом банке;
    3. Собственно перевод средств по банку; как только средства поступят на банковский счет системы, они автоматически будут зачислены пользователю.

    Программное обеспечение должно позволять пользователю автоматизировать первые два шага. В демо-реализации сервиса пополнение счета выглядит следующим образом:


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


    Реализация получения банковских реквизитов выполняется с использованием функции GetListBank, которая возвращает список всех банков, через которые возможно пополнение счета:

  • function GetListBank(country: Smallint=-1): IResultCMD; stdcall;


  • В качестве единственного необязательного параметра функции может быть передан трехзначный международный код страны для автоматического немедленного отбора только тех банков, которые в данной стране находятся. Это может быть полезно в том случае, если у пользователя каким-либо образом указана в настройках страна его местонахождения. При передаче значения по умолчанию (-1) сервер вернет полный список всех банков в различных странах мира, через которые можно пополнить счет.

    Список банков возвращается в табличной части результирующей структуры, и состоит из пяти полей и неограниченного количества строк (каждая строка – отдельный банк):

  • code – код банка; этот код в последствии может быть передан в функцию GetBankRequisite для получения реквизитов выбранного банка;
  • name – название банка;
  • country – код страны;
  • country_name – название страны;
  • city – город банка;

    Пример вызова:

    var
      ResBank: IResultCMD;
    ...
    
    // на создание формы заполняем список стран
    procedure TfmInBank.FormCreate(Sender: TObject);
    var
      Row, c: Integer;
      add: Boolean;
    begin
      ResBank := MainForm.api.CMD.GetListBank;
    
      for Row:=0 to ResBank.fields.RowCount-1 do
      begin
        add := True;
        for c:=0 to cbCountry.Values.Count-1 do
          if cbCountry.Values[c]=ResBank.Fields.Fields[Row,2] then
          begin
            add := False;
            break;
          end;
        if add then
          cbCountry.AddItemValue(ResBank.Fields.Fields[Row,3], 
                 ResBank.Fields.Fields[Row,2]);
      end;
    
      rate_create := False;
    end;
    
    // После выбора страны заполним комбобокс банков этой страны
    procedure TfmInBank.cbCountryChange(Sender: TObject);
    var
      Row: Integer;
    begin
      cbBank.Items.Clear;
      cbBank.Values.Clear;
      for Row:=0 to ResBank.fields.RowCount-1 do
      begin
        if ResBank.Fields.Fields[Row,2] = cbCountry.Value then
          cbBank.AddItemValue(ResBank.Fields.Fields[Row,1] + ' ' +
                  ResBank.Fields.Fields[Row,4], ResBank.Fields.Fields[Row,0]);
      end;
    end;

    После того, как пользователь выбрал банк пополнения, ему необходимо предоставить его банковские реквизиты. Данная операция выполняется путем вызова функции GetBankRequisite, в которую необходимо передать код банка.

  • function GetBankRequisite(bank_code: Integer): IResultCMD; stdcall;


  • Эта функция также возвращает табличную часть в результирующей структуре, содержащую следующие поля:

  • param – символьное представление параметра;
  • r_param – национальный синоним (название параметра на русском языке);
  • value – значение;
  • sort – порядок сортировки; в указанном порядке реквизиты должны быть выданы пользователю.

    Пример вызова:

    procedure TfmInBank.cbBankChange(Sender: TObject);
    var
      edt: TEdit;
      lbl: TLabel;
      Row, rc: Integer;
      Res: IResultCMD;
    begin
      // Получение реквизитов выбранного банка
      Res := MainForm.api.CMD.GetBankRequisite(StrToInt(cbBank.Value));
    
      for row:=pRequisite.ControlCount-1 downto 0 do
        pRequisite.Controls[Row].Free;
    
      rc := 0;
      for Row:=0 to Res.Fields.RowCount-1 do
      begin
        if Res.GetFieldValue('sort', Row)='-1' then
          Continue;
    
        lbl := TLabel.Create(Self);
        lbl.Left := 8;
        lbl.Top := rc*24+10;
        lbl.Caption := Res.GetFieldValue('r_param', Row);
        lbl.Parent := pRequisite;
    
        edt := TEdit.Create(Self);
        edt.Left := 150;
        edt.Width := pRequisite.ClientWidth - edt.Left - 8;
        edt.Top := lbl.Top-2;
        edt.Text := Res.GetFieldValue('value', Row);
        edt.Parent := pRequisite;
        inc(rc);
      end;
    
      if edt <> nil then
      begin
        pRequisite.Height := edt.Top + 28;
        fmInBank.ClientHeight := pRequisite.Top + pRequisite.Height + 80;
      end;
    end;

    После того, как пользователь ознакомится с банковскими реквизитами, он может сформировать квитанцию для пополнения ICQ-кошелька через банк. Оформление квитанции выполняется с помощью функции CreateBankOrder:

  • function CreateBankOrder(bank_code: Integer; sum: Real): IResultCMD; stdcall;


  • Данная функция принимает два обязательных параметра:

  • bank_code – код банка;
  • sum – сумма пополнения счета; валюта суммы пополнения – юниты, в параметрах результирующей структуры будет возвращена сумма в национальной валюте страны выбранного банка; например, если выбран российский банк, то будет возвращена сумма к оплате в валюте «Российский рубль».

    Приведенный ниже листинг демонстрирует пример оформления квитанции:

    procedure TfmInBank. btnCreateBankOrderClick(Sender: TObject);
    begin
      // Выпишем счет на оплату
      ResOrder := MainForm.api.CMD.CreateBankOrder(StrToInt(cbBank.Value),
             edtSum.Value);
      ShowMessage(ResOrder.result_text);
      btnPrint.Enabled := True;
      edtSum.Value := 0;
    end;

    Операция печати квитанции не требует обращения к серверу, так как функция CreateBankOrder возвращает все необходимые для печати параметры:

    // Здесь rep643 – компонент типа TfrxReport, и он связан с двумя наборами данных:
    // dsRequisite643, dsOrder: TfrxUserDataSet 
    procedure TfmInBank.btnPrintClick(Sender: TObject);
    begin
      if cbCountry.Value='643' then
        rep643.ShowReport();
    end;
    
    procedure TfmInBank.dsRequisite643GetValue(Const VarName: WideString; 
           var Value: Variant);
    begin
      Value := ResOrder.FindFieldValue('param', 'value', VarName);
    end;
    
    procedure TfmInBank.dsOrderGetValue(Const VarName: WideString; var Value: Variant);
    begin
      // Параметры счета
      Value := ResOrder.GetAnswerValue(VarName);
    end;

    Полный текст примера см. в модуле uInBank.

    Пополнение счета картой оплаты

    Платежная система Delta Key, так же как и ее сервис ICQMoney позволяет пополнить счет пользователя картой пополнения, распространяемой как на материальном носителе (пластик), так и в электронном виде. Каждая карта пополнения содержит серию, номер и ключ пополнения. Введя все эти данные, пользователь пополняет свой счет.

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

  • function ActiveCard(serial, number: Integer; code: Int64): IResultCMD; stdcall;


  • В эту функцию соответственно передаются параметры serial (серия карты), number (номер карты) и code (ключ пополнения). Функция возвращает строку – результат выполнения операции, в случае ошибки генерируется исключительная ситуация, а пользователю выдается текст ошибки. Все передаваемые в функцию параметры – целые числа, поэтому рекомендуется ограничить возможность ввода серии, номера и ключа активации только целыми числами. Максимальное количество цифр серии – 4, номера – 7, ключа активации – 12.

    Необходимо также отметить, что количество попыток активации карты ограничено, и платежная система предусматривает набор блокировок, вызываемых при слишком большом количестве неуспешных попыток активации, о чем пользователь извещается при получении ошибки сервера.

    Рассмотрим пример реализации операции:


    procedure TfmFillCard.sBitBtn1Click(Sender: TObject);
    var
      Res: IResultCMD;
      serial, number: Integer;
      code: Int64;
    begin
      try
        serial := StrToInt(edtSerial.Text);
        number := StrToInt(edtNumber.Text);
        code := StrToInt64(edtCode.Text);
      except
        ShowMessage('Все параметры должны быть числовыми');
        abort;
      end;
      Res := MainForm.api.CMD.ActiveCard(serial, number, code);
      ShowMessage(Res.result_text);
      if not cbNoClose.Checked then Close;
    end;

    Полный текст примера см. в модуле uFillCard.

    Пополнение счета через терминал оплаты

    Счет пользователя также может быть пополнен через терминал моментальной оплаты. Так как пополнение счета через терминал не требует выставления счета или ввода пользователем каких-либо данных, то вся операция сводится к показу инструкции, и может быть реализована следующим образом:

    procedure TRoasterForm.mInTerminalClick(Sender: TObject);
    var
      s: WideString;
    begin
      s := 'Для оплаты через терминал, имеющий кнопку "Delta Key", ' +#13+
              'необходимо найти в меню терминала эту кнопку и заплатить ' + 
              'на номер 16' + UIN + #13 +
              'Желаете ли Вы просмотреть геолокатор (адреса терминалов оплаты)?';
      if MessageDlg(s, mtInformation, mbYesNo, 0) = mrYes then
      begin
        MainForm.api.CMD.GoToGeoURL;
        ShellExecute(0,'open', PAnsiChar(s), nil, nil, SW_SHOWNORMAL);
      end;
    end;

    В приведенном примере пользователю будет выдано следующее сообщение:


    Здесь для удобства пользователь может сразу перейти на страницу сайта со списком точек пополнения – геолокатор, ссылка же на эту страницу получается с помощью функции GetGeoURL интерфейса IApi.

    Пополнение счета переводом из других платежных систем

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

    Список систем, из которых можно пополнить счет в системе ICQMoney:

  • Delta Key
  • Moneta
  • Единый кошелек
  • Мобильный кошелек QIWI

    Операция пополнения осуществляется путем отправки пользователя на Web-страницу, через которую он может попасть в мерчант выбранной платежной системы для проведения платежа, поэтому для всех пунктов вызывается по одной функции, которая открывает окно браузера с нужным адресом, например:

    procedure TRoasterForm.mInWebmoneyClick(Sender: TObject);
    begin
      // Перевод из WebMoney
      MainForm.api.CMD.FromWebMoney;
    end;

    Для других платежных систем функции вызова следующие:

  • Delta Key – FromDeltaKey;
  • WebMoney – FromWebMoney;
  • MoneyMail – FromMoneymail;
  • Малышка – FromMalyshka;
  • Star Trek – FromStarTrek;
  • Мобильный кошелек – FromMobileWallet;
  • Ru Transfer – FromRuTransfer;
  • E-bablo - FromEbablo;
  • Elios Gold - FromEliosGold;
  • Tajpay - FromTajpay;
  • WebKassa - FromWebKassa;

    Пополнение счета из обменного пункта валют происходит аналогичным образом:

    procedure TRoasterForm.mInExchangeClick(Sender: TObject);
    begin
      MainForm.api.CMD.FromExchanger;
    end;

    К содержанию

    Вывод средств


    В системе реализованы следующие возможности вывода средств со своего счета:

  • Банк;
  • Кредитная карта;
  • Различные платежные системы.

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

    Вывод средств на банковский счет

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


    Для просмотра списка стран, в которые возможен вывод средств через банк, используется функция GetlistOutbankCountries:

  • function GetlistOutbankCountries: IResultCMD; stdcall;


  • Эта функция в своей табличной части возвращает список стран, и может быть использована следующим образом:

    procedure TfmOutBank.FormCreate(Sender: TObject);
    var
      Row: Integer;
    begin
      // Получаем список стран
      ResCnt := MainForm.api.CMD.GetlistOutbankCountries;
      for Row:=0 to ResCnt.Fields.RowCount-1 do
        cbCountry.AddItemValue(ResCnt.GetFieldValue('country_text', Row) + ' - ' +
               ResCnt.GetFieldValue('curr_name', Row),
        ResCnt.GetFieldValue('inc', Row));
    end;

    После выбора страны пользователь должен заполнить свои банковские реквизиты, причем список запрашиваемых реквизитов зависит от выбранной страны. Таким образом разработчику необходимо после выборы страны послать на сервер запрос для выборки реквизитов для заполнения. Сделать это можно с помощью функции GetlistOutbankRequisite, в которую в качестве параметра передается код выбранной пользователем страны:

  • function GetlistOutbankRequisite(country: Smallint): IResultCMD; stdcall;


  • Пример использования:

    procedure TfmOutBank.cbCountryChange(Sender: TObject);
    var
      Res: IResultCMD;
      Row, rc: Smallint;
      lbl: TLabel;
      edt: TEdit;
    begin
      lblCountryCurr.Caption := ResCnt.FindFieldValue('inc', 'curr_text',
             cbCountry.Value);
      lblCountryCurr.Tag := StrToInt(ResCnt.FindFieldValue('inc', 'curr_code', 
             cbCountry.Value));
    
      // Выводим список реквизитов к заполнению
      Res := MainForm.api.CMD.GetlistOutbankRequisite(StrToInt(              
             ResCnt.FindFieldValue('inc', 'country_code', cbCountry.Value)));
      rc := 0;
    
      for row:=pRequisite.ControlCount-1 downto 0 do
        pRequisite.Controls[Row].Free;
    
      for Row:=0 to Res.Fields.RowCount-1 do
      begin
        lbl := TLabel.Create(Self);
        lbl.Left := 8;
        lbl.Top := rc*24+10;
        lbl.Caption := Res.GetFieldValue('r_param', Row);
        lbl.Parent := pRequisite;
    
        edt := TEdit.Create(Self);
        edt.Left := 150;
        edt.Width := pRequisite.ClientWidth - edt.Left - 8;
        edt.Top := lbl.Top-2;
        edt.Parent := pRequisite;
        edt.HelpKeyword := Res.GetFieldValue('param', Row);
        inc(rc);
      end;
    
      if rc<>0 then
        pRequisite.Height := edt.Top + 28
      else
        pRequisite.Height := 132;
    
      fmOutBank.ClientHeight := pRequisite.Top + pRequisite.Height + 170;
      rate_create := False;  
    end;

    Для создания заказа выплаты используется функция OrderBankout, описанная следующим образом:

  • function OrderBankout(Form: TMultipartForm; country, curr: Smallint; sum: double; full_statement: WideString): IResultCMD; stdcall;


  • Параметры, передаваемые функции OrderBankout:

  • Form: TMultipartForm - объект, заполненный всеми параметрами, полученными с помощью функции GetlistOutbankRequisite; количество параметров зависит от выбранной страны;
  • country: Smallint – код страны;
  • curr: Smallint – код валюты; актуально для тех стран, где имеют официальное распространение две и более валют, например, страны Европы, где кроме евро может использоваться и другая национальная валюта;
  • sum: double – сумма пополнения, дробное число с двумя знаками после запятой;
  • full_statement – формулировка платежного поручения, с которой необходимо отправить средства на расчетный счет.

    Рассмотрим пример, в котором формируется и заполняется объект Form и осуществляется вызов функции OrderBankout.

    procedure TfmOutBank.btnOkClick(Sender: TObject);
    var
      Form: TMultipartForm;
      Row: Smallint;
      Res: IResultCMD;
    begin
      Form := TMultipartForm.Create;
      for row:=0 to pRequisite.ControlCount-1 do
        if pRequisite.Controls[Row] is TEdit then
          Form.AddFormField(TEdit(pRequisite.Controls[Row]).HelpKeyword, TEdit(pRequisite.Controls[Row]).Text );
    
      Res := MainForm.api.CMD.OrderBankout(Form, 
             StrToInt(ResCnt.FindFieldValue('inc', 'country_code', cbCountry.Value)),
             StrToInt(ResCnt.FindFieldValue('inc', 'curr_code', cbCountry.Value)),
             edtSum.Value, mFullStatement.Text);
    
      ShowMessage(Res.result_text);
    end;

    Для ознакомления с полным текстом примера смотрите модуль uOutBank.

    Вывод средств в другую платежную систему Вывод средств в различные платежные системы осуществляется похожими методоми, поэтому они все будут рассмотрены в рамках одного параграфа.

    Поддержка вывода осуществлена для следующих платежных систем:

  • Delta Key;
  • WebMoney;
  • Яндекс.Деньги;
  • RbkMoney;
  • MoneyMail;
  • Moneta.ru;
  • Единый кошелек;
  • Мобильный кошелек QIWI;


    Операция вывода средств в E-gold осуществляется через отправку пользователя в обменный пункт с помощью функции FromExchanger, не требующей никаких дополнительных параметров:

  • procedure FromExchanger; stdcall;


  • Рассмотрим вывод средств в остальные платежные системы. Для каждой из них предусмотрена отдельная функция, осуществляющая операцию:

  • Delta Key:
    function OutDeltakey(keyt: WideString; sum: double): IResultCMD; stdcall;
  • WebMoney:
    function OutWebmoney(wallet: WideString; sum: double): IResultCMD; stdcall;
  • Яндекс.Деньги:
    function OutYandex(account: WideString; sum: double): IResultCMD; stdcall;
  • Rupay:
    function OutRupay(account: WideString; sum: double): IResultCMD; stdcall;
  • MoneyMail:
    function OutMoneyMail(email: WideString; sum: double): IResultCMD; stdcall;
  • Moneta.ru:
    function OutMonetaRU(account: WideString; sum: double): IResultCMD; stdcall;
  • E-port:
    function OutEPort(number, control_code: WideString; sum: double): IResultCMD; stdcall;
  • Музыкальная карта:
    function OutMusic(account: WideString; sum: double): IResultCMD;
  • Единый кошелек:
    function Out1Wallet(account: WideString; sum: double): IResultCMD; stdcall;
  • Мобильный кошелек:
    function OutMobile(phone: WideString; sum: double): IResultCMD; stdcall;

    Все функции кроме OutEPort запрашивают по два параметра, первый из которых является номером счета, кошелька, телефона или емейлом, а второй – сумма в виде дробного числа с двумя знаками после запятой. Возвращаемое значение – структура типа IResultCMD, в случае ошибки будет сгенерирована исключительная ситуация, а пользователю показан текст ошибки сервера. Функция OutEPort запрашивает два параметра – номер карты и контрольный код, так как именно эти параметры предусмотрены этой системой для пополнения.

    В приведенном ниже листинге приведен код модуля uOut, в котором все операции совершаются с помощью одной формы.

    unit uOut;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, sEdit, Buttons, sBitBtn, Mask, sMaskEdit, sCustomComboEdit,
      sCurrEdit, sCurrencyEdit;
    
    type
      TfmOut = class(TForm)
        Label1: TLabel;
        lblPs: TLabel;
        lblField1: TLabel;
        lblR: TLabel;
        edtVal1: TsEdit;
        btnSend: TsBitBtn;
        sBitBtn2: TsBitBtn;
        Label6: TLabel;
        Label7: TLabel;
        edtSum: TsCurrencyEdit;
        btnRate: TsBitBtn;
        btnHint: TsBitBtn;
        edtVal2: TsEdit;
        lblField2: TLabel;
        procedure FormShow(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure btnRateClick(Sender: TObject);
        procedure btnSendClick(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure btnHintClick(Sender: TObject);
        procedure sBitBtn2Click(Sender: TObject);
        procedure edtVal1KeyPress(Sender: TObject; var Key: Char);
        procedure edtVal1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
      private
        { Private declarations }
        rate_create: Boolean;
      public
        { Public declarations }
        payform: Integer;
        ps: WideString;
        hint: WideString;
      end;
    
    var
      fmOut: TfmOut;
    
    implementation
    
    uses MainUnit, uApiInterface, uRate;
    
    {$R *.dfm}
    
    procedure TfmOut.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      Action := caFree;
    end;
    
    procedure TfmOut.FormCreate(Sender: TObject);
    begin
      rate_create := False;
    end;
    
    procedure TfmOut.FormShow(Sender: TObject);
    begin
      lblPs.Caption := ps;
      lblPs.Left := (ClientWidth - lblPs.Width) div 2;
      if payform = 56 then lblR.Visible := True;
      if payform=194 then
      begin
        lblField2.Visible := True;
        edtVal2.Visible := True;
        fmOut.Height := 220;
      end;
      
      btnHint.Hint := Hint;
    end;
    
    procedure TfmOut.btnSendClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
    
      if payform=56 then 
        Res := MainForm.api.CMD.OutWebmoney(edtVal1.Text, edtSum.value)
      else if payform=33  then 
        Res := MainForm.api.CMD.OutYandex(edtVal1.Text, edtSum.value)
      else if payform=182 then 
        Res := MainForm.api.CMD.OutRupay(edtVal1.Text, edtSum.value)
      else if payform=41  then 
        Res := MainForm.api.CMD.OutMoneymail(edtVal1.Text, edtSum.value)
      else if payform=140 then 
        Res := MainForm.api.CMD.OutMonetaRU(edtVal1.Text, edtSum.value)
      else if payform=164 then 
        Res := MainForm.api.CMD.OutMusic(edtVal1.Text, edtSum.value)
      else if payform=199 then 
        Res := MainForm.api.CMD.OutMobile(edtVal1.Text, edtSum.value)
      else if payform=413 then 
        Res := MainForm.api.CMD.Out1Wallet(edtVal1.Text, edtSum.value)
      else if payform=194 then 
        Res := MainForm.api.CMD.OutEPort(edtVal1.Text, edtVal2.Text, edtSum.value);
           
      ShowMessage('Заявка на перевод принята. '+
            'Результаты обработки смотрите в финансовой статистике');
      Close;
    end;
    
    procedure TfmOut.sBitBtn2Click(Sender: TObject);
    begin
      Close;
    end;
    
    procedure TfmOut.btnHintClick(Sender: TObject);
    begin
      ShowMessage(hint);
    end;
    
    procedure TfmOut.btnRateClick(Sender: TObject);
    var
      p: TPoint;
    begin
      // Комиссия
      if not rate_create then
      begin
        fmRate := TfmRate.Create(Self);
        fmRate.cmdName := 'pay_momental';
        fmRate.subj_param := IntToStr(payform);
        rate_create := True;
      end;
    
      fmRate.count := edtSum.Value;
    
      p.X := btnRate.Left + btnRate.Width;
      p.Y := btnRate.Top;
      p := ClientToScreen(p);
    
      fmRate.Left := p.X;
      fmRate.Top := p.Y;
      fmRate.Show;
    
    end;
    
    procedure TfmOut.edtVal1KeyDown(Sender: TObject; var Key: Word; 
         Shift: TShiftState);
    begin
      if (Shift = [ssCtrl]) and (((Key=67)) or ((Key=86))) then 
        TCustomEdit(Sender).PasteFromClipboard;
    end;
    
    procedure TfmOut.edtVal1KeyPress(Sender: TObject; var Key: Char);
    begin
      if (payform=56) or (payform=33) then
        if (key<>#8) and ((key<'0') or (key>'9')) then key := #0;
    end;
    
    end.

    После создания формы перед ее показом пользователю вызывающий модуль заполняет список глобальных переменных формы fmOut, например:

    procedure TRoasterForm.mOutEportClick(Sender: TObject);
    begin
      // E-port
      fmOut := TfmOut.Create(Self);
      fmOut.payform := 194;
      fmOut.ps := 'E-port';
      fmOut.hint := 'Укажите номер карты и контрольный код';
      fmOut.lblField1.Caption := 'Номер карты:';   fmOut.ShowModal;
    end;

    К содержанию

    Электронные чеки


    ICQMoney позволяет любому пользователю выпустить любое количество и на любую сумму электронные чеки, являющиеся удобным средством расчета в Интернете. С помощью этой технологии можно пополнить счет другого пользователя просто продиктовав ему серию, номер и код активации электронного чека. При создании электронного чека его номинал списывается со счета пользователя, а при активации эта же сумма (за минусом комиссии) зачисляется на счет пользователю, активировавшему электронный чек.

    В ICQMoney реализованы две функции для работы с электронными чеками:

  • Создание электронных чеков;
  • Активация электронных чеков.

    Более полная версия DKCP-протокола позволяет также проверить действительность электронного чека и узнать её номинал без активации (без кода пополнения), просмотреть список выписанных электронных чеков, аннулировать электронный чек. Данные функции доступны пользователям на сайте и могут также быть реализованы в ICQMoney.

    Создание электронных чеков происходит при вызове функции CreateEcheck:

  • function CreateEcheck(nominal: double; count: Integer): IResultCMD; stdcall;


  • С помощью этой функции может быть создано более одного электронного чека, и их количество передается в параметре count. Так как электронные чеки могут быть разных номиналов, т.е. выписаны на любые суммы, то в функцию также необходимо передать параметр nominal.

    В результате вызова функции будут возвращены все серии, номера и коды активации выписанных электронных чеков, и все эти данные должны быть показаны пользователю. Именно по этим данным пользователь сможет активировать электронный чек на свой счет или передать данные для активации другому пользователю системы. Данные для активации возвращаются в результирующей структуре в дополнительном параметре all_echeck, при этом все параметры разделяются точкой с запятой (;). Для более удобного показа данные могут быть предварительно отформатированы как в приведенном ниже примере.


    Пример вызова функции:

    procedure TfmEcheckCreate.btnCreateClick(Sender: TObject);
    var
      Res: IResultCMD;
      arrStr: TStringArr;
      Row: Integer;
    begin
      Res := MainForm.api.CMD.CreateEcheck(edtNominal.Value, edtCount.AsInteger);
      ShowMessage(Res.result_text);
    
      fmDlgShowInfo := TfmDlgShowInfo.Create(Self);
      fmDlgShowInfo.lblInfo.Caption := 'Созданы электронные чеки:';
    
      // замена
      arrStr := MainForm.api.SplitString(Res.GetAnswerValue('all_echeck'), ';');
      for Row:=0 to Length(arrStr)-1 do
        fmDlgShowInfo.mInfo.Lines.Add(arrStr[Row]);
    
      fmDlgShowInfo.ShowModal;
    end;

    В результате выполнения этого после создания кода будет выдано следующее окно:


    При этом важно, чтобы полученную информацию пользователь мог скопировать и/или сохранить в файл.

    Активация электронного чека выполняется с помощью функции ActiveEcheck:

  • function ActiveEcheck(serial: WideString; number, code: Int64): IResultCMD; stdcall;


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


    Пример кода, реализующего активацию электронного чека:

    procedure TfmFillEcheck.btnActiveClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      Res := mainForm.api.CMD.ActiveEcheck(edtSerial.Text, 
             StrToInt64(edtNumber.Text), StrToInt64(edtCode.Text));
      ShowMessage(Res.Result_text);
      if not cbNoClose.Checked then Close;
    end;

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

    К содержанию

    Пластиковые карты


    Пластиковые карты предоставляют удобный способ вывода средств из системы. Для организации этого сервиса пользователь должен заказать карту, сразу оплатив ее со своего счета в системе, получить по почте, активировать, после чего в случае необходимости может заказать вывод средств на нее в любое удобное время. Вывод средств с этой пластиковой карты возможен в любом банкомате, в любой точке мира. Количество карт у пользователя не ограничено.

    Полный список операций, возможный с пластиковыми картами:

  • Получение списка возможных типов карт для заказа;
  • Заказ карты;
  • Получение списка имеющихся карт;
  • Переименование собственной карты;
  • Активация;
  • Блокировка карты (например, в случае потери);
  • Заказ вывода средств на карту;
  • Получение информации о заказах на вывод.

    Рассмотрим данные операции более подробно.

    Получение списка возможных типов карт для заказа

    Для данного действия предусмотрена функция GetTypesMCard:

  • function GetTypesMCard(): IResultCMD; stdcall;


  • Вызов функции не предполагает передачи ей каких-то дополнительных параметров. Значение возвращается по запросу к серверу в структуре типа IResultCMD, в табличной части которой имеются следующие поля:

  • num – код типа; данный код может быть использован при заказе карты определенного типа;
  • name – название;
  • price – цена; в качестве разделителя целой и дробной части используется точка, поэтому для использования в расчетах этот символ должен быть заменен на системный разделитель;
  • curr – код валюты;
  • curr_text – название валюты;
  • admin_text – описание; здесь могут быть указаны тарифы и условия использования карты.

    При заказе карты пользоваетелю необходимо выдать список возможных карт, например, в комбобоксе, а при выборе какой-то карты ее параметры должны быть показаны отдельно:


    Пример кода, реализующего приведенную форму:

    var
      ResType: IResultCMD; // список типов карт
    
    ...
    
    procedure TfmMcardOrder.FormShow(Sender: TObject);
    var
      Row: Smallint;
    begin
      ResType := MainForm.api.CMD.GetTypesMCard();
      for Row:=0 to ResType.Fields.RowCount-1 do
        cbType.AddItemValue(ResType.GetFieldValue('name', Row), 
                 ResType.GetFieldValue('num', Row));
    end; 
    
    procedure TfmMcardOrder.cbTypeChange(Sender: TObject);
    begin
      mAdminText.Text := ResType.FindFieldValue('num', 'admin_text', cbType.Value);
      edtSum.Text := AnsiReplaceStr( ResType.FindFieldValue('num', 'price', 
            cbType.Value), '.', DecimalSeparator );
      lblCurr.Caption := ResType.FindFieldValue('num', 'curr_text', cbType.Value);
    end;

    Заказ карты

    После получения списка карт пользователю остается только нажать кнопку «Заказать», и в случае успешного ответа сервера цена карты будет списана с пользовательского счета, а заказ поступит на рассмотрение оператору.

    Операция выполняется с помощью функции OrderMCard:

  • function OrderMCard(mcard_type: Smallint): IResultCMD; stdcall;


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

    procedure TfmMcardOrder.btnOrderClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      if cbType.Value = '' then
      begin
        MessageDlg('Выберите тип пластиковой карты', mtWarning, [mbOk], 0);
        if cbType.CanFocus then cbType.SetFocus;
        Exit;
      end;
    
      Res := MainForm.api.CMD.OrderMCard(StrToInt(cbType.Value));
      ShowMessage(Res.result_text);
      Close;
    end;

    Получение списка имеющихся карт

    Вне зависимости от того, в каком статусе находится карта, – активирована она или заказ на нее только что оформлен – все они отображаются в списке имеющихся у пользователя карт. Для получения такого списка используется функция GetListMCard:

  • function GetListMCard(): IResultCMD; stdcall;


  • Сам список возвращается в табличной части результирующей структуры указанной функции:

  • order – № заказа; каждый заказ карты имеет уникальный номер; так как сразу после заказа № карты еще не известен (поле num), то во многие функции передается именно № заказа;
  • num – № карты; 16 цифр, определяющих номер пластиковой карты;
  • type – код типа; определяет, какого типа карта была заказана;
  • type_text – тип карты;
  • uname_card – название карты; может быть изменено пользователем для его более удобной навигации в списке;
  • expiration_date – дата истечения; дата окончания срока действия карты;
  • activation – признак активности; активация происходит пользователем после получения карты по почте; вывод средств на карту не может быть осуществлен до ее активации;
  • activation_text – активирована; текстовое представление поля activation;
  • stoplist – признак блокировки; в случае утери, кражи или других обстоятельств пользователь может заблокировать карту;
  • stoplist_text – блокировка; текстовое представление поля stoplist;
  • status – код статуса;
  • status_text – статус; текстовое представление поля status;
  • transact – транзакция заказа.


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

    В системе предусмотрены следующие статусы карт:

    Код статуса Расшифровка статуса Разрешенные операции с картой в данном статусе
    Переименование Заказ вывода средств Активация Блокировка
    1Оплачена, нет документов от держателяДаНетНетНет
    2Документы от держателя на рассмотрении системойДаНетНетНет
    3Документы отправлены в банкДаНетНетНет
    4Документы на рассмотрении банкаДаНетНетНет
    5Карта выслана банком системеДаНетНетНет
    6Карта выслана системой держателюДаНетДаНет
    7Карта заблокирована держателемДаНетНетНет
    8Карта заблокирована банкомДаНетНетНет
    9Карта заблокирована системойДаНетНетНет
    10Карта не активирована держателемДаНетНетНет
    11Карта активнаДаДаНетДа
    12Истек срок действияДаНетНетНет
    13Карта закрытаДаНетНетНет

    В приведенном ниже примере рассматривается получение списка карт, и разрешение/запрет на выполнение некоторых операций:

    var
      ResList: IResultCMD; // список карт
      was_show: Boolean; // определяет, было ли уже произведено отображение карт
    
    ...
    
    procedure TfmMCardList.btnShowClick(Sender: TObject);
    begin
      // Загрузка списка
      ResList := MainForm.api.CMD.GetListMCard;
      MainForm.api.Face.ShowResInGrid(aGrid, ResList, 'getlist_mcard', 100,
          'num,uname_card,expiration_date,activation_text,stoplist_text,status_text');
      ResizeGrid;
      if ResList.Fields.RowCount > 0 then was_show := True;
      EnableButtons;
    end;
    
    procedure TfmMCardList.PopupMenu1Popup(Sender: TObject);
    var
      activation, status, stoplist: Smallint;
    begin
      // Изменить активность, блокировку, разблокировку, возможность вывода
      if not was_show then Exit;
    
      mActive.Enabled := False;
      mBlock.Enabled := False;
      mOut.Enabled := False;
    
      activation := StrToInt(ResList.GetFieldValue('activation', aGrid.Row-1));
      status := StrToInt(ResList.GetFieldValue('status', aGrid.Row-1));
      stoplist := StrToInt(ResList.GetFieldValue('stoplist', aGrid.Row-1));
    
      if (activation=0) and (status=6) then mActive.Enabled := True;
    
      if (stoplist=0) and (activation=1) then mBlock.Enabled := True;
    
      if (activation=1) and (status=11) and (stoplist=0) then mOut.Enabled := True;
    
    end;
    
    procedure TfmMCardList.EnableButtons;
    var
      activation, status, stoplist: Smallint;
    begin
      if not was_show then Exit;
    
      btnActive.Enabled := False;
      btnBlock.Enabled := False;
      btnOut.Enabled := False;
    
      activation := StrToInt(ResList.GetFieldValue('activation', aGrid.Row-1));
      status := StrToInt(ResList.GetFieldValue('status', aGrid.Row-1));
      stoplist := StrToInt(ResList.GetFieldValue('stoplist', aGrid.Row-1));
    
      if (activation=0) and (status=6) then btnActive.Enabled := True;
    
      if (stoplist=0) and (activation=1) and (status=11) then
      begin
        btnBlock.Enabled := True;
        btnOut.Enabled := True;
      end;
    end;
    
    procedure TfmMCardList.aGridMouseDown(Sender: TObject; Button: TMouseButton; 
      Shift: TShiftState; X, Y: Integer);
    begin
      // добавим реакцию на правую кнопку
      if button=mbRight then
      begin
        SendMessage(aGrid.Handle,WM_LBUTTONDOWN,MK_LBUTTON,x+y shl 16);
        SendMessage(aGrid.Handle,WM_LBUTTONUP,MK_LBUTTON,x+y shl 16);
      end;
    
      // Доступность кнопочек
      EnableButtons();
      
    end;

    Переименование собственной карты

    Операция переименования производится для более удобной ориентации пользователя в списке его карт, и выполняется с помощью функции RenameMCard:

  • function RenameMCard(order_num: Integer; new_name: WideString): IResultCMD; stdcall;


  • Переименование может быть выполнено вне зависимости от статуса карты. В результате выполнения функции будет возвращена строка результата, при этом если результат неуспешный, то будет сгенерирована исключительная ситуация, а пользователю показан текст ошибки сервера.

    Параметр order_num определяет номер заказа, а new_name – новое имя карты. Пример вызова операции:

    procedure TfmMCardList.btnRenameClick(Sender: TObject);
    var
      s, old_value, res_text: WideString;
    begin
      // Переименование
      if not was_show then Exit;
      
      old_value := aGrid.Cells[2, aGrid.Row];
      s := InputBox('Новое название карты', 
                 'Введите новое название карты, под которым она будет отображаться',
                 old_value);
    
      if s=old_value then
        Exit;
    
      res_text := MainForm.api.CMD.renameMCard(StrToInt(ResList.GetFieldValue('order', 
        aGrid.Row-1)), s);
      ShowMessage(res_text);
      aGrid.Cells[2, aGrid.Row] := s;
    
    end;

    Активация карты

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

    Активация пластиковой карты выполняется с помощью функции ActiveMCard:

  • function ActiveMCard(order_num: Integer; cvc: WideString): IResultCMD; stdcall;


  • В функцию необходимо передать номер заказа (order_num) и CVC карты.

    Пример реализации операции активации:

    procedure TfmMCardList.btnActiveClick(Sender: TObject);
    var
      Res: IResultCMD;
      cvc: WideString;
    begin
      // Активация
      if not was_show then Exit;
    
      cvc := InputBox('CVC', 'Введите CVC полученной карты', '');
      if cvc = '' then Exit;
      Res := MainForm.api.CMD.ActiveMCard(StrToInt(ResList.GetFieldValue(
                'order', aGrid.Row-1)), cvc);
      ShowMessage(Res.result_text);
      btnShow.Click;
    end;

    После активации карта автоматически переходит в статус 11 («Активна»).

    Блокировка карты

    В случае утери, кражи или других обстоятельств пользователь может заблокировать карту для предотвращения несанкционированного ее использования сторонними лицами.

    Операция блокировки выполняется с помощью функции BlockMCard:

  • function BlockMCard(order_num: Integer): IResultCMD; stdcall;


  • В качестве единственного параметра в функцию передается номер заказа карты. Пример вызова функции:

    procedure TfmMCardList.btnBlockClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      // Заблокировать
      if not was_show then Exit;
    
      if MessageDlg('Вы выполняете операцию блокировки карты.'+#13+
                'Операция разблокировки будет возможна только через ' +#13+
                'взаимодействие со службой безопасности системы. '+#13+
                'Вы уверены, что хотите заблокировать карту?', mtWarning, 
                mbYesNo, 0) <> mrYes then Exit;
    
      Res := MainForm.api.CMD.BlockMCard(StrToInt(ResList.GetFieldValue(
             'order', aGrid.Row-1)));
      ShowMessage(Res.result_text);
      btnShow.Click;
    end;

    Заказ вывода средств на карту

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

    Инициализация вывода средств происходит путем вызова функции PayMCard:

  • function PayMCard(mcard_num: Int64; sum: double): IResultCMD; stdcall;


  • В качестве параметров в функцию передается номер карты (mcard_num) и сумма вывода в валюте счета пользователя (sum).


    Пример реализации вывоза данной операции:

    var
      ResMCard: IResultCMD;
    
    ...
    
    procedure TfmOutMCard.FormShow(Sender: TObject);
    var
      row: Integer;
    begin
      // показываем карты
      ResMCard := MainForm.api.CMD.GetListMCard;
      for Row:=0 to ResMCard.Fields.RowCount-1 do
        cbMCard.AddItemValue(ResMCard.GetFieldValue('uname_card', row) +
             ' ('+ ResMCard.GetFieldValue('num', Row) + ')', 
             ResMCard.GetFieldValue('num', Row));
    end;
    
    procedure TfmOutMCard.btnOrderClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      // Заказ вывода на карту
      Res := MainForm.api.CMD.PayMCard(StrToInt64(cbMCard.Value), edtSum.Value);
      ShowMessage(Res.result_text);
      Close;
    end;

    Получение информации о заказах на вывод

    Заказ на вывод средств может обрабатываться достаточно продолжительное время, в течение которого пользователю просто необходимо получать информацию о состоянии обработки его заказа. Для реализации данной концепции используется функция GetListMCardRequest:

  • function GetListMCardRequest(): IResultCMD; stdcall;


  • Функция не требует передачи каких-либо параметров, и в табличной части результирующей структуры выдает информацию о состоянии обработки заказанных выводов средств.


    В приведенном ниже примере реализована также подстветка каждого заказа в зависимости от состояния его обработки (принцип светофора: зеленые – успех, желтые – в обработке, красные – отказ в обработке).

    procedure TfmOutMCardList.mShowClick(Sender: TObject);
    begin
      // отображение списка заявок
      ResShow := MainForm.api.CMD.GetListMCardRequest;
      MainForm.api.Face.ShowResInGrid(aGrid, ResShow, 'getlist_mcard_request', 100,
        'num_order,num_card,itogo_shop,curr_shop,date_order,date_out,result_text');
      WasShow := True;
      ResizeGrid;
    end;
    
    procedure TfmOutMCardList.aGridDrawCell(Sender: TObject; ACol, ARow: Integer; 
      Rect: TRect; State: TGridDrawState);
    Const
      clrGreen1 = $00d3ffd8;
      clrRed1 = $00a9a9ff;
      clrYellow1 = $00cbfff7;
      clrGray1 = $00d4d4d4;
    begin
      if (not WasShow) or (ARow = 0) or (ACol = 0) then Exit;
    
      (Sender as TStringGrid).Canvas.Brush.Color := clrGray1;
    
      if (ARow>=1) and (ResShow.Fields.RowCount>0) then
      begin
        if ResShow.GetFieldValue('done', ARow-1)='1' then
          (Sender as TStringGrid).Canvas.Brush.Color := clrGreen1;
        if ResShow.GetFieldValue('done', ARow-1)='-1' then
          (Sender as TStringGrid).Canvas.Brush.Color := clrRed1;
        if ResShow.GetFieldValue('done', ARow-1)='0' then
          (Sender as TStringGrid).Canvas.Brush.Color := clrYellow1;
      end;
    
      (Sender as TStringGrid).Canvas.Font.Color := clBlack;
    
      //активная строка
      if ARow = (Sender as TStringGrid).Row then
        (Sender as TStringGrid).Canvas.Brush.Color :=
               (Sender as TStringGrid).Canvas.Brush.Color - $00111111;
    
      (Sender as TStringGrid).Canvas.FillRect(Rect);
      (Sender as TStringGrid).Canvas.TextOut(Rect.Left+2, Rect.Top+2,
             (Sender as TStringGrid).Cells[ACol,ARow]);
    end;

    К содержанию

    Платежные требования (счета к оплате)


    Платежные требования (выставленные счета) как один из сервисов ICQMoney позволяют одному пользователю затребовать (попросить) средства у другого пользователя системы. Пользователь, который получит данный счет на оплату, сможет оплатить этот счет, отказаться от оплаты, либо отложить решение. Сообщение о получении выставленного счета отображается в Информаторе-OnLine.

    Функция CreatePayrequest позволяет пользователю выставить счет на оплату другому пользователю.

  • function CreatePayrequest(user: WideString; sum: double; description: PChar; system_num: Integer): IResultCMD; stdcall;


  • Параметры, передаваемые функции:

  • user: WideString – UIN пользователя
  • sum: double – сумма, которая поступит пользователю, выставившему счет, после его оплаты
  • description: PChar – текстовое описание, например, «Оплата доступа в Интернет», «Требование средств за хранение информации» и любое другое описание
  • system_num: Integer – номер счета в учетной системе пользователя, выставившего счет. Служит инструментом, позволяющим пользователю легче ориентироваться и вести учет выставленных к оплате счетов.

    Возвращает строку – результат выполнения запроса на сервер. В случае неуспешного запроса генерируется исключительная ситуация и пользователю выдается текст ошибки сервера.


    Пример вызова:

    procedure TfmCreatePayrequest.sBitBtn1Click(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      // Запрос требования
      Res := MainForm.api.CMD.CreatePayrequest(StrToInt(user), edtSum.Value, 
              PAnsiChar(mNote.Text), edtNum.IntValue);
      ShowMessage(Res.result_text);
      Close;
    end;

    После выставления счета пользователь может посмотреть список всех счетов, которые он выставил, с помощью функции GetlistOutPayrequest:

  • function GetlistOutPayrequest(date_start, date_end: TDate; status: Shortint=-1): IResultCMD; stdcall;


  • В эту функцию необходимо передать диапазон дат, в течение которого были выставлены счета (date_start, date_end), а также статус, в котором они в настоящий момент находятся (status). Передача в параметр статуса значения по-умолчанию (-1) запрещает отбор по статусу.

    Функция в табличной части результирующей структуры возвращает следующие данные:

  • payrequest_num – № счета;
  • system_num – уникальный №;
  • sum_shop – сумма;
  • pay_name – плательщик;
  • shop_name – получатель;
  • description – описание;
  • date_in – выставлен;
  • date_out – дата оплаты;
  • transact – транзакция;
  • status – код статуса;
  • result – код результата;
  • status_text – статус;
  • result_text – результат.


    Пример реализации получения списка выставленных самим пользователем счетов (а также подстветка счетов в зависимости от их статуса оплаты):

    var
      WasShow: Boolean;
      ResShow: IResultCMD;  
    
    ...
    
    procedure TfmPayrequestList.mShowClick(Sender: TObject);
    begin
      ResShow := MainForm.api.CMD.GetlistOutPayrequest(dtpDateStart.Value, 
             dtpDateEnd.Value);
      MainForm.api.Face.ShowResInGrid(aGrid, ResShow, 'out_payrequest', 100,
          'payrequest_num,system_num,sum_shop,pay_name,description,date_in,' + 
          'date_out,status_text,result_text');
      ResizeGrid;
    end;
    
    procedure TfmPayrequestList.aGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect;
      State: TGridDrawState);
    Const
      clrGreen1 = $00d3ffd8;
      clrRed1 = $00a9a9ff;
      clrYellow1 = $00cbfff7;
      clrGray1 = $00d4d4d4;
    begin
      //светофор
      (Sender as TStringGrid).Canvas.Brush.Color := clrGray1;
    
      if (WasShow) and (ARow>=1) and (ResShow.Fields.RowCount>0) then
      begin
        if ResShow.GetFieldValue('result', ARow-1)='23' then
          (Sender as TStringGrid).Canvas.Brush.Color := clrYellow1;
        if ResShow.GetFieldValue('result', ARow-1)='24' then
          (Sender as TStringGrid).Canvas.Brush.Color := clrGreen1;
        if ResShow.GetFieldValue('result', ARow-1)='25' then
          (Sender as TStringGrid).Canvas.Brush.Color := clrRed1;
      end;
    
      (Sender as TStringGrid).Canvas.Font.Color := clBlack;
    
      //активная строка
      if ARow = (Sender as TStringGrid).Row then
        (Sender as TStringGrid).Canvas.Brush.Color :=
          (Sender as TStringGrid).Canvas.Brush.Color - $00111111;
    
      //фиксированные строки
      if (ARow = 0) or (ACol = 0) then
        (Sender as TStringGrid).Canvas.Brush.Color := clBtnFace;
    
      (Sender as TStringGrid).Canvas.FillRect(Rect);
      (Sender as TStringGrid).Canvas.TextOut(Rect.Left+2, Rect.Top+2,
        (Sender as TStringGrid).Cells[ACol,ARow]);
    end;

    О выставлении счета получатель уведомляется с помощью Информатора-OnLine, и получает возможность оплаты или отказа от оплаты платежного требования. Получить параметры счета на оплату можно, вызвав функцию GetparamsPayrequest и передав в нее номер выставленного счета.

  • function GetparamsPayrequest(payrequest_num: Integer): IResultCMD; stdcall;


  • В табличной части результирующей структуры возвращаются все параметры счета на оплату:

  • payrequest_num – номер платежного требования;
  • system_num – номер счета;
  • transact – транзакция;
  • description – описание;
  • sum_pay – сумма плательщика;
  • sum_shop – сумма получателя;
  • date_in – дата выставления;
  • date_out – дата оплаты;
  • status – код статуса;
  • result – код результата;
  • status_text – статус;
  • result_text – результат;
  • pay_name – плательщик;
  • shop_name – получатель.

    Оплата платежного требования осуществляется с помощью вызова функции PayPayrequest:

  • function PayPayrequest(payrequest_num: Integer): IResultCMD; stdcall;


  • В качестве единственного параметра в нее передается номер полученного платежного требования.

    Отказ от оплаты требования происходит с помощью вызова функции RefusePayrequest, которая также в качестве параметра принимает номер полученного платежного требования:

  • function RefusePayrequest(payrequest_num: Integer): IResultCMD; stdcall;



  • Пример реализации кода получения параметров счета, оплаты и отказа от оплаты:

    var
      payrequest_num: Integer;
    
    ...
    
    procedure TfmPayPayrequest.FormShow(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      // Параметры счета на оплату
      lblCaption.Caption := 'Оплата счета № ' + IntToStr(payrequest_num);
    
      Res := MainForm.api.CMD.GetparamsPayrequest(payrequest_num);
      edtSum.Text := AnsiReplaceStr( Res.GetFieldValue('sum_pay'), '.', 
             DecimalSeparator );
      lblCorr.Caption := Res.GetFieldValue('shop_name');
      lblStatus.Caption := Res.GetFieldValue('status_text'); 
      mComment.Text := Res.GetFieldValue('description');
    end;
    
    procedure TfmPayPayrequest.btnPayClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      Res := MainForm.api.CMD.payPayrequest(payrequest_num);
      ShowMessage(Res.result_text);
      Close;
    end;
    
    procedure TfmPayPayrequest.btnRefuseClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      Res := MainForm.api.CMD.RefusePayrequest(payrequest_num);
      ShowMessage(Res.result_text);
      Close;
    end;

    К содержанию

    Безопасность


    ICQMoney поддерживает достаточно обширный список настроек безопасности, от самых простых, обязательных для всех пользователей – смена логина, пароля и платежного пароля, до таких как одноразовые ключи, доступ по IP и пр.

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

  • Смена логина; операция позволяет изменить логин пользователя для доступа к сервису ICQMoney.
  • Смена пароля; операция позволяет изменить пароль пользователя для доступа к сервису ICQMoney.
  • Смена платежного пароля; операция позволяет изменить платежный пароль пользователя, применяемый для подтверждения финансовых операций.
  • Доступ по IP; позволяет задать список IP-адресов, с которых разрешены операции в системе ICQMoney.
  • Одноразовые ключи; при активации сервиса одноразовых ключей пользователю генерируется пятнадцать ключей, и каждый раз при авторизации у него запрашивается следующий ключ. После ввода четырнадцати ключей пятнадцатый вводится до тех пор, пока не будет сгенерирована новая серия ключей.
  • Оплата в один клик. Включение оплаты в один клик позволяет пользователю оплачивать на сайтах одним щелчком мыши. Отключение оплаты в один клик запрещает пользователю оплату на сайтах и через мерчант системы, разрешая производить операции только в пределах клиентского приложения ICQMoney.

    В целях безопасности в DLL Uni Transfer отсутствуют последние функции безопасности, и пользователь имеет возможность только сменить логин и пароль для доступа к сервису. Смена платежного пароля и настройка других мер безопасности осуществляется в Интернет-кабинете пользователя.

    Смена логина

    Для операции смены логина используется функция ChangeLogin:

  • function ChangeLogin(newLogin, newLoginConfirm: WideString): IResultCMD; stdcall;


  • В качестве параметров в эту функцию передается новый логин (newLogin) и подтверждение нового логина (newLoginConfirm).


    Пример реализации:

    procedure TfmSecChangeLogin.btnChangeLoginClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      Res := MainForm.api.CMD.ChangeLogin(edtLogin.Text, edtReLogin.Text);
      ShowMessage(Res.result_text);
      MainForm.api.CMD.Authorize;
      Close;
    end;

    Смена пароля Для операции смены пароля используется функция ChangePassword:

  • function ChangePassword(newPassword, newPasswordConfirm: WideString): IResultCMD; stdcall;


  • В качестве параметров в эту функцию передается новый логин (newPassword) и подтверждение нового логина (newPasswordConfirm).


    Пример реализации:

    procedure TfmSecChangePassword.btnChangePasswordClick(Sender: TObject);
    var
      Res: IResultCMD;
    begin
      Res := MainForm.api.CMD.ChangePassword(edtPassword.Text, edtRePassword.Text);
      ShowMessage(Res.result_text);
      MainForm.api.CMD.Authorize;
      Close;
    end;

    К содержанию

    Тарифы


    Система предоставляет пользователю возможность выбрать наиболее удобный для него тариф. По-умолчанию, пользователю автоматически устанавливается тариф «Базовый», в любое время он может его сменить. В базовом тарифе при каждой внутрисистемной операции с пользователя удерживается процент системной комиссии порядка 0,4-0,75%, в других тарифах комиссия может быть уменьшена либо вообще отсутствовать, зато ежемесячно взимается абонентская плата.

    Получение списка тарифов

    Для получения списка тарифов используется функция интерфейса ICMD:

  • function GetlistTariff(): IResultCMD; stdcall;


  • Функция не требует передачи дополнительных параметров. В табличной части возвращается список имеющихся тарифов.

    Пример вызова:

    procedure TfmTariffList.btnShowClick(Sender: TObject);
    var
      Col, Row: Integer;
    begin
      ResShow := MainForm.api.CMD.GetlistTariff();
    
      aGrid.Views[0].DataController.RecordCount := ResShow.Fields.RowCount;
      for row:=0 to ResShow.Fields.RowCount-1 do
      begin
        aGrid.Views[0].DataController.Values[row, 0] := IntToStr(Row+1);
        for col:=0 to ResShow.Fields.ColCount-1 do
          aGrid.Views[0].DataController.Values[row, col] := 
                  ResShow.Fields.fields[row, col];
      end;
    
      lblCurrent.Caption := ResShow.GetAnswerValue('current_name');
    end;

    Получение параметров тарифа

    Для получение параметров выбранного пользователем тарифа используется функция:

  • function GetparamsTariff(num_tariff: Integer): IResultCMD; stdcall;


  • В функцию передается номер тарифа (список номеров вместе с названиями возвращается функцией GetlistTariff).

    Пример реализации:


    unit uTariff;
    
    interface
    
    type
      TfmTariff = class(TForm)
        ...
      public
        { Public declarations }
        num_tariff: Integer;
      end;
    
    ...
    
    implementation
    
    procedure TfmTariff.FormShow(Sender: TObject);
    var
      Res: IResultCMD;
      row, col: Integer;
    begin
      // Показ тарифа
      Res := MainForm.api.CMD.GetparamsTariff(num_tariff);
      lblCaption.Caption := 'Ставки системной комиссии по тарифу "' +
               Res.GetAnswerValue('name') + '"';
      lblDescription.Caption := Res.GetAnswerValue('description');
      lblAbon.Caption := 'Абонентская плата: ' + Res.GetAnswerValue('abon_pay') + 
               ' UNI в месяц';
    
      aGrid.Views[0].DataController.RecordCount := Res.Fields.RowCount;
      for row:=0 to Res.Fields.RowCount-1 do
      begin
        aGrid.Views[0].DataController.Values[row, 0] := IntToStr(Row+1);
        for col:=0 to Res.Fields.ColCount-1 do
          aGrid.Views[0].DataController.Values[row, col+1] := 
                   Res.Fields.fields[row, col];
      end;
    
      // Активный?
      if Res.GetAnswerValue('Current')='1' then
        lblCaption.Caption := lblCaption.Caption + ' (Активный)';
    end;
    
    ...
    
    end.

    Применение тарифа к своему счету

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

  • function ChangeKeytTariff(num_tariff: Integer): IResultCMD; stdcall;


  • Пример реализации:

    procedure TfmTariffList.btnSetClick(Sender: TObject);
    var
      Res: IResultCMD;
      qw: String;
      qwres: Integer;
    begin
      qw := 'Установить тариф "' +
            ResShow.GetFieldValue('name', 
            ViewMain.DataController.GetEditingRecordIndex) +
            '" для Вашего счета?'+#13+
            'При смене тарифа со счета будет списана абонентская плата за месяц';
      qwres := MessageBox(0, PAnsiChar(qw), 'Подтверждение выбора тарифа', MB_YESNO);
      if qwres = IDNo then Exit;
    
      // выбор тарифа
      Res := MainForm.api.CMD.ChangeKeytTariff(StrToInt(ResShow.GetFieldValue(
                 'num', ViewMain.DataController.GetEditingRecordIndex)));
      ShowMessage(Res.result_text);
    
      lblCurrent.Caption := ResShow.GetFieldValue('name',
                 ViewMain.DataController.GetEditingRecordIndex);
    
    end;

    К содержанию

    3. Финансовая модель партнерства


    Партнеры-разработчики ПО взаимодействуют с системой по принципам партнерской программы.

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

    О передаче кода программы в подключаемый модуль см. раздел Описание интерфейсов.


    По вопросам и предложениям относительно сервиса ICQMoney, а также по вопросам данной спецификации обращайтесь в департамент IT-Технологий нашей системы: Контакты

    К содержанию

    Вернуться назад