Page 1 of 1

API REST SUNAT - GUIAS DE REMISION ELECTRONICAS

Posted: Sat Oct 21, 2023 5:04 pm
by edufloriv
Saludos amigos,

Esperando se encuentren todos bien. Estoy tratando de consumir una api de Sunat (entidad reguladora de impuestos acá en Perú). Esta api me debe retornar un token con el que después podré realizar el envío de guías electrónicas.
La documentación de Sunat indica esto para realizar el request:
En la cabecera (Header) se debe enviar el siguiente parámetro:
Content-Type Application/json
El cuerpo (Body) de la consulta deberá ser de tipo “x-www-form-urlencoded” y se debe
enviar los siguientes parámetros:
grant_type : password
scope : https://api-cpe.sunat.gob.pe
client_id : <client_id> generado en menú SOL
client_secret : <client_secret> generado en menú SOL
username : <Número de RUC> + <Usuario SOL>
password : <Contraseña SOL>

Donde:
- El parámetro grant_type tiene valor fijo
También dá un json de ejemplo para realizar pruebas con Postman:
gre-token-2.png
gre-token-2.png (29.46 KiB) Viewed 5477 times
Y esta debería ser la respuesta de la api, debería devolver el token, el tipo de token y el tiempo de vigencia:
gre-token-3.png
gre-token-3.png (185.32 KiB) Viewed 5477 times
Yo tengo así mi código:

Code: Select all

* ------------------------------------------------------------------------
FUNC SunatGRE_GetToken

LOCAL cJson , aJson , cCliDoc , hBody , oRestApi
LOCAL aData := {}

   oRestApi := ApiRestOpen()
   IF ! EMPTY(oRestApi)
//  esta url es para realizar pruebas :
      cURL  := "https://gre-test.nubefact.com/v1/clientessol/test-85e5b0ae-255c-4891-a595-0b98c65c9854/oauth2/token"
      hBody := { => }
      hBody ["grant_type"]    := "password"
      hBody ["scope"]         := "https://api-cpe.sunat.gob.pe"
      hBody ["client_id"]     := "test-85e5b0ae-255c-4891-a595-0b98c65c9854"
      hBody ["client_secret"] := "test-Hty/M6QshYvPgItX2P0+Kw=="
      hBody ["username"]      := "20522209636MODDATOS"
      hBody ["password "]     := "MODDATOS"
      cJson := hb_jsonEncode( hBody , .F. )
      cResp := ApiRestSend( oRestApi , "POST" , "application/json" , cURL , cJson , , "application/x-www-form-urlencoded" )
      hb_jsondecode( cResp , @aData )
      hb_memowrit( 'respuesta_sunat.txt' , cResp )
      hBody := Nil
      IF VALTYPE(aData) = 'H'
         cToken := aData["access_token"]
         cTipo  := aData["token_type"]
         cExpir := aData["expires_in"]
         msginfo("Token: "+cToken)
      ENDIF
   ENDIF
   oRestApi := NIL

RETURN

*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
* FUNCION   : ApiRestOpen
* COMENTARIO:
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------

FUNCTION ApiRestOpen

LOCAL oRestApi := ""
LOCAL nTimeOut := 20  //seconds

//Init
BEGIN SEQUENCE WITH {|o| break(o)}
	oRestApi := Win_OleCreateObject( "MSXML2.ServerXMLHTTP" )
//                          nResolve         nConnect        nSend               nRecieve
	oRestApi:setTimeouts( nTimeout * 1000 , nTimeout * 1000 , nTimeout * 1000 , nTimeout * 1000 )
RECOVER
     MsgStop( "Microsoft XML Core Services (MSXML) 6.0 is not installed."+CRLF+;
          "Download and install MSXML 6.0 from http://msdn.microsoft.com/xml"+CRLF+;
          "before continuing.")
END SEQUENCE

RETURN( oRestApi )


*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
* FUNCION   : ApiRestSend
* COMENTARIO:
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------

FUNCTION ApiRestSend( oRestApi, cMethod, cContentType, cUrl, cBody, cToken, cContentType2 )

LOCAL cReturn

/*
cContentType:
"application/json;charset=utf-8"
"application/x-www-form-urlencoded"
"application/xml"
*/

DO EVENTS

BEGIN SEQUENCE WITH {|o| break(o)}

	set( _SET_CODEPAGE, 'UTF8' )
	oRestApi:Open( cMethod , cUrl , .F. )
	oRestApi:setRequestHeader("Content-Type",cContentType)
   IF VALTYPE(cContentType2) = 'C'
      oRestApi:setRequestHeader("Content-Type",cContentType2)
   ENDIF
   IF VALTYPE(cToken) = 'C'
      oRestApi:setRequestHeader("Authorization" , "Token token=" + cToken)
   ENDIF
   IF VALTYPE(cBody) = 'C'
      oRestApi:Send( cBody )
   ELSE
      oRestApi:Send()
   ENDIF
	set( _SET_CODEPAGE, "ESWIN" )

	IF oRestApi:Status <> 200
		BREAK "HTTP status: " + hb_NToS(oRestApi:status) + " "  + oRestApi:statusText 
	ENDIF

//	cReturn := oRestApi:ResponseBody()
	cReturn := oRestApi:ResponseText()

RECOVER USING oErr

	cReturn := "!ERROR!" + CRLF + HB_TRANSLATE( IF (ValType(oErr) = 'O' , oErr:Description , oErr ) , 'ESWIN' )
 
END SEQUENCE
 	
RETURN cReturn

Pero a mi me retorna lo siguiente (contenido de respuesta_sunat.txt):
gre-token-4.png
gre-token-4.png (9.48 KiB) Viewed 5477 times
Al parecer estoy armando mal el request. Lo que no entiendo es como colocar ese atributo "urlencoded" al body. Agradeceré su gentil ayuda.

Re: API REST SUNAT - GUIAS DE REMISION ELECTRONICAS

Posted: Sun Oct 22, 2023 11:39 am
by edk
Ahí lo tienes, aquí tienes la solución:

Code: Select all

      cURL  := "https://gre-test.nubefact.com/v1/clientessol/test-85e5b0ae-255c-4891-a595-0b98c65c9854/oauth2/token"
      hBody := { => }
      hBody ["grant_type"]    := "password"
      hBody ["scope"]         := "https://api-cpe.sunat.gob.pe"
      hBody ["client_id"]     := "test-85e5b0ae-255c-4891-a595-0b98c65c9854"
      hBody ["client_secret"] := "test-Hty/M6QshYvPgItX2P0+Kw=="
      hBody ["username"]      := "20522209636MODDATOS"
      hBody ["password"]      := "MODDATOS"
      //cJson := hb_jsonEncode( hBody , .F. )
      
      cBody := ""
      hb_HEval ( hBody, { | cKey, cValue | cBody += IF ( !Empty ( cBody ), "&", "" ) + AllTrim ( cKey ) + "=" + tip_URLEncode( cValue ) } )
      //cBody is x-www-form-urlencoded

      cResp := ApiRestSend( oRestApi , "POST" , "application/json" , cURL , cBody , , "application/x-www-form-urlencoded" )
Tenga en cuenta que x-www-form-urlencoded no es un formato json sino un sting que envía parámetros como una extensión de URL.

También tuvo un error en la definición de la clave hBody ["password "] (hay un espacio innecesario). Debería ser hBody ["password"].

Re: API REST SUNAT - GUIAS DE REMISION ELECTRONICAS

Posted: Sun Oct 22, 2023 1:53 pm
by edufloriv
Tocayo, muchas gracias, funciona !!

Así me devuelve la respuesta.
{
"access_token":"test-eyJhbGciOiJIUzUxMiJ9.ImE1NTFkMTI2LTgwYzAtNDRmYS04NjRiLTRiNGQ3Zjk3ZWE2NSI.JEQQGteYhHnepJyS0a_ao3KnzAvBKiL8ARUIZqxE-Ue2p8fBSZ7sg7ywqueFQJdG3yGKl1i83DKXUluRgiBWSw",
"token_type":"JWT",
"expires_in":3600
}
Ya te debo varias cervezas amigo. Un gran abrazo y que tengas excelente día.