Skip to content

08. Montagem da DPS

Tiago S. P. Rodrigues edited this page Dec 22, 2025 · 1 revision

Montagem da DPS

Este documento demonstra o fluxo de transformação de dados, desde o payload simplificado que recebemos na API, até a geração do XML final da DPS (Documento Provisório de Serviço) que será assinado e transmitido para a API Nacional de NFS-e.

1. Entrada de Dados (API Payload)

A API foi desenhada para receber um JSON "amigável", abstraindo a complexidade das siglas e estruturas da Receita Federal. O uso de palavras em inglês é mera conveniência, e pode ser alterado no NfseMapper.php se desejado.

Exemplo de Payload de Entrada:

{
    "company_id": 1,
    "customer": {
        "name": "Empresa Cliente LTDA",
        "cpfCnpj": "12345678000199",
        "address": {
            "street": "Av. Paulista",
            "number": "1000",
            "zip_code": "01310100",
            "city_code": "3550308",
            "country_code": "1058"
        }
    },
    "service": {
        "code": "1.05",
        "description": "Licenciamento de Software",
        "nbs_code": "123456789",
        "amount": 1500.00,
        "tax_rate": 0
    }
}

2. Normalização de Dados (NfseMapper.php)

O primeiro passo é converter esse JSON amigável para a estrutura interna de arrays que espelha a estrutura do XML da NFS-e. Isso é feito pela classe App\Services\Nfse\NfseMapper.

O objetivo é desacoplar a interface pública da API das regras de nomenclatura do governo (ex: xNome, cTribNac), que não possuem uma nomenclatura amigável, dificultando a integração.

Exemplo no Código (NfseMapper.php):

public function toInternal(array $data): array
{
    // ... extração dos dados ...

    // Mapeamento para siglas da NFS-e
    $tomador = [
        'xNome' => $customer['name'],
        'cpfCnpj' => $customer['cpfCnpj'] ?? null,
        // ...
    ];

    $servico = [
        'cTribNac' => $service['code'], // Código de Tributação Nacional
        'xDescServ' => $service['description'],
        'cNBS' => $service['nbs_code'],
        // ...
    ];

    return [
        'tomador' => $tomador,
        'valores' => $valores,
        'servico' => Tools::removeNullValues($servico),
    ];
}

3. IMPORTANTE: Validação de Regras de Negócio (NfseValidator)

Antes de tentar gerar qualquer XML, aplicamos regras de negócio para garantir a consistência dos dados. Isso ocorre em App\Services\Nfse\NfseValidator. As regras de validação são baseadas no manual oficial da Receita Federal, localizadas no arquivo schemas/docs/anexo_i-sefin_adn-dps_nfse-snnfse-v1-00-20251210.xlsx e nos schemas XSD.

**IMPORTANTE: ** Atualmente, apenas algumas validações básicas de exemplo foram implementadas. Certifique-se de revisar e implementar todas as regras necessárias para o seu caso de uso. É provável que, com atualizações futuras, mais regras sejam adicionadas ao código fonte de exemplo incluso neste material. Algumas regras geralmente são validadas pelo próprio webservice, mas é recomendável validar o máximo possível antes de enviar a requisição.

Exemplo no Código (NfseValidator.php):

protected function validateCustomer(array $data): void
{
    $isExterior = isset($tomador['endereco']['cPais']) && $tomador['endereco']['cPais'] != '1058';

    if ($isExterior) {
        if (!empty($tomador['cpfCnpj'])) {
            throw new Exception("Para tomador no exterior, não deve ser informado CPF/CNPJ.");
        }
    } else {
        if (empty($tomador['cpfCnpj'])) {
            throw new Exception("CPF ou CNPJ do tomador é obrigatório para tomadores nacionais.");
        }
    }
}

4. Construção do XML (XmlBuilderService)

Esta é a etapa mais crítica. A classe App\Services\Nfse\XmlBuilderService transforma o array interno em uma string XML compatível com o padrão DPS (Documento Provisório de Serviço).

4.1. Geração do ID da DPS

O atributo Id da tag infDPS segue uma regra estrita de concatenação: DPS + CMun(7) + TipoInscr(1) + CNPJ(14) + Serie(5) + Numero(15).

public function generateDpsId(int $dpsNumber, string $dpsSeries): string
{
    // ...
    return "DPS{$cMun}{$tipoInscr}{$cnpj}{$serie}{$nDps}";
}

4.2. Montagem da Estrutura

Utilizamos a biblioteca spatie/array-to-xml para converter o array PHP. A estrutura deve seguir fielmente o XSD. Até mesmo a ordem dos elementos é importante..

$dpsArray = [
    'infDPS' => [
        '_attributes' => ['Id' => $infDpsId],
        'tpAmb' => $this->company->environment->value, // 1=Produção, 2=Homologação
        'dhEmi' => $issueDate,
        'prest' => [ ... ], // Dados do Prestador (Sua empresa)
        'toma' => $this->buildCustomer($data['tomador']), // Dados do Tomador
        'serv' => $this->buildService($data), // Dados do Serviço
        'valores' => $this->buildValues($data['valores']), // Valores monetários
    ]
];

5. Validação de Schema XSD (XmlValidatorService)

Após gerar o XML e assiná-lo, o XML é validado contra os arquivos XSD oficiais da Receita Federal.

A classe App\Services\Nfse\XmlValidatorService realiza este trabalho.

5.1. Hack Necessário para correção dos schemas

Os arquivos XSD oficiais (tiposSimples_v1.00.xsd) contêm expressões regulares (Regex) que funcionam em validadores de algumas linguagens como Java/.NET, mas falham no libxml do PHP.

O método prepareSchemas cria uma cópia dos schemas em cache e aplica correções:

// Substitui regex incompatível com PHP
$content = str_replace(
    'pattern value="^(?!0{1,5}$)\d{1,5}$"',
    'pattern value="[0-9]{1,5}"',
    $content
);

Outra correção é que, às vezes, o schema XSD contém um padrão de versão incorreto. Por exemplo, apesar da versão atual da NFS-e ser 1.01, o schema pode conter o pattern 1.\00, o que fará com que a validação falhe. Por isso, modificamos o validador para trocar o pattern pela versão atual configurada no sistema:

$content = str_replace(
    '<xs:pattern value="1\.00"/>',
    '<xs:pattern value="' . config('services.nfse.version') . '"/>',
    $content
);

Outra forma de corrigir seria simplesmente corrigir os arquivos XSD manualmente, mas isso não é recomendado, pois a cada atualização oficial você teria que repetir o processo.

Os XSD corrigidos são armazenados em cache na pasta storage/app/schemas_cache/ para evitar retrabalho. É recomendado limpar o cache sempre que atualizar os schemas oficiais, ou sempre que realizar deploys da aplicação, com o comando:

php artisan nfse:clear-schemas

5.2. Validação do DOM

Utilizamos a função nativa schemaValidate do PHP.

$dom->loadXML($xmlContent);
if (!$dom->schemaValidate($schemaFile)) {
    throw new Exception("Erro de validação do Schema XSD: ...");
}

Se o código passar por esta etapa, o XML geralmente está pronto para ser transmitido para a API Nacional.