#!/usr/bin/env python3 """Arranque rápido de la API de auditoría de facturas de MuninCloud (v1), con Python y `requests`. pip install requests MC_LLAVE_PROCESAR= python3 inicio_rapido.py factura.pdf compra El segundo argumento dice qué es la factura PARA LA EMPRESA DE LA LLAVE: `compra` si la recibe (es el cliente), `venta` si la emite (es el proveedor). La llave de procesar se crea con la de administrar, con el NIF y el nombre de esa empresa (paso 2 del arranque con curl, o POST /v1/llaves). """ # ── paso 0 · lo que usan los pasos ── import json import os import sys import time import requests API = os.environ.get("MC_API", "https://api.munincloud.com") def error_de(r): """Los errores siempre traen un `codigo` estable (el que debe mirar tu programa) y un `mensaje` para personas.""" try: return r.json()["error"]["codigo"] except (ValueError, KeyError): return f"HTTP {r.status_code}" # ── fin paso 0 ── # ── paso 1 · la sesión, con la llave de procesar en la cabecera ── sesion = requests.Session() sesion.headers["Authorization"] = "Bearer " + os.environ["MC_LLAVE_PROCESAR"] # ── fin paso 1 ── # ── paso 2 · subir la factura, diciendo si es compra o venta ── if len(sys.argv) != 3 or sys.argv[2] not in ("compra", "venta"): sys.exit("uso: python3 inicio_rapido.py factura.pdf compra|venta") ruta, tipo = sys.argv[1], sys.argv[2] with open(ruta, "rb") as fh: r = sesion.post(f"{API}/v1/trabajos", headers={"Idempotency-Key": f"{os.path.basename(ruta)}-{tipo}-{time.strftime('%Y%m%d')}"}, data={"tipo": tipo}, files={"factura": (os.path.basename(ruta), fh)}, timeout=60) if r.status_code != 202: sys.exit(f"no se pudo subir: {error_de(r)}") trabajo = r.json()["trabajo"] print("trabajo:", trabajo) # ── fin paso 2 ── # ── paso 3 · consultar hasta que deje de estar «en_curso», respetando Retry-After ── while True: r = sesion.get(f"{API}/v1/trabajos/{trabajo}", timeout=30) if r.status_code in (429, 503) or (r.status_code == 200 and r.json()["estado"] == "en_curso"): time.sleep(int(r.headers.get("Retry-After", "5"))) continue break if r.status_code != 200: sys.exit(f"no se pudo consultar: {error_de(r)}") resultado = r.json() if resultado["estado"] == "terminado": documento = resultado["documento"] print(json.dumps(documento["factura"], ensure_ascii=False, indent=2)) print("dirección:", documento["direccion"]) # RECIBIDA si dijiste compra, EMITIDA si dijiste venta print("avisos:", [a["codigo"] for a in documento.get("avisos", [])]) else: print("rechazado:", resultado["error"]["codigo"], "-", resultado["error"]["mensaje"]) # ── fin paso 3 ── # ── paso 4 · lo que queda en la pool ── pool = sesion.get(f"{API}/v1/pool", timeout=30).json() print("disponibles:", pool["disponibles"], "· estado:", pool["estado"]) # ── fin paso 4 ──