useEnvioSRI.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import { useState } from "react"
  2. import { toast } from "sonner"
  3. import type { EstadoEnvio, RespuestaVerificacionSRI } from "@/types/envio-sri"
  4. import { extractInfoFromXml } from "@/lib/xml-utils"
  5. export function useEnvioSRI() {
  6. const [xmlFile, setXmlFile] = useState<File | null>(null)
  7. const [p12File, setP12File] = useState<File | null>(null)
  8. const [password, setPassword] = useState("")
  9. const [signedXml, setSignedXml] = useState<string | null>(null)
  10. const [accessKey, setAccessKey] = useState<string>("")
  11. const [ambiente, setAmbiente] = useState<'1' | '2'>('1') // 1=Pruebas, 2=Producción
  12. const [isLoading, setIsLoading] = useState(false)
  13. const [estadoEnvio, setEstadoEnvio] = useState<EstadoEnvio>("idle")
  14. const [respuestaAutorizacion, setRespuestaAutorizacion] = useState<RespuestaVerificacionSRI | null>(null)
  15. // Paso 1: Firmar el XML (opcional si ya viene firmado)
  16. const handleSign = async () => {
  17. if (!xmlFile || !p12File || !password) {
  18. toast.error("Por favor completa todos los campos para firmar")
  19. return false
  20. }
  21. setIsLoading(true)
  22. setSignedXml(null)
  23. try {
  24. const formData = new FormData()
  25. formData.append("xml", xmlFile)
  26. formData.append("p12", p12File)
  27. formData.append("password", password)
  28. const response = await fetch("/api/sign-invoice", {
  29. method: "POST",
  30. body: formData,
  31. })
  32. const data = await response.json()
  33. if (!response.ok) {
  34. throw new Error(data.details || data.error || "Error al firmar")
  35. }
  36. setSignedXml(data.signedXml)
  37. toast.success("XML firmado exitosamente")
  38. return true
  39. } catch (error) {
  40. console.error(error)
  41. toast.error(error instanceof Error ? error.message : "Error al firmar el XML")
  42. return false
  43. } finally {
  44. setIsLoading(false)
  45. }
  46. }
  47. // Paso 2: Enviar al SRI
  48. const handleEnviarSRI = async () => {
  49. if (!signedXml) {
  50. toast.error("Primero debes firmar el XML o cargar uno firmado")
  51. return false
  52. }
  53. if (!accessKey.trim()) {
  54. toast.error("Debes ingresar la clave de acceso del documento")
  55. return false
  56. }
  57. setIsLoading(true)
  58. setEstadoEnvio("enviando")
  59. try {
  60. const response = await fetch("/api/send-to-sri", {
  61. method: "POST",
  62. headers: {
  63. "Content-Type": "application/json",
  64. },
  65. body: JSON.stringify({ signedXml }),
  66. })
  67. const data = await response.json()
  68. if (!response.ok) {
  69. throw new Error(data.details || data.error || "Error al enviar")
  70. }
  71. toast.success("Documento enviado al SRI, verificando estado...")
  72. setEstadoEnvio("verificando")
  73. // Automáticamente verificar el estado
  74. await handleVerificarEstado()
  75. return true
  76. } catch (error) {
  77. console.error(error)
  78. toast.error(error instanceof Error ? error.message : "Error al enviar al SRI")
  79. setEstadoEnvio("error")
  80. return false
  81. } finally {
  82. setIsLoading(false)
  83. }
  84. }
  85. // Paso 3: Verificar estado
  86. const handleVerificarEstado = async () => {
  87. if (!accessKey.trim()) {
  88. toast.error("Debes ingresar la clave de acceso")
  89. return false
  90. }
  91. setIsLoading(true)
  92. setEstadoEnvio("verificando")
  93. try {
  94. const response = await fetch("/api/poll-sri", {
  95. method: "POST",
  96. headers: {
  97. "Content-Type": "application/json",
  98. },
  99. body: JSON.stringify({ accessKey }),
  100. })
  101. const data: RespuestaVerificacionSRI = await response.json()
  102. if (!response.ok) {
  103. throw new Error(data.details || data.error || "Error al verificar")
  104. }
  105. setRespuestaAutorizacion(data)
  106. setEstadoEnvio(data.estado)
  107. if (data.estado === "autorizado") {
  108. toast.success("¡Documento autorizado por el SRI!")
  109. } else if (data.estado === "devuelto") {
  110. toast.warning("Documento devuelto por el SRI")
  111. } else if (data.estado === "no_autorizado") {
  112. toast.error("Documento no autorizado por el SRI")
  113. }
  114. return true
  115. } catch (error) {
  116. console.error(error)
  117. toast.error(error instanceof Error ? error.message : "Error al verificar estado")
  118. setEstadoEnvio("error")
  119. return false
  120. } finally {
  121. setIsLoading(false)
  122. }
  123. }
  124. // Cargar un XML firmado directamente
  125. const handleLoadSignedXml = async (file: File) => {
  126. try {
  127. const content = await file.text()
  128. // Extraer información del XML automáticamente
  129. const info = extractInfoFromXml(content)
  130. if (!info.claveAcceso) {
  131. toast.error("No se pudo encontrar la clave de acceso en el XML")
  132. return
  133. }
  134. // Validar que la clave de acceso tenga 49 dígitos
  135. if (info.claveAcceso.length !== 49) {
  136. toast.error("La clave de acceso debe tener 49 dígitos")
  137. return
  138. }
  139. setSignedXml(content)
  140. setXmlFile(file)
  141. setAccessKey(info.claveAcceso)
  142. // Establecer el ambiente si se encuentra en el XML
  143. if (info.ambiente) {
  144. setAmbiente(info.ambiente)
  145. }
  146. toast.success(`XML cargado: ${file.name}`)
  147. toast.info(`Clave de acceso extraída: ${info.claveAcceso.substring(0, 10)}...`)
  148. } catch (error) {
  149. toast.error("Error al leer el archivo XML")
  150. }
  151. }
  152. // Descargar XML firmado
  153. const handleDownloadSigned = () => {
  154. if (!signedXml) return
  155. const blob = new Blob([signedXml], { type: "application/xml" })
  156. const url = URL.createObjectURL(blob)
  157. const a = document.createElement("a")
  158. a.href = url
  159. a.download = `factura_firmada_${new Date().getTime()}.xml`
  160. document.body.appendChild(a)
  161. a.click()
  162. document.body.removeChild(a)
  163. URL.revokeObjectURL(url)
  164. toast.success("XML firmado descargado")
  165. }
  166. // Descargar comprobante autorizado
  167. const handleDownloadAutorizado = () => {
  168. if (!respuestaAutorizacion?.comprobante) return
  169. const blob = new Blob([respuestaAutorizacion.comprobante], { type: "application/xml" })
  170. const url = URL.createObjectURL(blob)
  171. const a = document.createElement("a")
  172. a.href = url
  173. a.download = `factura_autorizada_${accessKey}.xml`
  174. document.body.appendChild(a)
  175. a.click()
  176. document.body.removeChild(a)
  177. URL.revokeObjectURL(url)
  178. toast.success("Comprobante autorizado descargado")
  179. }
  180. // Reset
  181. const handleReset = () => {
  182. setXmlFile(null)
  183. setP12File(null)
  184. setPassword("")
  185. setSignedXml(null)
  186. setAccessKey("")
  187. setEstadoEnvio("idle")
  188. setRespuestaAutorizacion(null)
  189. }
  190. return {
  191. // State
  192. xmlFile,
  193. p12File,
  194. password,
  195. signedXml,
  196. accessKey,
  197. ambiente,
  198. isLoading,
  199. estadoEnvio,
  200. respuestaAutorizacion,
  201. // Setters
  202. setXmlFile,
  203. setP12File,
  204. setPassword,
  205. setAccessKey,
  206. // Actions
  207. handleSign,
  208. handleEnviarSRI,
  209. handleVerificarEstado,
  210. handleLoadSignedXml,
  211. handleDownloadSigned,
  212. handleDownloadAutorizado,
  213. handleReset,
  214. }
  215. }