nfephp-org/sped-gtin
Valide GTINs (EAN-8/12/13/14) para NFe/NFCe layout 4.00 conforme NT 2021.003: verifica estrutura, prefixo 789/790, região e dígito verificador, ajudando a evitar rejeições da SEFAZ por código inválido.
Installation:
composer require nfephp-org/sped-gtin
Ensure your composer.json has "minimum-stability": "dev" if using the dev-master branch.
First Use Case: Validate a GTIN in a Laravel request or model:
use NFePHP\Gtin\Gtin;
// In a controller or service
$gtin = request('gtin');
try {
if (Gtin::check($gtin)->isValid()) {
// Proceed with NFe/NFCe generation
return response()->json(['success' => true]);
}
return response()->json(['error' => 'Invalid GTIN'], 400);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 400);
}
Where to Look First:
region, prefix, checkDigit, restricted, type (for debugging).0405).Product Onboarding Validation:
use NFePHP\Gtin\Gtin;
use Illuminate\Validation\Rule;
public function rules()
{
return [
'gtin' => [
'required',
Rule::custom(function ($attribute, $value) {
try {
return Gtin::check($value)->isValid();
} catch (\Exception $e) {
return false;
}
}),
],
];
}
class GtinValidator
{
public function validate(string $gtin): bool
{
try {
return Gtin::check($gtin)->isValid();
} catch (\Exception) {
return false;
}
}
}
NFe/NFCe Payload Sanitization:
public function boot()
{
app()->resolving('nfe.generator', function ($generator) {
$generator->setGtinValidator(new GtinValidator());
});
}
$gtin = $product->gtin;
$validator = new GtinValidator();
if (!$validator->validate($gtin)) {
throw new \InvalidArgumentException("GTIN {$gtin} rejected by SEFAZ rules.");
}
Bulk Validation:
Collect::macro('validateGtins', function () {
return $this->filter(function ($gtin) {
try {
return Gtin::check($gtin)->isValid();
} catch (\Exception) {
return false;
}
});
});
// Usage:
$validGtins = Product::pluck('gtin')->validateGtins();
Laravel Artisan Command: Validate GTINs in a CSV import:
public function handle()
{
Excel::load('products.csv', function ($reader) {
$reader->each(function ($row) {
if (!Gtin::check($row['gtin'])->isValid()) {
$this->error("Invalid GTIN: {$row['gtin']}");
}
});
});
}
Event Listeners: Log GTIN validation failures to a compliance audit table:
public function handle(ProductValidated $event)
{
if (!$event->isValid) {
GtinAudit::create([
'gtin' => $event->gtin,
'error' => $event->exception?->getMessage(),
]);
}
}
API Response Wrapping: Standardize error messages for frontend consumption:
try {
$gtin = Gtin::check($request->gtin);
return ['valid' => $gtin->isValid(), 'region' => $gtin->region];
} catch (\Exception $e) {
return ['error' => 'GTIN_'.strtoupper(str_replace(' ', '_', $e->getMessage()))];
}
Prefix Restrictions:
789 or 790 are Brazil-only. Others (e.g., 0, 30–37, 400–440) are invalid for SEFAZ and will throw:
Somente prefixos do Brasil [789, 790] são aceitáveis.
Rule::custom(function ($value) {
return Gtin::check($value)->prefix === '789' || Gtin::check($value)->prefix === '790';
});
GTIN Length Ambiguity:
class StrictGtin extends \NFePHP\Gtin\Gtin
{
public function isStrictlyValid(): bool
{
return $this->isValid() && ($this->type === 13 || $this->type === 14);
}
}
"SEM GTIN" Edge Case:
true only for the exact string "SEM GTIN" (case-sensitive)."sem gtin", "Sem GTIN", or null will throw exceptions.$normalizedGtin = strtoupper(trim($gtin ?? ''));
if ($normalizedGtin === 'SEM GTIN') {
return true;
}
return Gtin::check($normalizedGtin)->isValid();
Check Digit Calculation:
Performance with Bulk Data:
$cacheKey = "gtin_{$gtin}";
if (!Cache::has($cacheKey)) {
Cache::put($cacheKey, Gtin::check($gtin)->isValid(), now()->addHours(1));
}
Exception Mapping: Create a helper to translate exceptions to SEFAZ error codes:
function gtinExceptionToSefazCode(\Exception $e): string
{
$message = strtolower($e->getMessage());
if (str_contains($message, 'prefix invalido')) return '0405';
if (str_contains($message, 'digito verificador')) return '0406';
return '9999'; // Unknown
}
Log Validation Details:
try {
$gtin = Gtin::check($request->gtin);
\Log::info('GTIN Validation', [
'gtin' => $request->gtin,
'valid' => $gtin->isValid(),
'region' => $gtin->region,
'prefix' => $gtin->prefix,
'type' => $gtin->type,
]);
} catch (\Exception $e) {
\Log::error('GTIN Validation Failed', [
'gtin' => $request->gtin,
'error' => $e->getMessage(),
'sefas_code' => gtinExceptionToSefazCode($e),
]);
}
788):
class CustomGtin extends \NFePHP\Gtin\Gtin
{
protected function validatePrefix(): void
{
if (!in_array($this->prefix, ['788', '789', '790'])) {
throw new \InvalidArgumentException("Prefixo {$this->prefix} não permitido.");
}
}
How can I help you explore Laravel packages today?