Datos fiscales en Stripe Checkout

Si vendes con Stripe Checkout, la sesión puede pedir los datos fiscales que la app de Fiscalapi para Stripe necesita para emitir la factura a nombre de tu cliente en cuanto se confirma el pago. Sin ellos, el pago entra en la factura global de su periodo.

Qué datos pide la sesión

Una factura a nombre de tu cliente necesita cinco datos. Stripe Checkout captura el RFC y la razón social con sus propios campos, y los otros tres con campos personalizados cuya clave lee la app:

DatoParámetro de la sesiónClave
RFCtax_id_collectionTax ID de tipo mx_rfc
Razón socialname_collection.businessNombre del negocio
Régimen fiscalcustom_fields, lista desplegablefiscalapiTaxRegimeCode
Uso del CFDIcustom_fields, lista desplegablefiscalapiCfdiUseCode
Código postal fiscalcustom_fields, numérico de 5 dígitosfiscalapiZipCode

Para una persona física, la app usa como razón social el nombre individual si la sesión lo trae y, si no, el nombre del negocio; por eso basta con pedir este último. El correo de la factura es el que tu cliente captura en Checkout.

Crear la sesión

Stripe Checkout no tiene campos condicionales: si los cinco datos son obligatorios, también los pide a quien no quiere factura. Pregunta antes en tu página si tu cliente quiere factura y crea la sesión con los campos solo en ese caso.

Crear una sesión de Checkout

import Stripe from 'stripe'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)

const taxRegimes = [
  ['601', 'General de Ley Personas Morales'],
  ['603', 'Personas Morales con Fines no Lucrativos'],
  ['605', 'Sueldos y Salarios e Ingresos Asimilados a Salarios'],
  ['606', 'Arrendamiento'],
  ['607', 'Régimen de Enajenación o Adquisición de Bienes'],
  ['608', 'Demás ingresos'],
  ['610', 'Residentes en el Extranjero sin Establecimiento Permanente en México'],
  ['611', 'Ingresos por Dividendos (socios y accionistas)'],
  ['612', 'Personas Físicas con Actividades Empresariales y Profesionales'],
  ['614', 'Ingresos por intereses'],
  ['615', 'Régimen de los ingresos por obtención de premios'],
  ['616', 'Sin obligaciones fiscales'],
  ['620', 'Sociedades Cooperativas de Producción que optan por diferir sus ingresos'],
  ['621', 'Incorporación Fiscal'],
  ['622', 'Actividades Agrícolas, Ganaderas, Silvícolas y Pesqueras'],
  ['623', 'Opcional para Grupos de Sociedades'],
  ['624', 'Coordinados'],
  ['625', 'Actividades Empresariales con ingresos a través de Plataformas Tecnológicas'],
  ['626', 'Régimen Simplificado de Confianza'],
]

const cfdiUses = [
  ['G01', 'Adquisición de mercancías'],
  ['G02', 'Devoluciones, descuentos o bonificaciones'],
  ['G03', 'Gastos en general'],
  ['I01', 'Construcciones'],
  ['I02', 'Mobiliario y equipo de oficina por inversiones'],
  ['I03', 'Equipo de transporte'],
  ['I04', 'Equipo de cómputo y accesorios'],
  ['I05', 'Dados, troqueles, moldes, matrices y herramental'],
  ['I06', 'Comunicaciones telefónicas'],
  ['I07', 'Comunicaciones satelitales'],
  ['I08', 'Otra maquinaria y equipo'],
  ['D01', 'Honorarios médicos, dentales y gastos hospitalarios'],
  ['D02', 'Gastos médicos por incapacidad o discapacidad'],
  ['D03', 'Gastos funerales'],
  ['D04', 'Donativos'],
  ['D05', 'Intereses reales pagados por créditos hipotecarios'],
  ['D06', 'Aportaciones voluntarias al SAR'],
  ['D07', 'Primas por seguros de gastos médicos'],
  ['D08', 'Gastos de transportación escolar obligatoria'],
  ['D09', 'Depósitos en cuentas para el ahorro y planes de pensiones'],
  ['D10', 'Pagos por servicios educativos (colegiaturas)'],
  ['S01', 'Sin efectos fiscales'],
]

const options = (catalog) =>
  catalog.map(([value, description]) => ({ value, label: `${value} - ${description}` }))

function fiscalDataFields(wantsInvoice) {
  if (!wantsInvoice) return {}
  return {
    tax_id_collection: { enabled: true, required: 'if_supported' },
    name_collection: { business: { enabled: true } },
    custom_fields: [
      {
        key: 'fiscalapiTaxRegimeCode',
        label: { type: 'custom', custom: 'Régimen fiscal' },
        type: 'dropdown',
        dropdown: { options: options(taxRegimes) },
      },
      {
        key: 'fiscalapiCfdiUseCode',
        label: { type: 'custom', custom: 'Uso del CFDI' },
        type: 'dropdown',
        dropdown: { options: options(cfdiUses), default_value: 'G03' },
      },
      {
        key: 'fiscalapiZipCode',
        label: { type: 'custom', custom: 'Código postal fiscal' },
        type: 'numeric',
        numeric: { minimum_length: 5, maximum_length: 5 },
      },
    ],
  }
}

// wantsInvoice sale de tu página: por ejemplo, una casilla «Quiero factura».
export async function createCheckoutSession(wantsInvoice) {
  return stripe.checkout.sessions.create({
    mode: 'payment',
    line_items: [
      {
        price_data: {
          currency: 'mxn',
          unit_amount: 11600,
          product_data: { name: 'Suscripción anual' },
        },
        quantity: 1,
      },
    ],
    customer_creation: 'always',
    success_url: 'https://example.com/gracias',
    ...fiscalDataFields(wantsInvoice),
  })
}

Las listas incluyen los regímenes y los usos del CFDI que el SAT admite en una factura de venta; quedan fuera CP01 y CN01, que son de los complementos de pago y de nómina. Una sesión admite hasta tres campos personalizados, así que estos tres ocupan todos los disponibles.

Qué hace la app con la sesión

Cuando se confirma el pago, la app lee los datos de la sesión y emite la factura. Si los datos son válidos y la sesión crea o usa un Customer, la app también los guarda en él, así que las siguientes compras de ese cliente se facturan aunque la sesión ya no los pida.

SituaciónQué hace la app
La sesión trae los cinco datos y son válidosEmite la factura a nombre de tu cliente.
La sesión no trae RFCEl pago entra en la factura global.
La sesión trae RFC, pero falta otro dato o no es válidoNo emite la factura: el pago entra en la factura global, salvo que corrijas los datos desde el panel del cliente en Stripe antes de que el pago entre en la factura global de su periodo.
Es la renovación de una suscripciónUsa los datos guardados en el Customer.

La app valida que el régimen fiscal corresponda al tipo de persona del RFC y que el uso del CFDI esté permitido para ese régimen, con las mismas reglas del SAT que aplica Fiscalapi antes de timbrar.

¿Le resultó útil esta página?