n1coePayDocs

SDKs de cliente

Web

n1co.js y @n1co/elements, los campos de tarjeta de n1co en tu página.

Navegador · n1co.js o npm

Corre en el navegador del comprador, en una página https; para desarrollar también se aceptan http://localhost y http://127.0.0.1 (inclúyelos en page_origins si lo usas).

Instalar

HTML

pagar.html
<script src="https://elements.n1co.com/v1/n1co.js"></script>
<script>
  const client = n1co('pk_3x4mpl3pUbl1shabl3k3yAAAAAAAAAAA', { environment: 'sandbox' });
</script>

Web

Terminal
npm install @n1co/elements
pnpm add @n1co/elements
yarn add @n1co/elements
bun add @n1co/elements
n1co.ts
import { loadN1co } from '@n1co/elements';

// null durante el renderizado en servidor: llámalo en el navegador.
const client = await loadN1co('pk_3x4mpl3pUbl1shabl3k3yAAAAAAAAAAA', { environment: 'sandbox' });

Vue / Nuxt

Terminal
npm install @n1co/elements
pnpm add @n1co/elements
yarn add @n1co/elements
bun add @n1co/elements
Pay.vue
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { loadN1co, type N1coClient, type Element as CardElement } from '@n1co/elements';

const cardBox = ref<HTMLElement | null>(null);
const client = ref<N1coClient | null>(null);
const card = ref<CardElement | null>(null);
const message = ref('');
const complete = ref(false);
const paying = ref(false);

onMounted(async () => {
  // null durante el renderizado en servidor; en Nuxt también puedes envolver este componente en <ClientOnly>.
  client.value = await loadN1co('pk_3x4mpl3pUbl1shabl3k3yAAAAAAAAAAA', { environment: 'sandbox' });
  if (!client.value || !cardBox.value) return;
  card.value = client.value.elements().create('card', { locale: 'es' });
  card.value.on('change', (e) => {
    complete.value = e.complete;
    message.value = e.error ? e.error.message : '';
  });
  card.value.mount(cardBox.value);
});

onBeforeUnmount(() => card.value?.unmount());

async function pay() {
  if (!client.value || !card.value) return;
  paying.value = true;
  message.value = '';
  try {
    // Tu endpoint crea el intent con tu secret_key y devuelve solo su intent_token.
    const { intent_token } = await fetch('/checkout/payment-intent', { method: 'POST' }).then((r) => r.json());
    const intent = await client.value.confirmPayment({ intent_token, card: card.value });
    if (intent.status === 'succeeded') {
      // Tu servidor confirma con el webhook antes de entregar el pedido.
      window.location.assign('/checkout/thanks');
    } else if (intent.status === 'processing') {
      message.value = 'Estamos confirmando tu pago. No vuelvas a pagar; te avisaremos.';
    } else {
      message.value = intent.last_error?.message ?? 'No se pudo completar el pago.';
      paying.value = false;
    }
  } catch (error) {
    message.value = error instanceof Error ? error.message : String(error);
    paying.value = false;
  }
}
</script>

<template>
  <form @submit.prevent="pay">
    <div ref="cardBox"></div>
    <p role="alert" v-text="message"></p>
    <button type="submit" :disabled="!complete || paying">Pagar $12.50</button>
  </form>
</template>
Opción Valores Por defecto Dónde
environment 'production', 'sandbox' 'production' n1co(), loadN1co()
locale 'es', 'en' 'es' create('card', …), create('cvc', …)

Métodos

Cliente

Método Qué hace Devuelve
n1co(publishableKey, options?) Crea el cliente con tu publishable_key. El cliente
loadN1co(publishableKey, options?) Carga n1co.js desde npm y crea el cliente. Promesa del cliente, null en SSR
elements().create('card', options?) Crea el formulario de tarjeta. Elemento de tarjeta
elements().create('cvc', options?) Crea el código de seguridad de una tarjeta guardada. Elemento cvc
confirmPayment({ intent_token, card?, cvc?, billing? }) Cobra el intent con una tarjeta nueva, la adjunta o su código; resuelve 3D Secure. Promesa del intent en estado final
retrievePaymentIntent(intent_token) Lee el intent como está ahora. Promesa del intent

Elemento

Método Qué hace Devuelve
mount(selectorOrElement) Inserta el iframe. El elemento
unmount() Quita el iframe y descarta lo escrito. El elemento
on(event, handler) Agrega un manejador de ready, change, focus, blur, error o capture. El elemento
off(event, handler) Quita un manejador. El elemento
focus(field?) Enfoca un campo. El elemento
clear() Vacía los campos. El elemento
update({ appearance?, locale? }) Cambia la apariencia o el idioma. El elemento
capture({ session_token }) Guarda la tarjeta bajo la sesión; solo el elemento de tarjeta. Promesa de { ok: true, card }

Apariencia

Pasa appearance al crear el campo. update() mezcla variables sin quitar las anteriores: al cambiar de tema envía la apariencia completa. Lo que el comprador escribió se conserva.

HTML

pay.html
<div id="card"></div>
<script>
  const themes = {
    light: {
      theme: 'light',
      variables: {
        colorBackground: '#ffffff',
        colorText: '#1a1a1a',
        colorTextPlaceholder: '#767676',
        colorBorder: '#cccccc',
        colorBorderFocus: '#0b7a75',
        colorDanger: '#c62828',
      },
    },
    dark: {
      theme: 'dark',
      variables: {
        colorBackground: '#1a1a1a',
        colorText: '#f2f2f2',
        colorTextPlaceholder: '#8b8b8b',
        colorBorder: '#444444',
        colorBorderFocus: '#4da3ff',
        colorDanger: '#ff7b72',
      },
    },
  };
  // Sigue el tema del sistema; update() recibe siempre la apariencia completa.
  const media = matchMedia('(prefers-color-scheme: dark)');
  const pick = (dark) => themes[dark ? 'dark' : 'light'];
  const card = n1co('pk_3x4mpl3pUbl1shabl3k3yAAAAAAAAAAA', { environment: 'sandbox' }).elements().create('card', { appearance: pick(media.matches) });
  card.mount('#card');
  media.addEventListener('change', (e) => card.update({ appearance: pick(e.matches) }));
</script>

Web

card.ts
import { loadN1co } from '@n1co/elements';

const themes = {
  light: {
    theme: 'light',
    variables: {
      colorBackground: '#ffffff',
      colorText: '#1a1a1a',
      colorTextPlaceholder: '#767676',
      colorBorder: '#cccccc',
      colorBorderFocus: '#0b7a75',
      colorDanger: '#c62828',
    },
  },
  dark: {
    theme: 'dark',
    variables: {
      colorBackground: '#1a1a1a',
      colorText: '#f2f2f2',
      colorTextPlaceholder: '#8b8b8b',
      colorBorder: '#444444',
      colorBorderFocus: '#4da3ff',
      colorDanger: '#ff7b72',
    },
  },
} as const;
// Sigue el tema del sistema; update() recibe siempre la apariencia completa.
const media = matchMedia('(prefers-color-scheme: dark)');
const pick = (dark: boolean) => themes[dark ? 'dark' : 'light'];

const client = await loadN1co('pk_3x4mpl3pUbl1shabl3k3yAAAAAAAAAAA', { environment: 'sandbox' });
const card = client?.elements().create('card', { appearance: pick(media.matches) });
card?.mount('#card');
media.addEventListener('change', (e) => card?.update({ appearance: pick(e.matches) }));

Vue / Nuxt

Card.vue
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { loadN1co, type Element as CardElement } from '@n1co/elements';

const themes = {
  light: {
    theme: 'light',
    variables: {
      colorBackground: '#ffffff',
      colorText: '#1a1a1a',
      colorTextPlaceholder: '#767676',
      colorBorder: '#cccccc',
      colorBorderFocus: '#0b7a75',
      colorDanger: '#c62828',
    },
  },
  dark: {
    theme: 'dark',
    variables: {
      colorBackground: '#1a1a1a',
      colorText: '#f2f2f2',
      colorTextPlaceholder: '#8b8b8b',
      colorBorder: '#444444',
      colorBorderFocus: '#4da3ff',
      colorDanger: '#ff7b72',
    },
  },
} as const;

const cardBox = ref<HTMLElement | null>(null);
const card = ref<CardElement | null>(null);
const media = ref<MediaQueryList | null>(null);
const pick = (dark: boolean) => themes[dark ? 'dark' : 'light'];
const follow = (e: MediaQueryListEvent) => card.value?.update({ appearance: pick(e.matches) });

onMounted(async () => {
  const client = await loadN1co('pk_3x4mpl3pUbl1shabl3k3yAAAAAAAAAAA', { environment: 'sandbox' });
  if (!client || !cardBox.value) return;
  media.value = window.matchMedia('(prefers-color-scheme: dark)');
  card.value = client.elements().create('card', { appearance: pick(media.value.matches) });
  card.value.mount(cardBox.value);
  media.value.addEventListener('change', follow);
});

onBeforeUnmount(() => {
  media.value?.removeEventListener('change', follow);
  card.value?.unmount();
});
</script>

<template>
  <div ref="cardBox"></div>
</template>
Opción Valores Por defecto Qué controla
theme 'light', 'dark' 'light' Colores base y texto secundario fijo.
colorBackground #rgb o #rrggbb según theme: #ffffff, #1c2128 Fondo del campo.
colorText #rgb o #rrggbb #1f2328, #e6e8eb Texto escrito.
colorTextPlaceholder #rgb o #rrggbb #8a8f98, #7d8590 Texto de ejemplo.
colorBorder #rgb o #rrggbb #c9ced6, #444c56 Borde.
colorBorderFocus #rgb o #rrggbb #2f6feb, #539bf5 Borde con foco.
colorDanger #rgb o #rrggbb #c62828, #ff7b72 Mensajes de error.
fontFamily 'system', 'sans-serif', 'serif', 'monospace' 'system' Tipografía.
fontSizeBase 12 a 20 px (16 o '16px') 15 px Tamaño base del texto.
borderRadius 0 a 16 px 6 px Esquinas.
spacingUnit 2 a 12 px 4 px Espaciado.

El contraste se exige: una apariencia inválida lanza invalid_options de forma síncrona en create() o update().

Ajusta colorBackground al fondo del contenedor donde va el campo para que se integre.

Escribe para buscar en la documentación.