Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/eclipxe13/CfdiUtils/llms.txt

Use this file to discover all available pages before exploring further.

A Retenciones e Información de Pagos document is a special category of CFDI that reports tax withholdings and payment information to the SAT. Unlike a regular income or expense CFDI, a Retenciones document uses its own XML schema (retenciones) and is not a cfdi:Comprobante — it is a retenciones:Retenciones root element with its own namespace, attribute set, and validation rules. CfdiUtils provides two creator classes for this document type:
  • CfdiUtils\Retenciones\RetencionesCreator10 — targets the version 1.0 schema (older format, signs with SHA-1).
  • CfdiUtils\Retenciones\RetencionesCreator20 — targets the version 2.0 schema (current format, signs with SHA-256).
Both classes share their implementation through RetencionesCreatorTrait and mirror the design of CfdiCreator40: you get a root element object (retenciones()), helper methods to set the certificate, sign, validate, and export to XML.

Constructor

Both RetencionesCreator10 and RetencionesCreator20 share the same constructor signature:
new RetencionesCreator10(
    array $retencionesAttributes = [],
    ?XmlResolver $xmlResolver = null,
    ?XsltBuilderInterface $xsltBuilder = null,
    ?Certificado $certificado = null,
)

new RetencionesCreator20(
    array $retencionesAttributes = [],
    ?XmlResolver $xmlResolver = null,
    ?XsltBuilderInterface $xsltBuilder = null,
    ?Certificado $certificado = null,
)
retencionesAttributes
array
default:"[]"
Key/value pairs set as attributes on the root Retenciones element. For version 1.0, typical keys are FechaExp and CveRetenc. For version 2.0, LugarExpRetenc is also required.
xmlResolver
XmlResolver|null
default:"null"
Controls local caching of SAT XSD and XSLT resources. Defaults to a new XmlResolver pointing to SAT remote URLs.
xsltBuilder
XsltBuilderInterface|null
default:"null"
The XSLT engine used to generate the cadena de origen. Defaults to CfdiUtils\CadenaOrigen\DOMBuilder.
certificado
Certificado|null
default:"null"
An optional CfdiUtils\Certificado\Certificado instance. When provided, putCertificado() is called at construction time.

Methods

All methods below are available on both RetencionesCreator10 and RetencionesCreator20 via the shared RetencionesCreatorTrait, plus the version-specific putCertificado() and buildCadenaDeOrigen() methods defined on each class.
MethodDescription
retenciones(): RetencionesReturns the root Retenciones element object (Retenciones10\Retenciones or Retenciones20\Retenciones).
putCertificado(Certificado $certificado): voidStores the certificate serial and PEM content on the retenciones node. Version 1.0 writes NumCert and Cert; version 2.0 writes NoCertificado and Certificado.
addSello(string $key, string $passPhrase = ''): voidSigns the cadena de origen and stores the Base64 result in the Sello attribute. Version 1.0 uses SHA-1; version 2.0 uses SHA-256.
buildCadenaDeOrigen(): stringFetches the version-specific XSLT and applies it to the current XML, returning the cadena de origen string. Throws \LogicException if the XmlResolver or XSLT builder is not set.
asXml(): stringSerializes the entire element tree to an XML string.
moveSatDefinitionsToRetenciones(): voidMoves namespace declarations to the root Retenciones node (SAT requirement).
validate(): AssertsValidates the document against its XSD schema. Returns a CfdiUtils\Validate\Asserts collection.
Unlike regular CFDI validation (which runs multiple business-rule validators), Retenciones validation only checks XSD schema conformance via XmlFollowSchema.

Creation Steps

1

Instantiate the creator with root attributes

Choose RetencionesCreator10 for the legacy v1.0 format or RetencionesCreator20 for the current v2.0 format.
<?php
use CfdiUtils\Retenciones\RetencionesCreator20;

$creator = new RetencionesCreator20([
    'FechaExp'       => '2024-03-15T10:00:00',
    'CveRetenc'      => '14', // Dividendos o utilidades distribuidos
    'LugarExpRetenc' => '64000',
]);
2

Populate the Retenciones element

Obtain the root element and add Emisor, Receptor, Periodo, Totales, and ImpRetenidos nodes.
$retenciones = $creator->retenciones();

$retenciones->addEmisor([
    'RfcE'            => 'EKU9003173C9',
    'NomDenRazSocE'   => 'ESCUELA KEMPER URGATE',
    'RegimenFiscalE'  => '601',
]);

$retenciones->getReceptor()->addExtranjero([
    'NumRegIdTribR'   => '998877665544332211',
    'NomDenRazSocR'   => 'WORLD WIDE COMPANY INC',
]);

$retenciones->addPeriodo([
    'MesIni'   => '03',
    'MesFin'   => '03',
    'Ejercicio' => '2024',
]);

$retenciones->addTotales([
    'MontoTotOperacion' => '55578643',
    'MontoTotGrav'      => '0',
    'MontoTotExent'     => '55578643',
    'MontoTotRet'       => '0',
    'UtilidadBimestral' => '0.00',
    'ISRCorrespondiente' => '0.00',
]);

$retenciones->addImpRetenidos([
    'BaseRet'     => '0',
    'ImpuestoRet' => '001', // ISR
    'TipoPagoRet' => '01',
    'MontoRet'    => '0.00',
]);
3

Add a complement (if required)

Retenciones documents often carry a specific complement such as Dividendos10. Use addComplemento() exactly as you would on a regular CFDI comprobante.
use CfdiUtils\Elements\Dividendos10\Dividendos;

$dividendos = new Dividendos();
$dividendos->addDividOUtil([
    'CveTipDivOUtil'           => '06',
    'MontISRAcredRetMexico'    => '0',
    'MontISRAcredRetExtranjero' => '0',
    'MontRetExtDivExt'         => '0',
    'TipoSocDistrDiv'          => 'Sociedad Nacional',
    'MontISRAcredNal'          => '0',
    'MontDivAcumNal'           => '0',
    'MontDivAcumExt'           => '0',
]);

$retenciones->addComplemento($dividendos);
4

Attach the certificate and sign

use CfdiUtils\Certificado\Certificado;

$creator->putCertificado(new Certificado('/certs/my_cert.cer'));
$creator->addSello('file:///certs/my_key.pem', 'my-passphrase');
5

Move namespace definitions and validate

$creator->moveSatDefinitionsToRetenciones();

$asserts = $creator->validate();
if ($asserts->hasErrors()) {
    foreach ($asserts->errors() as $assert) {
        echo $assert->getCode() . ': ' . $assert->getStatus()->getMessage() . PHP_EOL;
    }
    exit(1);
}
6

Save the XML

file_put_contents('/output/retenciones.xml', $creator->asXml());
echo 'Retenciones document saved.' . PHP_EOL;

Full Example: Retenciones 2.0

<?php
declare(strict_types=1);

use CfdiUtils\Retenciones\RetencionesCreator20;
use CfdiUtils\Certificado\Certificado;
use CfdiUtils\Elements\Dividendos10\Dividendos;

// Instantiate with root-level attributes
$creator = new RetencionesCreator20([
    'FechaExp'        => '2024-06-01T08:00:00',
    'CveRetenc'       => '14', // Dividendos o utilidades distribuidos
    'LugarExpRetenc'  => '64000',
]);

$retenciones = $creator->retenciones();

// Add related documents (optional, v2.0 only)
$retenciones->addCfdiRetenRelacionados([
    'TipoRelacion' => '01',
    'UUID'         => '1474b7d3-61fc-41c4-a8b8-3f22e1161bb4',
]);

// Emisor
$retenciones->addEmisor([
    'RfcE'           => 'EKU9003173C9',
    'NomDenRazSocE'  => 'ESCUELA KEMPER URGATE',
    'RegimenFiscalE' => '601',
]);

// Receptor — foreign entity
$retenciones->getReceptor()->addExtranjero([
    'NumRegIdTribR' => '998877665544332211',
    'NomDenRazSocR' => 'WORLD WIDE COMPANY INC',
]);

// Period covered
$retenciones->addPeriodo([
    'MesIni'    => '05',
    'MesFin'    => '05',
    'Ejercicio' => '2024',
]);

// Totals
$retenciones->addTotales([
    'MontoTotOperacion'  => '55578643',
    'MontoTotGrav'       => '0',
    'MontoTotExent'      => '55578643',
    'MontoTotRet'        => '0',
    'UtilidadBimestral'  => '0.00',
    'ISRCorrespondiente' => '0.00',
]);

// Withheld taxes
$retenciones->addImpRetenidos([
    'BaseRet'     => '0',
    'ImpuestoRet' => '001', // ISR
    'TipoPagoRet' => '01',
    'MontoRet'    => '0.00',
]);

// Dividends complement
$dividendos = new Dividendos();
$dividendos->addDividOUtil([
    'CveTipDivOUtil'            => '06',
    'MontISRAcredRetMexico'     => '0',
    'MontISRAcredRetExtranjero' => '0',
    'MontRetExtDivExt'          => '0',
    'TipoSocDistrDiv'           => 'Sociedad Nacional',
    'MontISRAcredNal'           => '0',
    'MontDivAcumNal'            => '0',
    'MontDivAcumExt'            => '0',
]);
$retenciones->addComplemento($dividendos);

// Certificate and signature
$creator->putCertificado(new Certificado('/certs/my_cert.cer'));
$creator->addSello('file:///certs/my_key.pem', 'my-passphrase');

// Move namespace declarations to root (SAT requirement)
$creator->moveSatDefinitionsToRetenciones();

// Validate (XSD only)
$asserts = $creator->validate();
if ($asserts->hasErrors()) {
    print_r($asserts->errors());
    exit(1);
}

// Save
file_put_contents('/output/retenciones_v2.xml', $creator->asXml());
echo 'Retenciones 2.0 document created.' . PHP_EOL;

Retenciones Version 1.0 Example

Version 1.0 documents use a slightly different attribute naming convention on Emisor, Receptor, Totales, and ImpRetenidos. The key differences:
Nodev1.0 attributev2.0 attribute
EmisorRFCEmisorRfcE
EmisorNomDenRazSocENomDenRazSocE
TotalesmontoTotOperacion (camelCase)MontoTotOperacion (PascalCase)
ImpRetenidosImpuestoImpuestoRet
PeriodoEjercEjercicio
Signing algorithmSHA-1SHA-256
<?php
use CfdiUtils\Retenciones\RetencionesCreator10;
use CfdiUtils\Certificado\Certificado;

$creator = new RetencionesCreator10([
    'FechaExp' => '2019-01-23T08:00:00-06:00',
    'CveRetenc' => '14',
]);

$retenciones = $creator->retenciones();

$retenciones->addEmisor([
    'RFCEmisor'      => 'EKU9003173C9',
    'NomDenRazSocE'  => 'ESCUELA KEMPER URGATE SA DE CV',
]);
$retenciones->getReceptor()->addExtranjero([
    'NumRegIdTrib'  => '998877665544332211',
    'NomDenRazSocR' => 'WORLD WIDE COMPANY INC',
]);
$retenciones->addPeriodo(['MesIni' => '5', 'MesFin' => '5', 'Ejerc' => '2018']);
$retenciones->addTotales([
    'montoTotOperacion' => '55578643',
    'montoTotGrav'      => '0',
    'montoTotExent'     => '55578643',
    'montoTotRet'       => '0',
]);
$retenciones->addImpRetenidos([
    'BaseRet'      => '0',
    'Impuesto'     => '01', // ISR
    'montoRet'     => '0',
    'TipoPagoRet'  => 'Pago provisional',
]);

$creator->putCertificado(new Certificado('/certs/my_cert.cer'));
$creator->addSello('file:///certs/my_key.pem', 'my-passphrase');
$creator->moveSatDefinitionsToRetenciones();

file_put_contents('/output/retenciones_v1.xml', $creator->asXml());

Reading Retenciones Documents

Learn how to read and parse an existing Retenciones XML document, extract withholding data, and validate the document structure.

Build docs developers (and LLMs) love