Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Correios Api Laravel Package

marcelofecchio/correios-api

Laravel/PHP package that provides a clean integration with the Correios API for Brazilian e-commerce applications. Simplifies shipping rate calculations, delivery tracking, and other Correios services through a configurable wrapper that reduces boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

This package wraps the Correios API (pre-postagem, DC-e, rastreamento, processamento assíncrono) in Laravel-native abstractions. Start by publishing the config and setting your remetente_padrao, since every pre-postagem inherits from it:

php artisan vendor:publish --tag=correios-config
// config/correios.php
'remetente_padrao' => [
    'nome' => '...',
    'cnpj' => '...',
    'inscricao_estadual' => '...',
    'contribuinte_icms' => true, // new in v0.5.0
],

Then read, in this order:

  1. README.md → section "DC-e e remetente contribuinte do ICMS" (added in v0.5.0). It enumerates which fiscal document applies to each remetente profile — this is the decision table you actually need.
  2. doc/comunicados/2026-10-dce-contribuinte-icms.md — the Correios communiqué behind Rejeição 812 and the 02/11/2026 SEFAZ validation date.
  3. CHANGELOG.md for the v0.5.0 surface area.

First use case: a remetente with an active inscrição estadual (ICMS contribuinte) shipping goods. As of v0.5.0 you must send the shipment with the NF-e key (chaveNFe), not with emiteDCe:

$prePostagem = Correios::prePostagem()->criar([
    'remetente' => $remetente,      // Pessoa with contribuinteIcms: true
    'destinatario' => $destinatario,
    'chaveNFe' => $chave,           // correct path for ICMS contribuintes
    // 'emiteDCe' => true,          // will now throw ErroValidacao
]);

Implementation Patterns

Guard at the domain level, not the controller. The package rejects emiteDCe on a contribuinteIcms: true remetente with an ErroValidacao on the emiteDCe field before the HTTP call. Treat this as a validation rule in your FormRequest / domain action so you get a friendly message instead of an exception bubbling out of a queued job:

// app/Http/Requests/PrePostagemRequest.php
public function rules(): array
{
    return [
        'emiteDCe' => [
            'boolean',
            Rule::prohibitedIf(fn () => $this->remetente()->contribuinteIcms),
        ],
    ];
}

Or, if you construct Pessoa objects directly, let the package do it and catch:

use Correios\Exceptions\ErroValidacao;

try {
    $prePostagem = Correios::prePostagem()->criar($payload);
} catch (ErroValidacao $e) {
    // $e->errors() / field: emiteDCe
    return back()->withErrors($e->errors());
}

Model contribuinteIcms as persisted state, not a request flag. Because the rule is a property of the remetente (does it have an active IE?), derive it once — from your clientes/remetentes table — and feed it into Pessoa:

$remetente = Pessoa::make([
    'nome' => $cliente->razao_social,
    'cnpj' => $cliente->cnpj,
    'inscricao_estadual' => $cliente->ie,
    'contribuinte_icms' => $cliente->ie !== null, // snake_case key
]);

Note both spellings exist: the PHP property is $contribuinteIcms, the array/config key is contribuinte_icms. Keep a single mapper (a PessoaFactory or Eloquent cast) rather than repeating the array shape in every call site.

Branch on the fiscal document, once. The cleanest pattern is a small strategy that returns either ['chaveNFe' => $chave] or ['emiteDCe' => true] for the payload:

final class DocumentoFiscal
{
    public static function paraRemetente(Pessoa $remetente, string $chave): array
    {
        return $remetente->contribuinteIcms
            ? ['chaveNFe' => $chave]
            : ['emiteDCe' => true];
    }
}

This keeps the v0.5.0 rule in one place when SEFAZ inevitably changes something else on 02/11/2026.

Handle 812 in three places, because it can surface in three. The package gives you one predicate per layer:

  • Synchronous creation → ErroContribuinteIcms (subclass of ErroApi), so existing catch (ErroApi $e) blocks keep working; add a narrower catch (ErroContribuinteIcms $e) above them if you want a specific user message.
  • Cancellation → PrePostagemCriada::recusadaPorContribuinteIcms() reads motivoCancelamento.
  • Async → ProcessamentoAssincrono::recusadaPorContribuinteIcms() scans failure messages and the contained pre-postagens.

A practical reconciliation job:

foreach ($lote->prePostagens as $prePostagem) {
    if ($prePostagem->recusadaPorContribuinteIcms()) {
        $this->marcarComoCorrigivel($prePostagem); // remetente deve usar chaveNFe
        continue;
    }
    // ... normal flow
}

if ($processamento->recusadaPorContribuinteIcms()) {
    Log::warning('Lote recusado por Rejeição 812', ['id' => $processamento->id]);
}

Use reconhece() for data you already have. When 812 arrives via a webhook, an e-mail parser, a stored motivo column, or a support ticket pasted into an admin panel, you don't have a package object — just text:

if (ErroContribuinteIcms::reconhece($textoDoCorreio)) {
    // backfill / reclassify / alert
}

That makes it a good fit for a one-off php artisan correios:reclassificar-erros command, or for backfilling historical cancellations that predate this release.

Gotchas and Tips

The exact API response format is unverified. The release notes state plainly: "O formato exato da rejeição na API ainda não foi observado." reconhece() matches on the message text or causa, so treat detection as best-effort until 02/11/2026. Log the raw payload whenever ErroApi comes back and reconhece() returns false but the message mentions DC-e — that log is how you'll discover the real shape and, eventually, report it upstream.

contribuinteIcms is opt-in, so nothing is blocked by default. Existing remetentes keep working exactly as before v0.5.0 — including the now-invalid emiteDCe path. This is a soft migration, not an automatic guard. Audit your remetente records and set the flag before 02/11/2026, otherwise SEFAZ rejects at the API and you get ErroContribuinteIcms in production instead of a validation error in your own code.

Don't infer contribuinteIcms from inscricao_estadual being merely present. An IE can be baixada/inativa; the criterion is an active IE. If your clientes table stores IE status, use it; if it doesn't, this release is a good prompt to add that field rather than guessing. The README section spells out which fiscal document goes with which case — follow it rather than improvising.

ErroValidacao and ErroContribuinteIcms are different failure modes. The former means "your payload was wrong, retrying is pointless" (a contribuinteIcms remetente sent emiteDCe). The latter means "the API rejected it with 812" — which can also happen if you marked the flag incorrectly and it slipped through, or if SEFAZ tightened the rule beyond what v0.5.0 anticipates. Don't collapse them into one handler; the first is a bug in your payload, the second is a business exception to reconcile.

Queued jobs are where this bites. Retry policies on ErroValidacao should be $dontReport / non-retryable — retrying a validation failure five times with backoff just spams logs. Add it to your job's retryUntil/failed() handling explicitly:

public function failed(Throwable $e): void
{
    if ($e instanceof ErroValidacao) {
        $this->prePostagem->marcarComoInvalida($e->errors());
    }
}

Version pin. If you're on ^0.4, this is a non-breaking addition (new class, new optional property, new predicate methods), so upgrading is cheap — but the behavioural change only activates once you set contribuinte_icms: true. Roll it out by populating the flag first, verifying in staging, then relying on it.

Extension point. ErroContribuinteIcms extends ErroApi, and the pattern of reconhece() + a typed subclass is the package's convention for specific rejections. If your operation hits another Rejeição before it's officially supported, mirror the pattern locally (a subclass with a reconhece() over your stored text) — and open an issue, since that's clearly how this package grows its error taxonomy.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource