Page 1 of 2
API Bancaria (SOLUCIONADO)
Posted: Mon Apr 12, 2021 8:36 am
by gmart1
Hola, quiero hacer uso de las APIs Bancarias para acceder a los extractos de cuentas.
He comenzado por analizar la API de BBVA. Con el siguiente código he conseguido acceder a las cuentas de un usuario de ejemplo que pone BBVA para hacer pruebas :
Code: Select all | Expand
local oHttp, cCadena, cNomObjOLE := "Microsoft.XMLHTTP", cToken := '', ;
cURL := "https://apis.bbva.com/business-accounts-sbx/v1/statements/aeb43"
oHttp:Open ( "GET" , cUrl, .f. )
oHttp:SetRequestHeader ( "Content-Type", "application/json")
oHttp:SetRequestHeader ( "Accept", "application/json")
oHttp:SetRequestHeader ( "Authorization", cToken )
oHttp:SetRequestHeader ( "Host", "apis.bbva.com" )
try
oHttp:Send ()
catch
? 'Error en Send'
return .t.
end
cCadena := oHttp:responseText
En este ejemplo no he incluido cToken y es donde tengo el problema. Para obtener un token de acceso válido el sistema se basa en oAuth (Three Legged Access). La siguiente información aparece en la BBVA API_Market para obtener el token de acceso :
GET /token/authorize
This endpoint is invoked by a web browser redirection (it requires user interaction). It redirects to the login page, and then shows the scopes approval page. At the end of this flow, this endpoint returns to the redirect_url configured for the associated application, with an authorization_code attached.
This endpoint issues a web browser redirection to the BBVA Connect login page. If necessary, also shows the scope approval page. Finally, another web browser redirection is made to the application’s redirect_uri, attaching a query parameter with the Authorization Code.
With that authorization_code and application’s credentials ( appid + client_secret ), the application must invoke the token endpoint to obtain a valid _SecurityToken.
If there is not any scope specified in the request, the scope of this Security_Token will be the default scope or scopes for each application.
This endpoint returns the access tokens for grant types client_credentials, authorization_code and refresh_token.
Según indican en la información al consumir el servicio con un GET el sistema redirige a una página para que el usuario se identifique y a continuación se pasa el control a una uri de mi aplicación Web. Como estoy intentando acceder desde mi programa de escritorio no sé cómo acceder a la página de identificación y recuperar la respuesta con el token de acceso.
Espero haberme explicado o por lo menos si hay alguien en el foro que haya conseguido acceder a API bancarias y me pudiera echar una mano.
Gracias.
Re: API Bancaria
Posted: Mon Apr 12, 2021 9:12 am
by AngelSalom
Hola, hace un tiempo estuve viendo opciones y quizás te interese esta plataforma.
https://www.afterbanks.com/No llegué a utilizarla porque mis clientes no me solicitan este tipo de acceso, pero antes de meterte con la API de cada entidad igual te sale a cuenta.
Re: API Bancaria
Posted: Tue Apr 13, 2021 9:25 am
by gmart1
Gracias Angel, le echaré un vistazo, pero por lo que he visto por encima es una API y para usarla llegaré al mismo problema con el que me he encontrado con la de BBVA.
Seguiré probando y puede que lo intente con modharbour que me permitirá acceder a la página de identificación y volver a la página web de la aplicación que desarrolle.
Re: API Bancaria
Posted: Fri Apr 16, 2021 4:22 pm
by gmart1
Hola, he podido avanzar en este tema. He conseguido con el authorization_code que me ha devuelto la página de identificación de la banca electrónica de BBVA, acceder a un access_token y con él recuperar los movimientos de una cuenta bancaria de prueba. El siguiente código hace todo el proceso :
Code: Select all | Expand
function BBVA ()
local oHttp, cCadena, aRespuesta := { => }, cCode, cToken, cFicTok := '.\TOK_BBV.TXT', cFicJSon := '.\EXT_BBV.JSON', ;
cNomObjOLE := "Microsoft.XMLHTTP", ;
cRedirect_Uri := "http://localhost", ;
cAppID := "'", cSecretID := "", ; && Esta información se consigue dándose de alta en https://www.bbvaapimarket.com/es y añadiendo una APP
cURL := "https://connect.bbva.com/token"
* Abrir navegador Chrome con la página dónde se solicitan los datos del usuario de la banca electrónica de BBVA
shellexecute ( , "open", "chrome.exe", ;
"https://connect.bbva.com/sandboxconnect?scope=" + ;
"&client_id=" + cAppID + ;
"&redirect_uri=" + cRedirect_Uri + ;
"&response_type=code" )
********* Una vez introducidos usuario y contraseña, el navegador se redije a cRedirect_Url con un parámetro que tiene el código de autorización : ?code=xxxxxx
********* cCode := Recuperar de la ventana del navegador la dirección (por ejemplo : http://http://localhost/?code=F16fo8) y sacar code "F16fo8"
* Realizar un POST con el authorization_code para acceder a un access_token válido
try
oHttp := CreateObject (cNomObjOLE)
catch
? 'Error de creacion oHttp'
return .t.
end
oHttp:Open ( "POST" , cUrl + "?grant_type=authorization_code" + ;
"&code=" + ALLTRIM (cCode) + ;
"&redirect_uri=" + cRedirect_Uri, .f. )
oHttp:SetRequestHeader ( "Authorization", "Basic " + cMimeEnc(cAppID + ':' + cSecretID) )
try
oHttp:Send ()
catch
? 'Error en Send Token'
return .t.
end
cCadena := oHttp:responseText
HB_MemoWrit ( cFicTok, cCadena, .f. )
aRespuesta := hb_jsonDecode (cCadena)
cToken := aRespuesta ['access_token']
* Realizar un GET con el acces_token y acceder a los movimientos de las cuentas
cUrl := "https://apis.bbva.com/business-accounts-sbx/v1/statements/aeb43"
oHttp:Open ( "GET" , cUrl, .f. )
oHttp:SetRequestHeader ( "Content-Type", "application/json")
oHttp:SetRequestHeader ( "Accept", "application/json")
oHttp:SetRequestHeader ( "Authorization", "jwt " + cToken )
oHttp:SetRequestHeader ( "Host", "apis.bbva.com" )
try
oHttp:Send ()
catch
? 'Error en Send'
return .t.
end
cCadena := oHttp:responseText
HB_MemoWrit ( cFicJSon, cCadena, .f. )
oHttp := Nil
return Nil
El problema que tengo ahora es que necesito leer de la ventana de Chrome el authorization_code con que responde la página de identificación.
He conseguido con la función : FindWindow(0, "BBVA Connect Google Chrome") recuperar el hWnd de la ventana, y con GetWindowText(hWnd) el título. Pero cómo puedo acceder al contenido de la ventana ?
Por ejemplo acceder al objeto html que contiene la ventana de Chrome.
Gracias.
Re: API Bancaria
Posted: Mon Apr 19, 2021 10:21 am
by MOISES
Hola:
Probablemente se genere una cookie, que puedas localizar.
Asimismo, quizás sea mejor utilizar un navegador menos intrusivo, como Firefox o Brave, si lo estimas oportuno.
Un saludo
Re: API Bancaria
Posted: Mon Apr 19, 2021 11:45 am
by gmart1
Gracias Moises, pero no hay ninguna cookie.
He incrustado la ventana del navegador dentro de mi programa, pero no sé cómo acceder a la dirección que se muestra para leer el code.

Re: API Bancaria
Posted: Mon Apr 19, 2021 12:18 pm
by Antonio Linares
Que valor tiene cCadena aqui ?
cCadena := oHttp:responseText
Re: API Bancaria
Posted: Mon Apr 19, 2021 12:41 pm
by gmart1
cCadena tiene este valor si previamente le he rellenado en el POST un cCode correcto
{"access_token":"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.XSWOkBMa1fvH5KzfsWVFDQLyJegRTyi-aG4AXh8XiubzfowJlDvtxTnGJTUaKwx_7ASwSfPfMs0VJcQZap6CWvs-HR_MBnHrD6VA3eiy-JvEhNiV2-sYLA-81TmCQj9Zx1oKccRxIRRlXWVAme32CPLTE13JYvocLVeiNIn1zYY2EycyHEEfp9huegwQjU6U19gofXzLCnc8K6GCGLZbm5c2_ZUuzXYk6uIjYzNRHmax2owwPvNRflGz7ZCJRkrP6wBL4oRgfefjTy0-MPfYnW60pbWyHEcu_J1BgAD40WYYxSzUoyCb-Rli8V4xDO8EEAZeHkL7-eJ3LkSXA-uo4A.DGBA9pirTlzwJaNu.788fUGfWZ9sGcL3jPhFC4vO_y5KmZJNdAtAng2ayttCP9ipH2IHFwef2qYfyt8W9-wkC49WxRwGaA0-zytHfWqfBDGA1a1yS419YHxcvFCKEUuyEkd9YXdV96WGKufEimPtd2rJ0hWvvv-6tx7hthbMBV3f6ghFPydYswo3zhzpWuNjZ_AaxQDvB1tNvA9oyJC09_KXP3VcbFOV-C8-sf_2cGoyHV-ejEnvK1lRglmKO2QIJVP5ua8NgeJmnXgTRx1-8Vakhz7NgkA.Nujj611NC6kQ1Nq5D1d5BQ","token_type":"jwt","refresh_token":"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.jve2r-yYAhdwUFI0ha8jhByskyOlteQuaE8cvEX282TTlIDR29y7OkF3pMLtNEl1A9EfP9vSc1E9fCW3W7UoOqgXZtKrde3VDf2zG6CaFlsyQf8UvaTrXIn1rQY1YgH3q8SGpz64IvfgT2WC53MPpE_337iJvrawAM-KXJbS5dRJG_13J0HDZr2KX2n0j91CbQ9GjYO55oyAly-CNf3JUbe3wll30fabHBE84wiIS9TiILYhxazAHqLhmjJUZQhQSCDMzu8Vuf5j3yDmWTufARl_ctn7AjGLLwkQuocSVCqhVuTOwN2S58AqNcCMEHnhSFxsfFRG2gn4q16h6-ShHQ.5YepOfZxXH8kHaXR.5GFQfnfBVg7KSTKiXTCrb4B4CB-zCIJll2G3kB-vW1ja9uJVuaYSajA-Gs6esal1pqPmA1Jh9V2v3GNfgiDz6Cs0MVqnKxS-3_sdh5NqdmlXUlJ17XXxZ9HhTmA7HBqaxL2fpORW06Gwd2BZNfA1L6pc5fdxIJRnw1IY-YWMaDMR2oH969sM8R_ChLHGXrZHPN0ZkcBufCS4H2dnh6-mbF7fNX5pUwGpyKy2P2fCxOnh4XWuZ5f15wvX7zxDx8-R4bYwWLvElocKqxHlwHwNEJZbAb0kP75dckYIV0hT4g73wfkMsQ5j4CHmAhmQzyqc-WjPM8g36qCMZExZeK-Z-idaUub3NBLEFvGSRrrlvXdvRFg2oqw2JzRFUNg_m2QlhKxuhg.ynbLLTwnfypdGymEmpR2aA","expires_in":2699,"scope":"businessAccountsSbx_aeb43","refresh_expires_in":359999}
El problema lo tengo para conseguir el cCode :
- abierto Chrome con Shellexecute y la página de identificación de BBVA Connect
- espero a que el usuario rellene su usuario y contraseña
- la página de BBVA Connect redirije a localhost con el code=
Aparece la pantalla que he puesto antes y en ese momento desde el programa debo leer la dirección en una cadena y sacar lo que haya después de code=
No sé como recuperar la dirección en la que está la ventana de Chrome.
Re: API Bancaria
Posted: Mon Apr 19, 2021 6:50 pm
by Antonio Linares
Gabriel,
Usando FindWindow() y una vez tienes el hWnd de la ventana principal, podrias acceder al hWnd del control donde esta la URL si conoces su ID.
Usa Spy++ (te lo instala Visual Studio Community, y es gratuito) para encontrar este valor. Spy++ es muy potente y en unos minutos aprenderás a usarlo.
En cuanto a usar "Microsoft.XMLHTTP", construye el ejemplo FWH\samles\olebrow.prg y selecciona "Microsoft.XMLHTTP.1.0". Este ejemplo de FWH te muestra "todo" lo que ese objeto OLE puede ofrecerte. Prueba a pedirle otras cosas y a ver si aparece la URL

Re: API Bancaria
Posted: Tue Apr 20, 2021 9:13 am
by gmart1
He instalado spyxx y he localizado la ventana de chrome.
¿Cómo puedo acceder a los controles que contiene ?

Re: API Bancaria
Posted: Tue Apr 20, 2021 1:03 pm
by Antonio Linares
Estás revisando los "threads", selecciona revisar "Windows" (Ctrl+W) desde la primera opción del menú
Lo más sencillo es "Find Window" entonces se te muestra un circulo que has de arrastrar con el ratón y pasarlo por encima de los controles que quieras revisar
Re: API Bancaria
Posted: Tue Apr 20, 2021 1:12 pm
by Antonio Linares
Parece que es una clase propia de Chrome y los controles son simulados.
Intenta por la via del OLE que te he comentado, y si eso también falla, yo lo intentaría con mod_harbour
Re: API Bancaria
Posted: Wed Apr 21, 2021 6:47 am
by gmart1
Muchas gracias, Antonio. He seguido tu consejo y he probado con el objeto OLE "Shell.Explorer.2" y ya he conseguido que funcione.
Al principio lo intenté con este ActiveX, pero se activaba IE versión 8 y la página de ConnectBBVA no se desplegaba bien.
He encontrado en el foro este mensaje dónde viene el código de la función INTER_APP_WEB_CONFIG, que permite asociar a un ejecutable la versión 11 de IE.
https://forums.fivetechsupport.com/viewtopic.php?f=3&t=36913&hilit=INTER_APP_WEB_CONFIG&sid=67fc2483c0a3c659cdf9f225d2a25661Ya he conseguido abrir la página dónde se identifica el usuario. Activo un oTimer que cada 3 segundos comprueba que se haya identificado correctamente y el servidor haya redirigido a la Url con el "?code=".
El ActiveX tiene la propiedad "Document" y ese objeto a su vez tiene la propiedad "Url" con la que accedo a la dirección del navegador.
Code: Select all | Expand
oDOM := oActiveX:GetProp( "Document" )
cUrl := oDOM:Url
Voy a depurar el código y lo publicaré aquí.
Re: API Bancaria (SOLUCIONADO)
Posted: Wed Apr 21, 2021 3:37 pm
by gmart1
Buenas tardes, dejo aquí la versión definitiva en forma de clase.
Code: Select all | Expand
#include "Fivewin.ch"
class TApiBBVA
data oWnd && Ventana Padre
data cNomObjOLE && Nombre del objeto OLE para hacer GET y POST
data cAppID && Informaci¢n APP creada en BBVAMarket
data cSecretID && Informaci¢n C¢digo secreto de BBVAMarket
data cURL && URL de BBVA
data cRedirect_Uri && Url a la que se redige la respuesta de ConnectBBVA
data cToken && Access_Token devuelto por oAuth
data cCode && Authorization_Code necesario para solicitar access_token
data cFicTok && Nombre de fichero para almacenar Token
data cFicJSon && Nombre de fichero para almacenar respuesta JSon
data oTimer && Timer para comprobar cada 3 segundos que ha respondido ConnectBBVA
method New (oWnd)
method EXTRACTO () && Devuelve JSon con los extractos de las cuentas del cliente BBVA
method PEDIR_CODE () && Si el usuario a rellenado sus datos correctos en ConnecBBVA, rellena ::cCode
method IniTimer ( oDlg, oActiveX ) && Iniciar ::oTimer
method TENGO_CODE ( oActiveX ) && Comprueba que la cUrl contiene en ::cCode, lo rellena y devuelve true
method PEDIR_TOKEN () && Petici¢n de access_Token usando ::cCode y si hay respuesta devuelve true
method PEDIR_EXTRACTO () && Petici¢n de Extracto usando ::cToken, devuelve Json son los datos
endclass
method New (oWnd) class TApiBBVA
::oWnd := oWnd
::cNomObjOLE := "Microsoft.XMLHTTP" && "WINHTTP.WINHTTPREQUEST.5.1"
::cAppID := '' && Hay que darse de alta en BBVAMarket
::cSecretID := ""
::cURL := "https://connect.bbva.com/token"
::cRedirect_Uri := "http://localhost"
::cToken := ""
::cCode := ""
::cFicTok := '.\TOK_BBV.TXT'
::cFicJSon := '.\EXT_BBV.JSON'
return Self
method EXTRACTO () class TApiBBVA
if ::PEDIR_CODE ()
if ::PEDIR_TOKEN ()
::PEDIR_EXTRACTO ()
endif
endif
return Nil
method PEDIR_CODE () class TApiBBVA
local oDlg, oActiveX, nPos, oTimer, oApi
oApi := Self
define dialog oDlg from 100, 100 to 950,1000 title "Navegar" pixel ;
of ::oWnd
activate dialog oDlg on init ;
( oActiveX := ShowPageWeb( oDlg, ;
"https://connect.bbva.com/sandboxconnect?scope=" + ;
"&client_id=" + oApi:cAppID + ;
"&redirect_uri=" + oApi:cRedirect_Uri + ;
"&response_type=code", 0, 0, 825, 950 ) , ;
oApi:IniTimer (oDlg, oActiveX ) )
release ::oTimer
return oDlg:nResult = ID_OK
method IniTimer ( oDlg, oActiveX ) class TApiBBVA
define Timer ::oTimer of oDlg interval 3000 ;
action IF ( ::TENGO_CODE (oActiveX), oDlg:End (ID_OK), )
activate Timer ::oTimer
return Nil
method TENGO_CODE ( oActiveX ) class TApiBBVA
local oDOM := oActiveX:GetProp( "Document" ), ; // -> Object Document
Retorno := .f., nPos
local cUrl := oDOM:Url
if (nPos := AT ( "?code=", cUrl )) # 0
::cCode := SUBSTR ( cUrl, nPos + 6 )
Retorno := .t.
endif
return Retorno
Function ShowPageWeb( oParent, cUrl, nT, nL, nH, nW )
local oActiveX, oHttp, oActiveXdo
oActiveX := TActiveX():New( oParent, "Shell.Explorer.2", nT, nL, nW, nH )
oActivex:Silent := .T. // aqui silence... en el script.
oActiveX:Do( "Navigate2", cUrl )
oActivex:SetFocus()
WHILE oActivex:Busy
SysWait(.2)
ENDDO
oActiveXdo := oActiveX:Document()
oHttp := oActiveX:GetProp( "Document" ) // -> Object Document
if Empty( oHttp )
MsgInfo( "Problemas en el Site...", ;
"Problemas en el Site..." )
RETURN( .F. )
endif
Return oActiveX
method PEDIR_TOKEN () class TApiBBVA
local oHttp, cCadena, aRespuesta
try
oHttp := CreateObject (::cNomObjOLE)
catch
? 'Error de creacion oHttp'
return .t.
end
oHttp:Open ( "POST" , ::cUrl + "?grant_type=authorization_code" + ;
"&code=" + ALLTRIM (::cCode) + ;
"&redirect_uri=" + ::cRedirect_Uri, .f. )
oHttp:SetRequestHeader ( "Authorization", ;
"Basic " + cMimeEnc(::cAppID + ':' + ::cSecretID) )
try
oHttp:Send ()
catch
? 'Error en Send Token'
return .t.
end
cCadena := oHttp:responseText
* HB_MemoWrit ( ::cFicTok, cCadena, .f. )
aRespuesta := hb_jsonDecode (cCadena)
if hb_HHaskey ( aRespuesta, 'access_token' )
::cToken := aRespuesta ['access_token']
endif
oHttp := Nil
return ! EMPTY ( ::cToken )
method PEDIR_EXTRACTO () class TApiBBVA
local oHttp, cUrl, cCadena
cURL := "https://apis.bbva.com/business-accounts-sbx/v1/statements/aeb43"
try
oHttp := CreateObject (::cNomObjOLE)
catch
? 'Error de creacion oHttp'
return .t.
end
oHttp:Open ( "GET" , cUrl, .f. )
oHttp:SetRequestHeader ( "Content-Type", "application/json")
oHttp:SetRequestHeader ( "Accept", "application/json")
oHttp:SetRequestHeader ( "Authorization", "jwt " + ::cToken )
oHttp:SetRequestHeader ( "Host", "apis.bbva.com" )
try
oHttp:Send ()
catch
? 'Error en Send'
return .t.
end
cCadena := oHttp:responseText
HB_MemoWrit ( ::cFicJSon, cCadena, .f. )
oHttp := Nil
return Nil
La clase se usaría así :
Code: Select all | Expand
oApiBBVA := TApiBBVA():new () && Pasar como parámetro la ventana padre
oApiBBVA:EXTRACTO ()
La pantalla de identificación de ConnectBBVA con los datos de usuario de pruebas (contraseña:123456)

El resultado final es el fichero EXT_BBV.JSON con el siguiente contenido :
{"result":{"code":200,"info":"OK"},"data":{"AEB43file":"110182241902008000282104202104201000019193174009783CARLOS BANANERO RUIZ \n 2201822419210420210420028972000000000551180000000000ABONO COMP. TRU 30/1/2017 \n 2201822419210420210420021731000000000003500000000000LIQUID.ABONO COM \n 3301822419020080002800010000000025535350001000000002553535100001919317400978 \n 88999999999999999999000035 "}}
Re: API Bancaria (SOLUCIONADO)
Posted: Wed Apr 21, 2021 4:03 pm
by Antonio Linares
Excelente!
muchas gracias