Zaroz
Facturación y monedero

Gestiona una suscripción

Una suscripción es una compra de un producto y plan; es lo que se renueva y factura, y la instancia de servicio es lo que corre. Comprar está en Suscripciones y facturación. Esta página cubre todo lo que haces con una suscripción después. Los ejemplos usan el ayudante de Usar la API de Zaroz.

Listar y leer

curl

TerminalCode
curl -sS "$ZAROZ_API/subscriptions?project_id=$PROJECT&page_size=50" -H "$AUTH" curl -sS "$ZAROZ_API/subscriptions/$SUBSCRIPTION" -H "$AUTH" curl -sS "$ZAROZ_API/subscriptions/$SUBSCRIPTION/resources" -H "$AUTH" # lo que ejecuta

TypeScript

TypeScriptCode
import { call } from "./zaroz"; const list = await call("GET", `/subscriptions?project_id=${PROJECT}`); for (const s of list.body.data) console.log(s.id, s.status, s.auto_renew, s.cancels_at, s.upcoming_charge);

Python

Code
from zaroz import call _, subs = call("GET", f"/subscriptions?project_id={PROJECT}") for s in subs["data"]: print(s["id"], s["status"], s["auto_renew"], s["cancels_at"], s["upcoming_charge"])

status es pending, active, suspended, pending_termination, terminated, cancelled o failed. Una suscripción suspendida (normalmente por impago) permanece detenida hasta liquidar la factura; pagarla levanta la suspensión.

Detener o reanudar la renovación

Renovación y cancelación son cosas distintas a propósito.

  • PUT /subscriptions/{id}/renewal decide si se emite la próxima factura. El servicio sigue en marcha y no se fija fecha de fin.
  • PUT /subscriptions/{id}/cancellation decide si el servicio termina cuando termina el periodo pagado.

curl

TerminalCode
curl -sS -X PUT "$ZAROZ_API/subscriptions/$SUBSCRIPTION/renewal" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"auto_renew": false}' curl -sS -X PUT "$ZAROZ_API/subscriptions/$SUBSCRIPTION/cancellation" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"cancel_at_period_end": true}' # {"subscription_id":"...","status":"active","cancels_at":"2026-11-30T00:00:00Z","auto_renew":true,"changed":true} # si cambias de opinión antes de ese instante: curl -sS -X PUT "$ZAROZ_API/subscriptions/$SUBSCRIPTION/cancellation" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"cancel_at_period_end": false}'

TypeScript

TypeScriptCode
await call("PUT", `/subscriptions/${SUB}/renewal`, { auto_renew: false }); const c = await call("PUT", `/subscriptions/${SUB}/cancellation`, { cancel_at_period_end: true }); console.log(c.body.cancels_at, c.body.changed); // changed:false significa que ya era así; no es un error

Python

Code
call("PUT", f"/subscriptions/{SUB}/renewal", {"auto_renew": False}) _, c = call("PUT", f"/subscriptions/{SUB}/cancellation", {"cancel_at_period_end": True}) print(c["cancels_at"], c["changed"]) # changed False significa que ya era así; no es un error

Programar un fin no mueve dinero: no se emite ni anula ninguna factura y no se acredita nada. Enviar la decisión que ya está en vigor funciona y responde changed: false. Una suscripción suspendida no se puede cancelar al final del periodo (409) porque devolvería al servicio un servicio detenido por impago; liquida antes lo adeudado. Estas llamadas requieren el rol que puede comprar por la organización (Owner o Admin); un token de organización necesita subscriptions.renewal.manage o subscriptions.cancel.

Cancelar ya y recuperar el tiempo sin usar

Si prefieres parar de inmediato y convertir la parte sin usar del periodo pagado en crédito de monedero, pide un presupuesto y canjéalo. El presupuesto va firmado y vale diez minutos.

curl

TerminalCode
curl -sS "$ZAROZ_API/subscriptions/$SUBSCRIPTION/wallet-credit-quote" -H "$AUTH" # {"eligible": true, "total_cents": 1840, "minimum_cents": 100, "below_minimum": false, # "quote_id": "...", "expires_at": "...", "period_remaining_seconds": 1234567, ...} curl -sS -X POST "$ZAROZ_API/subscriptions/$SUBSCRIPTION/cancel-with-wallet-credit" -H "$AUTH" \ -H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" -d '{"quote_id": "<quote_id>"}' # {"status": "cancelled", "total_credited_cents": 1840, "credit_note_ids": ["..."], ...}

TypeScript

TypeScriptCode
import { randomUUID } from "node:crypto"; const q = await call("GET", `/subscriptions/${SUB}/wallet-credit-quote`); if (q.body.eligible && !q.body.below_minimum) { const done = await call("POST", `/subscriptions/${SUB}/cancel-with-wallet-credit`, { quote_id: q.body.quote_id }, { "Idempotency-Key": randomUUID() }); console.log("acreditado", done.body.total_credited_cents); } else { console.log("no elegible:", q.body.reason); }

Python

Code
import uuid _, q = call("GET", f"/subscriptions/{SUB}/wallet-credit-quote") if q["eligible"] and not q["below_minimum"]: _, done = call("POST", f"/subscriptions/{SUB}/cancel-with-wallet-credit", {"quote_id": q["quote_id"]}, headers={"Idempotency-Key": str(uuid.uuid4())}) print("acreditado", done["total_credited_cents"]) else: print("no elegible:", q["reason"])

Esto cancela el servicio ya y no se puede deshacer. Se rechaza con 422 si el presupuesto está alterado o es de otra suscripción, si la suscripción no es elegible o si el crédito no llega al mínimo, y con 409 si el presupuesto caducó o su importe cambió desde que lo pediste (pide uno nuevo). Puedes pedir el presupuesto tantas veces como quieras: no escribe nada.

Redimensionar

Un redimensionado indica la forma final completa (cpu, memoria en GiB y almacenamiento en GiB), no un cambio. Envía la dimensión que no quieres cambiar con su valor actual. El almacenamiento solo puede crecer. Presupuesta siempre primero.

curl

TerminalCode
BODY='{"resources": {"cpu": 2, "memory_gb": 4, "storage_gb": 40}}' curl -sS -X POST "$ZAROZ_API/subscriptions/$SUBSCRIPTION/resize/quote" -H "$AUTH" -H "Content-Type: application/json" -d "$BODY" # {"billing": {"kind": "growth", "subtotal_cents": ..., "tax_cents": ..., "total_cents": ..., "currency": "EUR"}, # "dimensions": [{"dimension": "cpu", "current": 1, "requested": 2, "growing": true, "available": true}, ...]} curl -sS -X POST "$ZAROZ_API/subscriptions/$SUBSCRIPTION/resize" -H "$AUTH" -H "Content-Type: application/json" -d "$BODY" # {"outcome": "...", "invoice": {"id": "...", "amount_cents": ..., "status": "pending"}, "scheduled_downsize": null} curl -sS -X POST "$ZAROZ_API/subscriptions/$SUBSCRIPTION/resize/cancel" -H "$AUTH" # 204, retira un redimensionado pendiente

TypeScript

TypeScriptCode
const target = { resources: { cpu: 2, memory_gb: 4, storage_gb: 40 } }; const quote = await call("POST", `/subscriptions/${SUB}/resize/quote`, target); if (quote.body.dimensions.some((d: any) => d.growing && !d.available)) throw new Error("sin capacidad ahora"); console.log(quote.body.billing); // { kind: "growth", total_cents, ... } o { kind: "downsize" } const resize = await call("POST", `/subscriptions/${SUB}/resize`, target); if (resize.body.invoice) { // págala: ver Monedero, recargas, códigos y facturas await call("POST", `/billing/invoices/${resize.body.invoice.id}/pay-with-wallet`, undefined, { "Idempotency-Key": randomUUID() }); }

Python

Code
target = {"resources": {"cpu": 2, "memory_gb": 4, "storage_gb": 40}} _, quote = call("POST", f"/subscriptions/{SUB}/resize/quote", target) if any(d["growing"] and not d["available"] for d in quote["dimensions"]): raise RuntimeError("sin capacidad ahora") print(quote["billing"]) # {"kind": "growth", "total_cents": ...} o {"kind": "downsize"} _, resize = call("POST", f"/subscriptions/{SUB}/resize", target) if resize.get("invoice"): call("POST", f"/billing/invoices/{resize['invoice']['id']}/pay-with-wallet", headers={"Idempotency-Key": str(uuid.uuid4())})

Cómo se factura:

  • Crecer una dimensión reserva capacidad durante 30 minutos y emite una factura pendiente de resize por la diferencia prorrateada. El aumento se aplica cuando esa factura se paga. Págala dentro de la reserva o el bloqueo se libera.
  • Reducir nunca se factura ni se abona. Se programa (scheduled_downsize) y se aplica al final del periodo de facturación actual, justo antes de que la próxima renovación se calcule con la forma más pequeña. Una nueva petición sustituye a una reducción programada anterior.
  • La comprobación de existencias del presupuesto es una vista previa en vivo y puede quedar desfasada cuando redimensionas.
  • POST .../resize/cancel retira un redimensionado pendiente (anulando su factura sin pagar) y responde 204 incluso si no había nada pendiente. Se rechaza con 409 solo cuando el aumento pendiente ya está pagado.

Redimensionar requiere el rol Owner o Admin; un token de organización necesita subscriptions.resize.manage. Se aplican el mínimo y el máximo del producto.

Cambiar de plan

Donde el producto lo ofrece, puedes pasar a otro de sus planes del catálogo con la misma forma de presupuesto y confirmación. Obtén los ids de plan en GET /catalog/products/{id}/plans.

curl

TerminalCode
curl -sS -X POST "$ZAROZ_API/subscriptions/$SUBSCRIPTION/resize/plan-change/quote" -H "$AUTH" \ -H "Content-Type: application/json" -d '{"plan_id": 7}' curl -sS -X POST "$ZAROZ_API/subscriptions/$SUBSCRIPTION/resize/plan-change" -H "$AUTH" \ -H "Content-Type: application/json" -d '{"plan_id": 7}'

TypeScript

TypeScriptCode
const plans = await call("GET", `/catalog/products/${PRODUCT}/plans`); const q = await call("POST", `/subscriptions/${SUB}/resize/plan-change/quote`, { plan_id: plans.body[0].id });

Python

Code
_, plans = call("GET", f"/catalog/products/{PRODUCT}/plans") _, q = call("POST", f"/subscriptions/{SUB}/resize/plan-change/quote", {"plan_id": plans[0]["id"]})

Se rechaza si el producto no ofrece cambio de plan por cuenta propia, o si plan_id no es uno de sus planes activos y públicos. La facturación es la de un redimensionado: el aumento se factura ahora y un plan menor se aplica al final del periodo.

Pruebas gratuitas

Una prueba es un periodo gratuito en un producto. GET /me/trials lista lo que puedes reclamar, incluidas las que ya usaste (como claimable: false, para que no desaparezcan en silencio).

curl

TerminalCode
curl -sS "$ZAROZ_API/me/trials" -H "$AUTH" curl -sS -X POST "$ZAROZ_API/me/trials" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"trial_price_id": "<id de la lista>", "name": "Mi prueba", "slug": "mi-prueba"}'

TypeScript

TypeScriptCode
const trials = await call("GET", "/me/trials"); const t = trials.body.trials.find((x: any) => x.claimable); if (t) { const claimed = await call("POST", "/me/trials", { trial_price_id: t.id, name: "Mi prueba", slug: "mi-prueba" }); console.log(claimed.body.subscription.id); }

Python

Code
_, trials = call("GET", "/me/trials") t = next((x for x in trials["trials"] if x["claimable"]), None) if t: _, claimed = call("POST", "/me/trials", {"trial_price_id": t["id"], "name": "Mi prueba", "slug": "mi-prueba"}) print(claimed["subscription"]["id"])
  • Reclamar crea la suscripción en tu organización personal (no puedes nombrar otra) y la activa al instante. No se cobra nada ni hace falta método de pago.
  • auto_renew empieza en false: si no haces nada, la prueba se suspende en trial_ends_at en lugar de facturarte. Para conservar el servicio, activa la renovación (PUT .../renewal) antes; la facturación empieza en trial_ends_at.
  • Una prueba por producto y cuenta, para siempre. Un segundo intento se rechaza con already_claimed; capacity_reached significa que lo intentes más tarde; not_available que la prueba está desactivada.
  • Cuando region_is_fixed es verdadero la prueba fija su propio hardware e ignora la región que envíes.
  • Donde el despliegue usa un captcha para pruebas, la reclamación necesita además un token de captcha.
Last modified on