Как я упоминал ранее, среди прочих нововведений в Delphi XE5 появилась поддержка авторизации по протоколам OAuth 1.0, OAuth 2.0, а вместе с этой возможностью и новая библиотека под названием REST Client Library. Так как чаще всего мне по работе и приходится заниматься написанием компонентов и модулей для работы с различными API — от Google и Facebook до Live Connect и Dropbox API, то, естественно, эта новая библиотека не могла остаться без моего внимания.
И проверить удобство работы с новой библиотекой я решил на сервисах, с которыми я больше всего работал в последнее время, а именно с одним из API Google — Drive API. Забегая немного вперед, скажу, что компоненты библиотеки можно использовать также и при разработке мобильных приложений, но обо всем по порядку.
Компоненты REST Client Library
Итак, вся работа библиотеки строится на использовании трех основных компонентов:
- Клиента (TRESTClient)
- Запроса (TRESTRequest)
- Ответа (TRESTResponse)
Так как большинство веб-сервисов требуют предварительной авторизации пользователя перед тем, как предоставить свои ресурсы API в использование, то в REST Client Library также имеется четыре компонента для авторизации и аутентификации пользователя:
- TSimpleAuthenticator
- THTTBasicAuthenticator
- TOAuth1Authenticator
- TOAuth2Authenticator
И, наконец, в качестве дополнительного компонента можно выделить компонент TRESTResponseDataSetAdapter, который, в некоторых случаях можно использовать для преобразования JSON-данных в набор данных TDataSet.
Сразу следует отметить, что основным форматом представления данных для REST Client Library является JSON.
Рассмотрим работу некоторых компонентов библиотеки с Google Drive API.
Авторизация в Google Drive API. Тестирование подключения.
Для начала работы с Drive API нам необходимо создать новый проект в Консоли Google и получить значения Client ID и Client Secret. О том, как это сделать я рассказывал, например, в статье «Тестирование запросов к API Google средствами Delphi. Компонент OAuthClient для Delphi XE — XE3«. Будем считать, что этот момент работы успешно выполнен и у вас есть необходимые данные для начала работы с сервисом, а заодно и с компонентами REST Client Library.
Теперь создадим новый проект Delphi XE5 и бросим на главную форму компоненты TRESTClient, TRESTRequest, TRESTResponse и TOAuth2Authenticator.
Теперь зададим необходимые параметры, а заодно и протестируем подключение к Google Drive API.
Получение ключей доступа к API в Design-Time
Выбираем на форме компонент OAuth2Authenticator1 и в Object Inspector’e жмем на ссылку «Configure…«:
В открывшемся окне необходимо задать следующие значения для следующих полей:
- Authorization-Endpoint: https://accounts.google.com/o/oauth2/auth
- Token-Endpoint: https://accounts.google.com/o/oauth2/token
- Redirctioon-Endpoint: urn:ietf:wg:oauth:2.0:oob
- Client-ID: значение Client ID вашего проекта
- Client-Secret: значение Client Secret вашего проекта
- Access-Scope: https://www.googleapis.com/auth/drive
В итоге окно настроек компонента примет следующий вид:
Теперь можно протестировать подключение. Жмем кнопку Authorize и, если все данные заданы верно, то откроется окно Google с подтверждением доступа к ресурсам API:
и после подтверждения доступа в окне настроек будут заполнены поля группы «Codes & Tokens«:
Теперь можно жать «Apply» после чего все заданные значения будут переданы в свойства компонента (включая полученные значения из Codes&Tokens).
В принципе, уже сейчас мы можем перейти к работе с ресурсами API, т.к. необходимые ключи доступа у нас уже есть. Однако, при работе с библиотекой нам с большой долей вероятности потребуется получать ключи доступа в run-time и поэтому, в этой части статьи стоит рассмотреть получение ключей доступа без использования рассмотренного выше помощника.
Получение ключей доступа в Run-Time
Для получения ключей доступа в run-time нам потребуется:
- Вызвать окно в котором пользователь подтвердит право доступа к ресурсам
- Получить значение code
- Обменять code на Acess Token
В принципе, эти шаги я уже рассматривал кучу раз в блоге. Теперь воспроизведем их с использованием возможностей REST Client Library.
Подключаем в uses главного модуля нашего приложения модуль REST.Authenticator.OAuth.WebForm.Win. Этот модуль содержит форму Tfrm_OAuthWebForm, которую можно использовать для авторизации пользователя. Tfrm_OAuthWebForm содержит TWebBrowser и кнопку Close и выглядит в дизайнере следующим образом:
У формы определены следующие новые события:
property OnAfterRedirect: TOAuth2WebFormRedirectEvent read FOnAfterRedirect write FOnAfterRedirect; property OnBeforeRedirect: TOAuth2WebFormRedirectEvent read FOnBeforeRedirect write FOnBeforeRedirect; property OnTitleChanged : TOAuth2WebFormTitleChangedEvent read FOnBrowserTitleChanged write FOnBrowserTitleChanged; type TOAuth2WebFormRedirectEvent = procedure(const AURL: string; var DoCloseWebView : boolean) of object; TOAuth2WebFormTitleChangedEvent = procedure(const ATitle: string; var DoCloseWebView : boolean) of object; |
OnBeforeRedirect и OnAfterRedirect срабатывают перед и после того, как нас перенаправят на другой URL в браузере. OnTitleChanged срабатывает, когда у браузера меняется свойствоTitle.
Согласно документации Google по OAuth 2.0., после того, как пользователь даст разрешение на доступ к своим данным, Google вернет нам значение AuthCode, которое мы и должны обменять на ключ доступа. Значение AuthCode также выписывается и в Title в виде строки типа «Success code = …..«.
Таким образом получение ключа доступа к API Google Drive в run-time может выглядеть следующим образом:
var wf: Tfrm_OAuthWebForm; begin //создаем окно с браузером для перенаправления пользователя на страницу Google wf:=Tfrm_OAuthWebForm.Create(self); try //определяем обработчик события смены Title wf.OnTitleChanged:=TitleChanged; //показываем окнои открываем в браузере URL с формой подтверждения доступа wf.ShowModalWithURL(OAuth2Authenticator1.AuthorizationRequestURI); finally wf.Release; end; //меняем AuthCode на AccessToken OAuth2Authenticator1.ChangeAuthCodeToAccesToken; //выводим значения ключей в Memo Memo1.Lines.Add(OAuth2Authenticator1.AccessToken); Memo1.Lines.Add(OAuth2Authenticator1.RefreshToken); Memo1.Lines.Add(DateTimeToStr(OAuth2Authenticator1.AccessTokenExpiry)); end; |
Обработчик события OnTitleChanged у окна будет выглядеть следующим образом:
procedure TForm21.TitleChanged(const ATitle: string; var DoCloseWebView: boolean); begin if (StartsText('Success code', ATitle)) then begin OAuth2Authenticator1.AuthCode:= Copy(ATitle, 14, Length(ATitle)); if (OAuth2Authenticator1.AuthCode <> '') then DoCloseWebView := TRUE; end; end; |
Таким образом мы получаем от Google необходимые значения ключей и можем приступать к непосредственной работе с API Google Drive.
Работа с TRESTRequest и TRESTResponse. Получаем данные от Google Drive.
Теперь можно приступать к получению необходимых данных от Google Drive. Прежде всего проверим настройки компонентов REST Client Library, которые мы расположили на форме нашего приложения. Итак,
у компонента TRESTClient свойства должны иметь следующие значения:
- Autenticator = OAuth2Authenticator1
- BaseURL = ‘https://www.googleapis.com/drive/v2′
у компонента TRESTRequest свойства должны иметь следующие значения:
- Client = RESTClient1
- Response = RESTResponse1
у компонента TRESTResponse все свойства оставляем со значениями по умолчанию.
Теперь попробуем получить список всех файлов, находящихся в Google Drive. Для этого я добавил на главную форму приложения компоненты TListBox для вывода имен файлов иTMemo — для вывода информации о конкретном файле. Главная форма приложения стала выглядеть следующим образом:
Чтобы получить список файлов из Google Drive нам необходимо послать GET запрос на URL https://www.googleapis.com/drive/v2/files.
Для этого воспользуемся компонентом TRESTRequest. Пишем обработчик OnClick кнопки «Получить список файлов»:
procedure TForm21.Button2Click(Sender: TObject); begin RESTRequest1.Method:=rmGET;//определяем HTTP-метод - GET RESTRequest1.Resource:='/files';//путь к ресурсу API RESTRequest1.Execute; //выполняем запрос end; |
Здесь стоит отметить, то, что
После того как запрос будет выполнен, у компонента TRESTRequest сработает событие:
property OnAfterExecute: TCustomRESTRequestNotifyEvent read FOnAfterExecute write FOnAfterExecute; type TCustomRESTRequestNotifyEvent = procedure(Sender: TCustomRESTRequest) of object; |
При этом ответ (TRESTResponse) будет содержать полученные от сервера данные. Напишем обработчик этого события:
procedure TForm21.RESTRequest1AfterExecute(Sender: TCustomRESTRequest); const //тип ответа сервера cResponseKind : array [0..3] of string = ('drive#fileList','drive#file','drive#about','drive#revision'); var JSONObject: TJSONObject; Kind: string; begin if Assigned(Sender.Response.JSONValue) then begin JSONObject:=Sender.Response.JSONValue as TJSONObject; //узнаем тип ответа сервера Sender.Response.GetSimpleValue('kind',Kind); case AnsiIndexStr(Kind, cResponseKind) of 0:ParseFileList(JSONObject); 1:ParseFile(JSONObject); end; end; end; |
Здесь методы ParseFileList и ParseFile используются для разбора JSON и выглядят следующим образом:
procedure TForm21.ParseFile(AJSONObject: TJSONObject); begin Memo1.Lines.Clear; Memo1.Lines.Add('Title: '+AJSONObject.Get('title').JsonValue.Value); Memo1.Lines.Add('Mime-Type: '+AJSONObject.Get('mimeType').JsonValue.Value); Memo1.Lines.Add('Created Date: '+AJSONObject.Get('createdDate').JsonValue.Value); end; procedure TForm21.ParseFileList(AJSONObject: TJSONObject); var FileObject: TJSONObject; Pair: TJSONPair; NextToken: string; ListItems: TJSONArray; I: Integer; begin //получаем параметр URL для получения следующей части списка файлов Pair:=AJSONObject.Get('nextPageToken'); if Assigned(Pair) then NextToken:=Pair.JsonValue.Value; //получаем список файлов ListItems:=AJSONObject.Get('items').JsonValue as TJSONArray; //парсим элементы массива for I := 0 to ListItems.Size-1 do begin FileObject:=ListItems.Get(i)as TJSONObject; //получаем название файла ListBox1.Items.Add(FileObject.Get('title').JsonValue.Value+'//'+FileObject.Get('id').JsonValue.Value); //.... end; //если получены не все файлы, то повторяем запрос, включая в URL параметр pageToken if Length(NextToken)>0 then begin RESTRequest1.Params.Clear; RESTRequest1.Params.AddItem('pageToken',NextToken, pkGETorPOST); RESTRequest1.Execute; end; end; |
Обратите внимание на последнюю часть в методе ParseFileList:
if Length(NextToken)>0 then begin RESTRequest1.Params.Clear; RESTRequest1.Params.AddItem('pageToken',NextToken, pkGETorPOST); RESTRequest1.Execute; end; |
Здесь наш компонент запроса получает также дополнительный параметр pageToken. Этот параметр при выполнении запроса вставляется в URL, либо, если мы выполняли бы POST-запрос — в тело запроса. То есть, в нашем случае, URL запроса выглядел следующим образом:
https://www.googleapis.com/drive/v2/files?pageToken=Dfkdf5g67
Коллекция Params у TRESTRequest может содержать следующие типы параметров:
- pkCOOKIE — параметр передается как Cookies
- pkGETorPOST — параметр передается как параметр в URL для GET-, POST- и PUT-запросов
- pkURLSEGMENT — параметр вставляется в URL как часть этого URL
- pkHTTPHEADER — параметр передается как заголовок запроса
- pkREQUESTBODY — параметр передается в теле запроса и, если определено несколько параметров этого типа, то запрос будет multi-part.
Посмотрим, как можно использовать различные типы параметров в запросе. Для примера, я специально не стал в обработчике OnAfterExecute парсить весь ответ и вытаскивать из ответа все свойства объекта, коих там в достатке, а ограничился только заголовком и идентификатора. Все сведения о файле мы будем получать отдельным запросом, который должен выглядеть, согласно документации Google Drive, следующим образом:
https://www.googleapis.com/drive/v2/files/{fileId}
где fileId — это идентификатор объекта, который мы можем получить вместе с заголовком из предыдущего запроса. То есть, нам выпала возможность использовать значения из коллекции Params в качестве элементов URL. Посмотрим как это можно сделать на практике. Будем выводить информацию по файлу в Memo после клика по его названию в ListBox. Пишем такой обработчик OnClick у ListBox:
procedure TForm21.ListBox1Click(Sender: TObject); var Id: string; begin Id:=Copy(ListBox1.Items[ListBox1.ItemIndex], pos('//',ListBox1.Items[ListBox1.ItemIndex])+2, Length(ListBox1.Items[ListBox1.ItemIndex])); RESTRequest1.Resource:='/files/{fileId}'; RESTRequest1.Method:=rmGET; RESTRequest1.Params.AddItem('fileId', Id, TRESTRequestParameterKind.pkURLSEGMENT); {или так RESTRequest1.Params.AddUrlSegment('fileId', Id); } RESTRequest1.Execute; end; |
Обратите внимание на то как я определил свойство Resource — часть в которую должен вставиться наш параметр обрамлена фигурными скобками, а внутри скобок записано имя параметра.
Вот пожалуй и готов небольшой примерчик взаимодействия с Google Drive с использованием новой библиотеки Delphi XE5 REST Client Library. Теперь при выполнении каждого запроса будет срабатывать OnAfterExecute в обработчике которого мы выбираем необходимые метод для парсинга JSON: ParseFileList, ParseFile и т.д. в зависимости от того, что нам требуется получить от сервера. Остается только добавить, что в папке с исходниками REST Client Library ({Path_To_Delphi_XE5}\source\data\rest\restdebugger\) для вас собран небольшой проектик на FireMonkey под названием RESTDebugger. С помощью этой простенькой программки можно отлаживать свою работу с различными онлайн-сервисами, да и вообще, взглянуть как использовать новые компоненты при работе с FireMonkey.
Выше я представил вам довольно простой пример работы с новыми компонентами, не залезая вглубь каждого компонента и, не рассматривая подробно каждое свойство и событие. Но, думаю, что и представленного выше примера окажется достаточно, чтобы оценить возможности новой библиотеки.
В целом скажу, что работа с REST Client Library мне показалась достаточно простой и удобной. Есть, конечно, что добавить к уже имеющимся компонентам, но уже сейчас библиотека вполне себе юзабельная особенно в VCL-проектах.
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
Рисунки у меня одного не отображаются?
Нет, просто на сервере был откат и последние два поста потерялись, пришлось восстанавливать из кэша Гугла. Пока только перезалил файлы рисунков — кликайте на картинку и увидите её в полном размере
Слово «Autenticator» встречается много раз. Явная опечатка.
да, опечатка. Спасибо, Роман :)
Vlad, огромное спасибо за статью!
всегда рад помочь :)
Под iOS 7.0.4 (у меня эта, за другие не скажу) НЕ РАБОТАЕТ
Спасибо, Влад за ликбез.
Начал юзать компоненты, — все работает вроде нормально.
Но есть роблема с кодировкой.
При передаче в API(мой собственный) например(Builder):
……
RESTRequest1->Params->ParameterByName( «log» )->Value = «Hello!!! Здравствуй мир!!!»;
……
то API принимает такую строку: «Hello!!! A7C4D0C0C2D1D2C2D3C9 CCC8D0!!!»
хотя обратный прием от API кириллицы проходит нормально…
не пойму в чем трабл???
хм..может перед отправкой стоит кодировку строки менять на ту, что установлена на сервере? Например, в UTF8? Попробуй, если поможет — отпишись плз. Хотя судя по приведенному тексту API принял явно что-то «левое» :)
Пробовал так:
RESTRequest1->Params->ParameterByName( «log» )->Value = AnsiToUtf8(«Hello!!! Здравствуй мир!!!»);
непомогло.
Строка A7C4D0C0C2D1D2C2D3C9 CCC8D0 выглядит так, как будто взяли текст, выполнили URLEncoded(), а потом повырезали все %. По-моему тут вопрос больше к API, чем к компонентам. С помощью других компонентов к API обращался? Или просто накидай небольшой скрипт на php и проверь туже строку.
Похоже на кодировку кепшенов в .dfm-ах
Буду копать…
[…] про единственную более менее готовую библиотеку OAuth для Delphi, которую хотел использовать в Twitter’e. Проблема её […]
Пользуюсь функциями Encode64() и Decode64() на стороне билдера и соответствующим кодированем
-декодированием на стороне API хоста.
[…] с API социальной Сети «ВКонтакте«. В принципе, REST Client Library, о которой я уже рассказывал не раз, вполне сгодится […]
ругается на RESTRequest1.Method := rmGET — не пойму в чем дело
Может быть у вас не подключен модуль REST.Types в uses? Или попробуйте написать так:
RESTRequest1.Method := TRESTRequestMethod.rmGET
спасибо! Действительно в uses не было прописано REST.Types. Но теперь ругается «Could not load SSL library.»
Что за облако? Если не секрет. Видимо рядом с EXE-файлом надо положить библиотеки для работы с SSL? По ссылке в списке есть эти библиотеки (libeay32.dll и ssleay32.dll), попробуйте — может поможет решить вашу проблему
Помогло. Спасибо большое. Но опять вышла ошибка :( Project Project1.exe raised exception class EidHTTPProtocolException with message ‘http/1.1 401 unauthorized delphi’
Это уже исключение библиотеки, а не ошибка в коде или ещё где-то. Видимо что-то вы не так настроили или не те параметры передаете или передаете их не так как того требует API. Здесь я вам уже врядли помогу.
u can help me to insert a event in google calendar?
After loging, i´m trying use this:
procedure TfrmPrincipal.Button4Click(Sender: TObject);
var Parametros: TJsonObject;
begin
try
Parametros := TJsonObject.Create;
Parametros.AddPair(‘summary’, TJSONString.Create(‘Sumário’));
Parametros.AddPair(‘location’, TJSONString.Create(‘Local’));
Parametros.AddPair(‘start’, TJSONObject.Create(TJSONPair.Create(‘dateTime’, ‘2014-05-28T17:00:00.000-03:00’)));
Parametros.AddPair(‘end’, TJSONObject.Create(TJSONPair.Create(‘dateTime’, ‘2014-05-28T17:30:00.000-03:00’)));
Parametros.AddPair(‘attendees’, TJsonArray.Create(TJSONObject.Create(TJSONPair.Create(’email’, ‘russo.bradock@gmail.com’))));
RESTRequest1.Method := rmPOST;
RESTRequest1.Resource := ‘/calendars/russo.bradock@gmail.com/events’;
RESTRequest1.AddBody(Parametros.ToString, ctAPPLICATION_JSON);
Memo1.Lines.Clear;
RESTRequest1.Execute;
finally
Parametros.Free;
end;
end;
Well, thanks for all.
(sorry for my english, i´m from Brazil)
[…] 09/09/2011 Vlad 5 Комментарии Delphi в Web На сегодняшний день интерфейс ClientLogin не рекомендуется использовать в API Google. Эта статья относится к архивным и не отражает современного положения дел. Для авторизации в API Google Вы можете использовать модуль для OAuth, или же, если у Вас Delphi XE5 и выше — библиотеку REST Client Library. […]
пробую получить список файлов, но возвращает почему-то только
{
«kind»: «drive#fileList»,
«etag»: «….»,
«selfLink»: «https://www.googleapis.com/drive/v2/files»,
«items»: []
}
т.е. items пустой, хотя файлы точно есть и пробую делать тоже самое на странице https://developers.google.com/drive/v2/reference/files/list — все правильно работает
не подскажете в чем может быть проблема? Delphi — XE6
На какой URL отправляется запрос (точный адрес без сокращений). Скорее всего что-то с ID папки из которой просите файлы или в параметрах что-то намудрено
RestClient — BaseURL —
https://www.googleapis.com/drive/v2
RestRequest — Resource —
files
в PArams — ничего нет
я пробую посмотреть список файлов в корне гугл драйв
у меня сейчас стоит XE7 и в Tools -> REST Debugger всё работает как надо. Если можете — выложите куда-нибудь исходник (без Client ID и Client Secret). То, что это не баг API или Delphi — точно, т.к. у нас этот API реализован и с REST Client Library и без. Ни одного баг-репорта не было
https://drive.google.com/open?id=0B6kR8ImhM6jFOFhLdFNwUk1fY0k&authuser=0
и сразу, если не сложно, можете подсказать как создавать папки, я смотрел документацию гугл, пробовал, но отвечает все время Bad request
я так понял я должен делать POST запрос, и в Params указывать title папки, parent id, и нужный mime-type…
//я так понял я должен делать POST запрос, и в Params указывать title папки, parent id, и нужный mime-type…
не совсем так. Вы в отправляемом на сервер JSON-объекте должны указывать title, а не в параметрах URL
Сегодня посмотрю всё. Как раз ещё один вопрос на подобную тему есть
Откройте мне доступ на файл :)
в RESTDEbugger я тоже проверил — все работает…
сори
https://drive.google.com/file/d/0B6kR8ImhM6jFOFhLdFNwUk1fY0k/view?usp=sharing
А как можно скачать определенный файл, изменить его — и обновить этот файл на гуглодиске?
А как авторизироваться на планшете? Ведь использование формы с Веббраузером не прокатит.
Прокатит. В REST Client Library две формы для авторизации — одна для Win, другая для FMX. Пример того, как авторизоваться в VK на смартфоне Android есть тут
Почитал\поискал, и я так понял, что нет способа получить ID некоторого определённого файла, просто передав Гуглодиску имя этого файла, а в ответ получить его ID — а только как ковырять\анализировать ответ drive#fileList для всех (тысяч) файлов находящихся на Гуглодиске? Как то это нелепо\странно, наверно я плохо искал? Подскажите…
Добрый день, Vlad.
Помогите разобраться. Delphi XE8. REST Oaut2 на Яндекс.Деньги
Конфигуратор OAuth2Authenticator1 успешно получает временный токен.
Run-time — никак…
TitleChanged упорно получает только название страницы моего форума. Никакого токена там нет.
Добрый день, Vlad.
Помогите разобраться. Delphi XE8. REST Oaut2 на Яндекс.Деньги
Конфигуратор OAuth2Authenticator1 успешно получает временный токен.
Run-time — никак…
TitleChanged упорно получает только название страницы моего форума. Никакого токена там нет.
что я упускаю?
Разобрался.. Надо не TitleChanged слушать, а OnAfterRedirect… именно там ловится code=*****
Теперь проблема с PayPal.
Auth-Code вроде получаю, но дальше его никуда прикрутить не удается.
Ругается «Unspecified certificate from client»
и на OAuth2Authenticator2.ChangeAuthCodeToAccesToken; ругается
и на запрос по документации PP
RESTRequest2.Method:=rmPOST; //определяем HTTP-метод — GET
RESTRequest2.Resource:= ‘/v1/identity/openidconnect/tokenservice’;
RESTRequest2.Params.AddItem(‘grant_type’,’authorization_code’,pkGETorPOST);
RESTRequest2.Params.AddItem(‘code’,OAuth2Authenticator2.AuthCode,pkGETorPOST);
RESTRequest2.Params.AddItem(‘redirect_uri’,’http://etacsdecoder.com/forum’,pkGETorPOST);
RESTRequest2.Execute;
есть ощущение, что я вылавливаю совсем не Auth-Code… но именно эта строка идет в ответе после code=
А DLL-ки для SSL лежат рядом с EXE-файлом?
эээ.. это первое упоминание про dll-ки для SSL .. видимо я что-то где-то невнимательно прочитал…
но в любом случае ssl — тема мною еще не изученная . буду искать.
Ну, тема «DLL для SSL» — это, в принципе, работа с протоколом HTTPS, а не только REST Client Library.
но Я.Д ведь тоже по https однако все без длл работает.
это особенность яндекса?
И правильно ли я понял, что на PP просто не надо производить обмен OAuth2Authenticator2.ChangeAuthCodeToAccesToken
вот по этому документу
https://developer.paypal.com/docs/integration/direct/paypal-oauth2/
Спасибо автору за статью. Но у себя на ХЕ6 не могу запустить.
код получаю, access token и refresh token тоже получаю, Но на запрос листинга файлов выдает постоянно 404 ошибку.
Кто-то на ХЕ6 запускал проект?
С ключами все в порядке, т.к. Ваша предыдущая выложенная программа под ХЕ3 работает и выдает листинг файлов.
Уважаемый автор. Не могли бы Вы выложить исходник Вашего рабочего проекта?
Прошу прощения за невнимательность. В списке сообщений под статьей нашел ссылку на проект. Откомпилил на ХЕ6. Все работает. Буду искать ошибки в своих каракулях. Где-то какую-то настройку неправильно поставил наверное.
Еще раз спавибо за статью.