QuickBooks
QuickBooks Online (Intuit) — API Accounting v3, OAuth 2.0
La diferencia entre 60 horas de trabajo y 8 semanas de calendario no es pereza de nadie: es la revisión de Intuit. Según la documentación de Intuit, la revisión técnica lleva en promedio unos 20 días desde que se inicia, y ese plazo depende de su agenda, de cuántos problemas encuentren y de qué tan rápido los corrijas. Si te rechazan el cuestionario de evaluación, el reloj arranca de nuevo. Vos podés tener todo funcionando perfecto en sandbox en dos semanas y seguir esperando un mes más para tocar datos reales. Dos consejos que ahorran semanas: primero, mandá el cuestionario de evaluación apenas tengas el conector andando en sandbox, no al final. Es la tarea de mayor demora y puede correr en paralelo mientras terminás lo demás. Segundo, no le prometas al dueño una fecha de salida hasta que Intuit apruebe. Prometé una fecha para "listo y probado en sandbox", que esa sí depende de vos. Y sumale, aparte de todo esto, entre 1 y 3 horas por mes de mantenimiento permanente: revisar que la renovación de tokens no se haya caído, renovar el certificado TLS, y conciliar los cobros que hayan quedado colgados.
Quien lo hace
El dueño solo NO puede. Hace falta el socio implementador, sí o sí.
Repartilo así:
EL DUEÑO (o quien tenga el rol de administrador en QuickBooks): crea la cuenta de desarrollador de Intuit a nombre del comercio, hace el clic de autorización que conecta la empresa (nadie más puede hacerlo por él), y aporta las URLs de política de privacidad y de contrato de usuario final que Intuit exige para las llaves de producción.
EL CONTADOR: define UNA sola cosa, pero es la más importante de todas: contra qué cuenta contable se deposita el cobro y si el pago entra a "Undeposited Funds" o directo al banco. Si esto se decide mal, cada cobro le genera trabajo manual de por vida. El contador NO toca el portal de desarrollador.
EL IMPLEMENTADOR / PROGRAMADOR: todo el resto. Y es la mayor parte. QuickBooks Online no tiene ninguna forma de recibir un webhook de ROKI ni de registrar un pago solo. Hay que construir un servicio conector, hospedarlo, mantenerlo con HTTPS público, y que renueve tokens todos los días para siempre. Esto no es una configuración: es un software que alguien tiene que operar indefinidamente.
Dicho sin vueltas: si no hay alguien que pueda mantener un servidor andando, esta integración no se sostiene. No la empieces.
Que tiene que existir antes de empezar
Donde: Ícono de engranaje (arriba a la derecha) > "Account and settings" (en interfaz en español, "Cuenta y configuración") > pestaña "Billing & subscription" / "Facturación y suscripción"
Sin suscripción viva no hay compañía y por lo tanto no hay realmId, que es el identificador con el que la API sabe a qué empresa escribir. Además necesitás ver la edición regional: QuickBooks Online no se comercializa oficialmente en Honduras, y la moneda base de la compañía se fija al crearla y NO se puede cambiar después. Averiguá esto ANTES de prometer nada, no después.
Donde: Ícono de engranaje > "Manage users" / "Administrar usuarios"
Solo un administrador puede autorizar una aplicación de terceros. Un usuario con rol limitado va a ver la pantalla de conexión y va a fallar sin un mensaje claro de por qué. No pierdas media hora con eso.
Donde: Ícono de engranaje > "Account and settings" > "Sales" / "Ventas" > sección "Sales form content" > "Custom transaction numbers", en ON
El external_reference de ROKI ("FAC-001") tiene que coincidir con el DocNumber de la factura en QuickBooks. Si QuickBooks numera solo y vos no controlás ese campo, no hay forma de casar el cobro con la factura y toda la integración pierde sentido.
Donde: Ícono de engranaje > "Chart of accounts" / "Plan de cuentas"
En el objeto Payment esto es DepositToAccountRef. Si no lo mandás, QuickBooks deposita el cobro en "Undeposited Funds" ("Fondos no depositados") y alguien tiene que armar el depósito a mano, cobro por cobro. Que el contador elija y lo deje por escrito antes de programar.
Donde: Menú lateral "Sales" / "Ventas" > "Customers" / "Clientes"
El objeto Payment exige CustomerRef. No podés registrar un pago contra un cliente que no existe. Si vendés a consumidor final ocasional, definí un cliente genérico y usá siempre ese.
Donde: Ícono de engranaje > "Account and settings" > "Advanced" / "Avanzado" > sección "Currency" / "Moneda" > "Multicurrency"
ROKI manda montos en lempiras en unidades decimales. Si la compañía tiene otra moneda base, necesitás CurrencyRef y ExchangeRate en cada Payment. Ojo: activar multimoneda en QuickBooks Online es IRREVERSIBLE. No lo prendas para probar.
Donde: https://developer.intuit.com > "Sign up" / "Sign in"
Es la cuenta dueña de la aplicación y de las llaves de producción. Si la crea el consultor con su correo personal, el día que se va el comercio pierde el control de su propia integración. Usá algo tipo sistemas@tucomercio.com con la contraseña en poder del dueño.
Donde: Fuera de QuickBooks: es infraestructura del comercio o del implementador
Es el requisito que la gente descubre tarde y duele. Necesitás: puerto 443/TCP abierto de entrada desde internet, certificado TLS válido emitido por una autoridad certificadora pública (autofirmado NO sirve, ni para Intuit ni como destino del webhook de ROKI), un nombre de dominio propio, y salida a internet por 443 hacia quickbooks.api.intuit.com, oauth.platform.intuit.com y aura.roki.systems. El almacén donde guardás los tokens debe ser cifrado y con permisos de archivo restringidos al usuario del servicio (nada de 0644 ni de dejarlos en un repositorio).
Los pasos
- 1. Crear la cuenta de desarrollador y la aplicación en el portal de Intuit
Entrá a https://developer.intuit.com y registrate. Después creás la app desde el panel de desarrollador y elegís la plataforma "QuickBooks Online and Payments". Advertencia honesta: Intuit rediseñó y renombró este portal varias veces en los últimos años, y sus páginas de documentación se arman con JavaScript, así que no siempre se puede verificar el rótulo exacto de cada botón. Los nombres que sí aparecen en la documentación y en el soporte oficial de Intuit son "My Hub", la sección "Keys & OAuth" bajo "Development" y bajo "Production" en la barra lateral, y la pantalla "Keys and credentials". Si en tu pantalla el botón se llama distinto, buscá la sección que muestre "Client ID" y "Client Secret": esa es. De entrada la app queda en modo desarrollo, con llaves de desarrollo. Eso es lo normal y con eso vas a trabajar las primeras semanas. - 2. Pedir el alcance com.intuit.quickbooks.accounting y entender que no hay uno más chico
Al configurar la app tenés que habilitar el origen de datos de contabilidad (Accounting). Ese es el alcance com.intuit.quickbooks.accounting, y es el único que necesitás para esta integración: es el que permite leer facturas y crear el objeto Payment. Acá va algo que tenés que decirle al dueño con todas las letras: ese alcance NO se puede achicar. Según la lista de alcances de Intuit, no existe un permiso de solo lectura, ni uno limitado a facturas, ni uno limitado a pagos. com.intuit.quickbooks.accounting da acceso de lectura y escritura a la contabilidad completa de la empresa: clientes, proveedores, asientos, cuentas, todo. El principio de "permiso mínimo" acá no se puede aplicar del lado de Intuit, porque Intuit no lo ofrece. El único control real es quién tiene el Client Secret y los tokens en tu servidor. El alcance com.intuit.quickbooks.payment es otro producto (la pasarela de cobro de Intuit) y no lo necesitás: quien cobra es ROKI. - 3. Cargar la Redirect URI
En la sección "Keys and credentials" de tu app, en "Redirect URIs", tocás "Add URI", pegás la dirección de tu conector y guardás con "Save". Si no guardás, no queda: es un error clásico. Ejemplo: https://conector.tucomercio.com/qbo/callback Tres reglas que Intuit no perdona: - Tiene que ser HTTPS. localhost con http no lo acepta en producción. - Tiene que coincidir carácter por carácter con la que mandás en la petición de autorización. Una barra de más al final y te tira "redirect_uri query parameter value is invalid". - Development y Production tienen listas SEPARADAS de Redirect URIs. Cargar la de desarrollo no te sirve para producción. Podés tener hasta 25 por app. - 4. Crear la compañía sandbox y probar ahí primero
Desde tu cuenta de desarrollador entrás a "My Hub" > "Sandboxes" y tocás "Add" (o "Add a sandbox company"), eligiendo por ejemplo QuickBooks Online Plus. La sandbox es una empresa de QuickBooks falsa, con datos de juguete, para que rompas cosas sin miedo. Ahí probás con las llaves de desarrollo y contra la URL base de sandbox. Direcciones base (confirmadas en la documentación de Intuit): - Sandbox: https://sandbox-quickbooks.api.intuit.com - Producción: https://quickbooks.api.intuit.com La diferencia entre sandbox y producción es exactamente eso: distinta URL base, distinto par de Client ID/Client Secret, distinta lista de Redirect URIs, y datos que no son reales. El flujo de OAuth es idéntico y usa los MISMOS endpoints de autorización y token en los dos casos. Lo que la sandbox NO te prueba: la sandbox es una compañía estadounidense en dólares. Si tu empresa real opera en otra moneda, todo el camino de conversión sigue sin probar. Tenelo presente. - 5. El flujo de OAuth 2.0: el clic del dueño y de dónde sale el realmId
Esta es la parte central y la que más se malentiende. Nadie escribe un usuario y contraseña de QuickBooks en tu sistema. Lo que pasa es esto: PASO A — tu conector manda al administrador de QuickBooks a esta dirección (endpoint confirmado en el documento de descubrimiento OpenID de Intuit, https://developer.api.intuit.com/.well-known/openid_configuration): https://appcenter.intuit.com/connect/oauth2?client_id=TU_CLIENT_ID&response_type=code&scope=com.intuit.quickbooks.accounting&redirect_uri=https://conector.tucomercio.com/qbo/callback&state=un_valor_aleatorio PASO B — el dueño ve una pantalla de Intuit, elige la empresa y aprieta el botón de conectar. Ese es el único momento en que interviene una persona. PASO C — Intuit lo devuelve a tu Redirect URI con tres cosas en la dirección: ?code=AB11...&state=un_valor_aleatorio&realmId=9130350000000000 Ese realmId es el identificador de la empresa. Es lo que hace que todo funcione y lo tenés que guardar junto con los tokens. Va dentro de cada llamada a la API: /v3/company/{realmId}/... . No es un dato secreto, pero sin él no podés escribir en ninguna parte. Y es POR EMPRESA: si el mismo consultor conecta cinco comercios, son cinco realmId con cinco juegos de tokens. No lo dejes fijo en el código. PASO D — tu conector canjea ese code por tokens contra el endpoint de token (también confirmado en el documento de descubrimiento): POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer Authorization: Basic <client_id:client_secret en base64> Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=AB11...&redirect_uri=https://conector.tucomercio.com/qbo/callback Y recibís access_token, refresh_token y sus vencimientos. Para revocar la conexión el endpoint es https://developer.api.intuit.com/v2/oauth2/tokens/revoke. - 6. Guardar y renovar los tokens: acá se rompen la mayoría de las integraciones
Leé esto dos veces, porque es la causa número uno de que una integración con QuickBooks "deje de andar sola" a los pocos meses. - El access token dura 1 hora. Nada más. - El refresh token dura hasta 100 días. - PERO el refresh token ROTA: cada vez que renovás, Intuit puede devolverte un refresh token nuevo, y en la práctica lo hace alrededor de una vez por día. Cuando devuelve uno nuevo, el anterior queda muerto. Regla operativa: guardá SIEMPRE el último refresh token que te devolvieron, pisando el anterior, en la misma transacción. Si tu sistema se queda con uno viejo, la próxima renovación te da invalid_grant, la conexión se cae, y la única salida es que el dueño vuelva a hacer el clic de autorización a mano. Y el otro escenario feo: si el comercio se toma vacaciones y el conector no llama a la API durante más de 100 días, el refresh token vence y la conexión se cae igual. Un conector sano renueva por su cuenta aunque no haya ventas. Agregale al conector una alerta: si falla la renovación, que le avise a alguien por correo o WhatsApp el mismo día. Si no, se enteran cuando ya hay treinta cobros sin registrar. - 7. Crear el cobro en ROKI y mandarle el enlace al cliente
Cuando en QuickBooks se emite una factura que se va a cobrar por ROKI, tu conector llama a ROKI: POST https://aura.roki.systems/api/connect/v1/payments Authorization: Bearer sk_live_... (usá sk_test_... mientras probás contra la sandbox) Content-Type: application/json { "amount": 1500.00, "external_reference": "FAC-001", "name": "Factura 001" } El amount va en unidades decimales, en lempiras. NUNCA en centavos: 1500.00 son mil quinientos lempiras, no quince lempiras. Esto conviene porque coincide con cómo QuickBooks maneja TotalAmt, que también es decimal. No hay conversión de centavos en ningún lado de esta integración, y está bien que así sea. El external_reference tiene que ser el DocNumber exacto de la factura en QuickBooks. Es el hilo que va a unir las dos puntas más adelante. ROKI responde 201 con { id, checkout_url, transaction_id: null }. Guardá el id de ROKI junto al Id interno de la factura de QuickBooks en una tabla tuya: la vas a necesitar. El checkout_url se lo mandás al cliente por correo o por WhatsApp, o lo ponés como enlace de pago. Ese enlace es lo que el cliente abre para pagar. - 8. Recibir el webhook de ROKI en el conector (no en QuickBooks)
Cuando el cliente paga, ROKI manda un aviso firmado a una URL pública HTTPS. Esa URL es la de TU conector, por ejemplo https://conector.tucomercio.com/roki/webhook. QuickBooks no participa de este paso: mirá el punto sobre webhooks más abajo. Validá la firma antes de creerle a nada. En la cabecera ROKI-Signature viene t=<unix>,v1=<hmac_sha256_hex>. Tomás el timestamp, le pegás un punto, le pegás el CUERPO CRUDO tal cual llegó (sin parsearlo, sin reordenarlo, sin reformatearlo), calculás HMAC-SHA256 con tu secreto y comparás. Compará con una función de tiempo constante, no con un == común. Y rechazá lo que tenga un timestamp de hace más de unos minutos, para que nadie te reenvíe un aviso viejo. Si el evento es payment.approved, el pago se acreditó y recién ahí seguís. Respondé rápido con 200 y hacé el trabajo pesado en segundo plano. Un webhook que tarda se reintenta, y un reintento mal manejado te duplica el cobro en la contabilidad. - 9. Encontrar el Id interno de la factura (el paso que casi todos se saltean)
Acá está la trampa técnica más costosa de toda la integración, y conviene entenderla aunque no programes. En QuickBooks, una factura tiene DOS números: - el DocNumber, que es "FAC-001", el número que ve el cliente y el que vos elegiste; - el Id, que es un número interno de QuickBooks tipo "249", que vos no elegís y que el cliente nunca ve. Para registrar el pago, QuickBooks exige el Id interno. No acepta el DocNumber. Y ROKI te devuelve el external_reference, que es el DocNumber. Entonces hay un paso de traducción obligatorio: GET https://quickbooks.api.intuit.com/v3/company/{realmId}/query?query=select Id, Balance, TotalAmt, CustomerRef from Invoice where DocNumber = 'FAC-001' Authorization: Bearer <access_token> Accept: application/json De ahí sacás el Id, el CustomerRef y el Balance pendiente. Atención con esto: el intérprete de consultas de QuickBooks a veces confunde valores con guiones y los toma como fechas. Un DocNumber tipo "FAC-001" está justo en la zona de riesgo. Probalo con tu formato real de numeración antes de dar la integración por lista. Si te da problemas, cambiá el formato de numeración (por ejemplo sin guiones) mientras estás a tiempo. Alternativa más segura si podés: guardá el Id interno de QuickBooks en tu propia tabla cuando creás el cobro en ROKI (paso 7), y así en este paso no consultás nada, solo leés tu tabla. Es más rápido y no depende del parser de Intuit. - 10. Registrar el pago contra la factura: el objeto Payment con Line y LinkedTxn
Ahora sí, el registro contable. En QuickBooks un pago es un objeto Payment que tiene un total y una lista de líneas, y cada línea dice a qué documento se aplica mediante LinkedTxn. Esa es toda la lógica: el LinkedTxn es la soga que ata el pago a la factura. POST https://quickbooks.api.intuit.com/v3/company/{realmId}/payment?minorversion=<fijá una versión y dejala fija> Authorization: Bearer <access_token> Content-Type: application/json Accept: application/json { "CustomerRef": { "value": "69" }, "TotalAmt": 1500.00, "TxnDate": "2026-08-16", "PaymentRefNum": "ROKI-9f3a2", "PrivateNote": "Cobro ROKI Connect, referencia FAC-001", "DepositToAccountRef": { "value": "35" }, "Line": [ { "Amount": 1500.00, "LinkedTxn": [ { "TxnId": "249", "TxnType": "Invoice" } ] } ] } Campo por campo, en castellano: - CustomerRef: el cliente. Obligatorio, y tiene que ser el MISMO cliente de la factura. - TotalAmt: el total cobrado, decimal. Los 1500.00 que vinieron de ROKI. - TxnDate: la fecha con la que entra el asiento. Usá la fecha de acreditación, no la de emisión. - PaymentRefNum: poné acá el identificador del pago de ROKI. Es tu rastro de auditoría cuando el contador pregunte de dónde salió esa plata. - PrivateNote: nota interna. Dejá escrito el external_reference. Cuesta nada y salva horas. - DepositToAccountRef: la cuenta que definió el contador. Si lo omitís, va a "Undeposited Funds" y alguien tiene que depositarlo a mano. - Line[].Amount: cuánto de este pago se aplica a ESA factura. - Line[].LinkedTxn[].TxnId: el Id INTERNO de la factura (el "249" del paso anterior, no "FAC-001"). - Line[].LinkedTxn[].TxnType: la palabra "Invoice". Si un solo pago cancela varias facturas, agregás más elementos al arreglo Line, uno por factura, y la suma de los Amount tiene que dar TotalAmt. Antes de mandar esto, verificá que no exista ya un Payment con ese mismo PaymentRefNum. Si el webhook llega dos veces y vos creás dos pagos, la factura queda pagada al doble y le dejás un problema al contador. - 11. Confirmar que la factura quedó saldada
No des por bueno un 200. Volvé a consultar la factura y mirá el campo Balance: GET https://quickbooks.api.intuit.com/v3/company/{realmId}/invoice/249 Si Balance quedó en 0, la factura está saldada. Si quedó en algo distinto de 0, hubo un pago parcial o algo salió mal, y eso tiene que quedar registrado en tu bitácora y, si podés, generar un aviso. La factura también te va a mostrar un arreglo LinkedTxn apuntando al Payment: es el mismo vínculo visto desde el otro lado. Sirve para auditar. Guardá en tu bitácora, para cada operación: el id de ROKI, el external_reference, el realmId, el Id de la factura, el Id del Payment creado, y la fecha. El día que haya una discusión de plata, esa tabla es toda tu defensa. - 12. El respaldo cuando el webhook no llega
Los webhooks se pierden. Se cae internet, se reinicia el servidor, vence un certificado. Asumilo desde el diseño, no como parche. Dejá una tarea programada que corra cada 15 o 30 minutos, tome todos los cobros que vos creaste en ROKI y que siguen sin registrar en QuickBooks, y consulte: GET https://aura.roki.systems/api/connect/v1/payments/{id} Authorization: Bearer sk_live_... Si el estado es aprobado y en QuickBooks no hay Payment, lo registrás con el mismo procedimiento del paso 10. La verificación de "¿ya existe este pago?" es lo que evita que el webhook y esta tarea creen el pago dos veces. Esto no es opcional. Sin este respaldo, tarde o temprano hay un cliente que pagó y una factura que en la contabilidad figura impaga. - 13. Pasar a producción: llaves reales y la revisión de Intuit
Cuando funciona todo en sandbox, pedís las llaves de producción en la sección "Production" > "Keys & OAuth" de tu app. Intuit no te las entrega por pedirlas. Antes exige, como mínimo: - Completar el perfil de la app. - Una URL de política de privacidad y una URL de contrato de usuario final (End User License Agreement). Tienen que ser direcciones que carguen de verdad. Si la app es solo para uso interno del comercio, el soporte de Intuit ha indicado que alcanza con una URL de reemplazo, pero si la vas a ofrecer a terceros necesitás documentos reales. - Cargar la Redirect URI de producción, que es una lista aparte de la de desarrollo. - Completar el cuestionario de evaluación de la app (app assessment questionnaire). Esto es obligatorio para TODA app que toque datos de producción, incluidas las privadas que nunca se publican en el App Store. Negarse a completarlo puede terminar en que Intuit te corte el acceso a producción. Qué te pregunta ese cuestionario, para que no te agarre desprevenido: dónde está hospedada la aplicación, cada cuánto hacés análisis de vulnerabilidades, si usás un sistema de monitoreo de seguridad (SIEM), cómo está la seguridad de red, si exigís autenticación de múltiples factores, cómo ciframos los datos en tránsito y en reposo, cómo manejás autenticación y autorización, y si tenés certificación ISO 27001 o auditoría SOC 2. Acá va la parte incómoda, y prefiero decírtela ahora: un consultor chico con un VPS compartido no tiene buenas respuestas para la mitad de esas preguntas. No es imposible pasar, pero no es un trámite. Preparalo con tiempo. Y los tiempos: según la documentación de Intuit, la revisión técnica lleva en promedio unos 20 días desde que se inicia, y ese plazo depende de la agenda de revisiones, de cuántos problemas encuentren y de qué tan rápido los corrijas. Si te rechazan, el reloj vuelve a empezar. Intuit además revisa las apps una vez por año, o más seguido si se le antoja: esto no es un permiso que se saca una vez y listo. No le pongas fecha de salida al dueño hasta que Intuit apruebe.
Como llega el webhook
QuickBooks Online NO puede recibir el webhook de ROKI. Ni con desarrollo a medida, ni pagando más. No existe.
Es importante entender por qué, porque la palabra "webhook" aparece en la documentación de Intuit y confunde a todo el mundo:
QuickBooks Online SÍ tiene webhooks, pero van en la dirección contraria. Son avisos que Intuit MANDA HACIA AFUERA, hacia una URL tuya, cuando algo cambia dentro de QuickBooks (se creó una factura, se modificó un cliente). Son de salida. QuickBooks no expone ninguna dirección donde un tercero como ROKI pueda golpear la puerta y decir "cobré 1500 lempiras". Como sistema en la nube y cerrado, QuickBooks solo acepta que le escriban por su API autenticada con OAuth, nunca por un aviso anónimo entrante.
Entonces, la alternativa, que además es la única arquitectura posible:
EL CONECTOR EN EL MEDIO. Hace falta un servicio propio, hospedado por el comercio o por el implementador, con una dirección pública HTTPS. Ese servicio:
- Recibe el webhook de ROKI en, por ejemplo, https://conector.tucomercio.com/roki/webhook
- Valida la firma ROKI-Signature (HMAC-SHA256 sobre timestamp + "." + cuerpo crudo)
- Responde 200 enseguida
- Traduce el external_reference "FAC-001" al Id interno de la factura en QuickBooks
- Llama a la API de QuickBooks con el access token vigente y crea el objeto Payment con su Line/LinkedTxn
- Registra todo en su bitácora
Lo que ese conector necesita, concreto: puerto 443/TCP abierto de entrada desde internet, certificado TLS emitido por una autoridad certificadora pública (autofirmado no sirve: ROKI no le va a entregar el aviso), un dominio propio, y salida por 443 hacia quickbooks.api.intuit.com y oauth.platform.intuit.com. Nada de puerto 80, nada de IP pelada, nada de túneles caseros tipo ngrok para producción.
Un detalle que juega a favor: como QuickBooks Online es un servicio en la nube, tu conector puede vivir en cualquier parte con internet. No tiene que estar en la oficina del comercio, no hay que abrir puertos en el router del negocio ni exponer la red interna. Podés poner el conector en un VPS y listo. Esta es la única ventaja real que QuickBooks Online tiene sobre un ERP instalado en la oficina, y no es poca.
Ahora, lo que nadie te dice: ese conector es software que hay que mantener andando para siempre. Renueva tokens todos los días, vence el certificado cada 90 días, se cae el VPS. Si nadie lo cuida, la integración muere en silencio y el comercio se entera cuando el contador cierra el mes. Definí quién lo opera ANTES de arrancar, y ponelo por escrito.
¿Y los webhooks propios de QuickBooks? Para esta integración no los necesitás. Solo servirían si quisieras que ROKI se entere cuando alguien emite una factura nueva en QuickBooks, para generar el cobro automáticamente. Es una mejora posterior, no parte de la puesta en marcha. La sección donde se configuran está dentro de la app en el portal de desarrollador, pero no pude confirmar el rótulo exacto de esa pestaña en la documentación oficial, así que no te lo voy a inventar.
Lo que se rompe
- El LinkedTxn.TxnId es el Id INTERNO de la factura ("249"), no el número que ve el cliente ("FAC-001"). El external_reference de ROKI es el DocNumber. Hay que traducir sí o sí con una consulta previa, o guardando el Id interno en tu propia tabla desde el momento en que creás el cobro. Si mandás el DocNumber donde va el Id, el pago falla o, peor, se aplica a otra factura.
- El intérprete de consultas de QuickBooks a veces confunde valores con guiones y los toma como fechas. Un DocNumber tipo "FAC-001" está en zona de riesgo. Probá tu formato real de numeración temprano; si falla, cambiá el formato mientras estás a tiempo, no después de emitir mil facturas.
- El refresh token rota casi todos los días. Cada renovación puede devolverte uno nuevo y mata al anterior. Si tu sistema guarda el viejo, la conexión se cae con invalid_grant y el dueño tiene que volver a autorizar a mano. Guardá siempre el último, en la misma transacción, sin excepciones.
- Si el conector no llama a la API durante más de 100 días, el refresh token vence igual y la conexión muere. Un comercio estacional que cierra tres meses vuelve a un sistema desconectado. Renová aunque no haya movimiento.
- El access token dura 1 hora. Si tu código lo cachea sin mirar el vencimiento, todo anda bien la primera hora de cada despliegue y después empieza a fallar de a ratos. Es un error que se diagnostica tardísimo porque parece intermitente.
- Si omitís DepositToAccountRef, el cobro cae en "Undeposited Funds" y alguien tiene que armar el depósito a mano, uno por uno, para siempre. Que el contador decida esto ANTES de programar, no cuando ya hay 200 pagos apilados.
- La moneda base de una compañía de QuickBooks Online se fija al crearla y no se puede cambiar nunca. Y activar multimoneda es IRREVERSIBLE. Si la empresa no está en lempiras, cada Payment necesita CurrencyRef y ExchangeRate. Verificá la edición regional y la moneda de la compañía ANTES de prometer plazos: QuickBooks Online no se comercializa oficialmente en Honduras y esto puede tumbar el proyecto entero en la primera semana.
- La sandbox es una empresa estadounidense en dólares. Que todo funcione en sandbox no prueba que el camino de moneda funcione en la empresa real. Reservá presupuesto para probar eso aparte.
- El alcance com.intuit.quickbooks.accounting es todo o nada: da lectura y escritura sobre la contabilidad completa. No hay permiso de solo lectura ni limitado a facturas. Decíselo al dueño en la reunión de arranque, no cuando aparezca en el contrato. El único control real sobre ese acceso es quién custodia el Client Secret y los tokens.
- El realmId es por empresa. Un implementador con varios clientes maneja varios realmId con varios juegos de tokens. Si lo dejás fijo en el código, el segundo cliente te escribe la contabilidad del primero. Suena absurdo hasta que pasa.
- Si el webhook llega dos veces (y va a llegar) y no controlás duplicados, creás dos Payment y la factura queda sobrepagada. Verificá siempre si ya existe un pago con ese PaymentRefNum antes de crear otro. El mismo cuidado va para la tarea de respaldo, que puede pisarse con el webhook.
- El cuestionario de evaluación de la app es obligatorio para tocar datos de producción, incluso si la app es privada y nunca se publica. Pregunta por hospedaje, cifrado en tránsito y en reposo, autenticación de múltiples factores, análisis de vulnerabilidades, SIEM, y certificación ISO 27001 o SOC 2. Un consultor chico con un VPS compartido no tiene buenas respuestas para la mitad. Preparalo con semanas de anticipación.
- Las Redirect URIs de Development y de Production son listas SEPARADAS. Cargarla en desarrollo no la habilita en producción, y el error que devuelve Intuit no es claro. Además debe coincidir carácter por carácter: una barra de más al final y no entra.
- Fijá el parámetro minorversion en las llamadas a la API. Si no lo fijás, Intuit puede mover el comportamiento por defecto y tu integración cambia sola un martes cualquiera sin que nadie haya tocado nada.
- Intuit aplica límites de tasa por compañía y por app. No verifiqué el número vigente, así que no te lo voy a inventar: consultalo en la documentación antes de diseñar procesos que disparen muchas llamadas seguidas, y programá reintentos con espera creciente ante un 429.
- Intuit revisa las apps aprobadas una vez por año, o más seguido si quiere. La aprobación no es permanente. Alguien tiene que hacerse cargo de responder esas revisiones o el comercio pierde el acceso a producción sin aviso útil.
- La documentación de developer.intuit.com se arma con JavaScript y varias páginas no se pueden leer con herramientas automáticas, además de que el portal fue rediseñado y renombrado varias veces. Por eso algunos rótulos exactos de botones pueden diferir de lo que ves en pantalla. Cuando eso pase, guiate por la función (buscá la pantalla que muestre "Client ID" y "Client Secret") y no por el nombre literal.
Lo que no pudimos confirmar
Cada nombre de menu y cada puerto de esta pagina se verifico contra la documentacion oficial de la plataforma. Estos 7 puntos no se pudieron confirmar, y se dejan senalados en vez de presentarlos como ciertos:
- Ruta "Ícono de engranaje > Account and settings > pestaña Billing & subscription" para ver la suscripción/edición regional - nombre-distinto. La pestaña "Billing & Subscription" existe, pero YA NO cuelga de "Account and settings". La ruta oficial actual es: Settings (engranaje) > "Subscriptions and billing" > pestaña "Billing & Subscription
- Ruta "Menú lateral Sales / Ventas > Customers / Clientes" para dar de alta el Customer - nombre-distinto. La documentación oficial actual (Intuit Platform / nuevo menú) documenta: "All apps" > "Customer Hub" > "Customers & leads" > "New customer". El par "Sales > Customers" corresponde al menú anterior y
- "La sandbox es una compañía estadounidense en dólares" (paso 4, sección de lo que la sandbox NO prueba) - no-existe. Esa restricción no existe tal como se enuncia. Al crear una sandbox con "QuickBooks Online Plus" hay un desplegable "Country" y las compañías sandbox son específicas por región ("you can't change this
- Al crear la app se elige la plataforma "QuickBooks Online and Payments" - no-verificable. No pude confirmar ese rótulo exacto en documentación oficial: developer.intuit.com se arma con JavaScript y devuelve contenido truncado. Fuentes de terceros que documentan el portal actual muestran "M
- Pantalla "Keys and credentials" donde se cargan las Redirect URIs - nombre-distinto. Existe pero el rótulo varía según la fuente y la versión del portal: el mensaje de error oficial de Intuit dice "listed in the Redirect URIs section on your app's Keys tab" ("Keys tab"), y otras págin
- Con multimoneda hacen falta CurrencyRef y ExchangeRate en cada Payment - no-verificable. CurrencyRef está confirmado como atributo opcional del Payment cuando la compañía tiene multimoneda activa. ExchangeRate en la entidad Payment concretamente NO lo pude confirmar: la referencia de API
- "QuickBooks Online no se comercializa oficialmente en Honduras" - no-verificable. No pude confirmar ni desmentir el caso de Honduras con una lista oficial de países. Lo que sí consta es que Intuit ofrece una versión "QuickBooks Online Global" para países sin edición localizada y qu
Fuentes
- https://developer.api.intuit.com/.well-known/openid_configuration (leído directo, JSON crudo: confirma authori
- https://developer.api.intuit.com/.well-known/openid_sandbox_configuration (leído directo, JSON crudo: confirma
- https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0
- https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/set-redirect-uri
- https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/faq
- https://developer.intuit.com/app/developer/qbo/docs/learn/scopes
- https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/payment
- https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/invoice
- https://developer.intuit.com/app/developer/qbo/docs/workflows/manage-linked-transactions
- https://developer.intuit.com/app/developer/qbo/docs/learn/explore-the-quickbooks-online-api/data-queries
- https://developer.intuit.com/app/developer/qbo/docs/develop/sandboxes
- https://developer.intuit.com/app/developer/qbo/docs/develop/sandboxes/sandbox-faqs
