> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gifty.lat/llms.txt
> Use this file to discover all available pages before exploring further.

# Entender el saldo y el canje parcial

> El importe emitido, lo que ya se usó y lo que todavía se puede canjear.

export const BalanceSequence = ({locale = "es-AR", fallback, fallbackHref}) => {
  const copy = ({
    "es-AR": {
      label: "Ejemplo de saldo",
      stages: ["Emitir", "Usar 30", "Usar 70"],
      issued: "EMITIDO",
      used: "USADO",
      available: "DISPONIBLE",
      unit: "unidades de ejemplo",
      play: "Reproducir",
      pause: "Pausar",
      reset: "Volver al inicio",
      detail: "Ver el ejemplo en texto",
      state: ["Todo el valor está disponible", "Queda saldo para otro canje", "El saldo se agotó"]
    },
    en: {
      label: "Balance example",
      stages: ["Issue", "Use 30", "Use 70"],
      issued: "ISSUED",
      used: "USED",
      available: "AVAILABLE",
      unit: "example units",
      play: "Play",
      pause: "Pause",
      reset: "Back to start",
      detail: "Read the text example",
      state: ["All value is available", "Value remains for another use", "The balance is exhausted"]
    },
    "pt-BR": {
      label: "Exemplo de saldo",
      stages: ["Emitir", "Usar 30", "Usar 70"],
      issued: "EMITIDO",
      used: "USADO",
      available: "DISPONÍVEL",
      unit: "unidades de exemplo",
      play: "Reproduzir",
      pause: "Pausar",
      reset: "Voltar ao início",
      detail: "Ver o exemplo em texto",
      state: ["Todo o valor está disponível", "Resta saldo para outro resgate", "O saldo se esgotou"]
    }
  })[locale] || null;
  const [step, setStep] = useState(0);
  const [playing, setPlaying] = useState(false);
  const [reduced, setReduced] = useState(false);
  useEffect(() => {
    const query = window.matchMedia("(prefers-reduced-motion: reduce)");
    const sync = () => setReduced(query.matches);
    sync();
    query.addEventListener("change", sync);
    return () => query.removeEventListener("change", sync);
  }, []);
  useEffect(() => {
    if (!playing) return;
    if (step === 2) {
      setPlaying(false);
      return;
    }
    const timer = window.setTimeout(() => setStep(current => Math.min(2, current + 1)), 1800);
    return () => window.clearTimeout(timer);
  }, [playing, step]);
  if (!copy) return <p>{fallback}</p>;
  const balance = [100, 70, 0][step];
  const used = 100 - balance;
  const [unitFirst, ...unitRest] = copy.unit.split(" ");
  const select = index => {
    setPlaying(false);
    setStep(index);
  };
  return <figure className="gifty-balance" data-reduced-motion={reduced ? "true" : "false"} aria-label={copy.label}>
      <div className="gifty-sequence-stages" role="group" aria-label={copy.label}>
        {copy.stages.map((stage, index) => <button key={stage} type="button" onClick={() => select(index)} aria-pressed={step === index}>
            <span>{index + 1}</span>{stage}
          </button>)}
      </div>
      <svg viewBox="0 0 480 240" role="img" aria-label={`${copy.issued}: 100. ${copy.used}: ${used}. ${copy.available}: ${balance}.`}>
        <rect x="24" y="32" width="168" height="164" rx="16" className="gifty-svg-card" />
        <text x="44" y="67" className="gifty-svg-label">{copy.issued}</text>
        <text x="44" y="128" className="gifty-svg-number">100</text>
        <text className="gifty-svg-note"><tspan x="44" y="161">{unitFirst}</tspan><tspan x="44" y="181">{unitRest.join(" ")}</tspan></text>
        <path d="M208 111 H245 M236 102 L245 111 L236 120" className="gifty-svg-arrow" />
        <text x="266" y="65" className="gifty-svg-label">{copy.available}</text>
        <text x="266" y="128" className="gifty-svg-number gifty-svg-accent">{balance}</text>
        <rect x="266" y="147" width="190" height="8" rx="4" className="gifty-svg-track" />
        <g transform="translate(266 147)">
          <rect width="190" height="8" rx="4" className="gifty-svg-progress" style={{
    transform: `scaleX(${balance / 100})`
  }} />
        </g>
        <text x="266" y="185" className="gifty-svg-note">{copy.used}: {used}</text>
      </svg>
      <figcaption>
        <p className="gifty-sequence-state" aria-live="polite" aria-atomic="true">{copy.state[step]}</p>
        <div className="gifty-sequence-actions">
          <button type="button" onClick={() => {
    if (step === 2) setStep(0);
    setPlaying(value => !value);
  }}>
            <span aria-hidden="true">{playing ? "Ⅱ" : "▷"}</span> {playing ? copy.pause : copy.play}
          </button>
          <button type="button" onClick={() => select(0)}>{copy.reset}</button>
          <a href={fallbackHref}>{copy.detail}</a>
        </div>
      </figcaption>
    </figure>;
};

El saldo es el importe emitido menos lo canjeado. **Esta documentación no conoce el saldo de tu código**: para ver un caso real, abrí su detalle en gifty con acceso a la tienda correspondiente.

## Un ejemplo, paso a paso

El diagrama usa unidades de ejemplo, no una moneda ni un saldo real. Elegí una etapa o reproducí la secuencia. La tabla de abajo contiene la misma información.

<BalanceSequence locale="es-AR" fallback="Ejemplo: se emiten 100 unidades, se canjean 30 y quedan 70; al usar las otras 70, el saldo llega a 0." fallbackHref="#ejemplo-completo" />

## Ejemplo completo

| Momento              | Emitido | Canjeado acumulado | Saldo |
| -------------------- | ------- | ------------------ | ----- |
| Emisión              | 100     | 0                  | 100   |
| Primer canje parcial | 100     | 30                 | 70    |
| Canje del resto      | 100     | 100                | 0     |

El monto a descontar debe ser mayor que cero y no superar el saldo disponible. Al agotarse el saldo, la gift card queda canjeada; mientras quede saldo después de un canje parcial, queda parcialmente canjeada.

## Cuándo se permite un canje parcial

La tienda debe tener habilitado el canje parcial y la gift card debe ser elegible. La preferencia está apagada por defecto. Se aplica a gift cards nativas de gifty, incluidas las ya emitidas que continúan siendo elegibles; las migradas quedan fuera de este circuito.

Si se desactiva la preferencia, las gift cards ya usadas parcialmente conservan la posibilidad de usar el saldo restante hasta agotarlo o vencer. Las que no se usaron parcialmente vuelven al comportamiento de canje completo. Activar la opción no recupera valor que ya se hubiera perdido.

## Si el saldo cambia mientras confirmás

Otra compra puede consumir saldo antes de tu confirmación. Leé el saldo actualizado y corregí el monto; no reintentes ciegamente el importe anterior. La confirmación vuelve a validar el saldo.

## Si un canje queda a medias

El canje en mostrador coordina el cupón de la tienda y el saldo. Si la operación queda pendiente, usá las opciones de completar o cancelar que ofrezca gifty. Cancelar un intento pendiente no es deshacer un canje ya completado.

Después de un canje parcial, la actualización del cupón continúa de forma durable. **No asumas que el código se puede volver a usar de manera instantánea**: seguí el estado de la operación.

## Lo que este ejemplo no decide

El diagrama no valida códigos, no cambia preferencias y no realiza canjes. Las condiciones reales se consultan en la tienda de la gift card. No supone que pueda usarse en otra tienda.

[Emitir y comprobar una entrega](/es/issue-and-deliver) · [Ver el ciclo completo](/es/gift-lifecycle)

[Leer esta página en Markdown](/exports/es/balance.md)
