<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jose Luis Bermejo Meléndez</title>
    <description>The latest articles on DEV Community by Jose Luis Bermejo Meléndez (@josebermejo).</description>
    <link>https://dev.to/josebermejo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4046680%2Fd8bab280-deb1-4987-a851-58e411b98f7a.jpg</url>
      <title>DEV Community: Jose Luis Bermejo Meléndez</title>
      <link>https://dev.to/josebermejo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/josebermejo"/>
    <language>en</language>
    <item>
      <title>How to Validate DNI, NIE, and Passports in PHP, Laravel &amp; Symfony Projects</title>
      <dc:creator>Jose Luis Bermejo Meléndez</dc:creator>
      <pubDate>Wed, 29 Jul 2026 18:06:13 +0000</pubDate>
      <link>https://dev.to/josebermejo/how-to-validate-dni-nie-and-passports-in-php-laravel-symfony-projects-5b5n</link>
      <guid>https://dev.to/josebermejo/how-to-validate-dni-nie-and-passports-in-php-laravel-symfony-projects-5b5n</guid>
      <description>&lt;h1&gt;
  
  
  Luhnify SDK for PHP
&lt;/h1&gt;

&lt;p&gt;Validating national identity documents (such as Spain's DNI, NIE, or Passports) in web applications can quickly become a maintenance headache. &lt;/p&gt;

&lt;p&gt;Maintaining custom regular expressions or checksum algorithms per country across different frameworks adds unnecessary technical debt. &lt;/p&gt;

&lt;p&gt;The official &lt;strong&gt;&lt;code&gt;luhnify/luhnify-php&lt;/code&gt;&lt;/strong&gt; SDK provides a unified, zero-PII, stateless API to validate official identity documents natively in &lt;strong&gt;Laravel&lt;/strong&gt;, &lt;strong&gt;Symfony&lt;/strong&gt;, or &lt;strong&gt;Vanilla PHP&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 1. Base Installation
&lt;/h2&gt;

&lt;p&gt;Add the package to your project using Composer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require luhnify/luhnify-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📦 2. Environment Configuration
&lt;/h2&gt;

&lt;p&gt;Depending on your application's architecture, choose your project type below to complete the setup in a single step:&lt;/p&gt;

&lt;h3&gt;
  
  
  🔹 Option A: If you are using Laravel (Automatic)
&lt;/h3&gt;

&lt;p&gt;The SDK features &lt;em&gt;Package Autodiscovery&lt;/em&gt;, meaning Laravel will automatically detect the package without requiring you to register Service Providers manually.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the installation wizard:&lt;/strong&gt;
Execute the following Artisan command in your terminal:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan luhnify:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What this command does for you:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Securely prompts you for your private Luhnify API Key (&lt;code&gt;X-API-Key&lt;/code&gt;) in the console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatically injects&lt;/strong&gt; the &lt;code&gt;LUHNIFY_API_KEY=your_key&lt;/code&gt; variable at the end of your &lt;code&gt;.env&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Publishes the framework configuration file to &lt;code&gt;config/luhnify.php&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Registers the client as a &lt;code&gt;singleton&lt;/code&gt; in the service container, ready for Dependency Injection.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  🔹 Option B: If you are using Symfony (Automatic Script)
&lt;/h3&gt;

&lt;p&gt;Thanks to the built-in installer hooked into Composer's lifecycle, the SDK auto-configures itself upon download without requiring manual YAML service mapping.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Check your project files:&lt;/strong&gt;&lt;br&gt;
After running &lt;code&gt;composer require&lt;/code&gt;, the SDK will automatically create two things in your project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A global configuration file at &lt;code&gt;config/packages/luhnify.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An environment variable block at the bottom of your &lt;code&gt;.env&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add your API Key:&lt;/strong&gt;&lt;br&gt;
Open your &lt;code&gt;.env&lt;/code&gt; file and replace the placeholder value with your real credential:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env
###&amp;gt; luhnify/luhnify-php ###
LUHNIFY_API_KEY=your_real_luhnify_api_key_here
###&amp;lt; luhnify/luhnify-php ###
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! Symfony's Autowiring now automatically knows how to instantiate and inject the client into your controllers using this variable.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔹 Option C: Native PHP / Vanilla PHP (Manual)
&lt;/h3&gt;

&lt;p&gt;If you are not using a framework or are working on a custom legacy setup, initialization is performed explicitly at your application's entry point.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Require the Composer Autoload&lt;/strong&gt; at the top of your script.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Instantiate the client manually:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;require_once&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Luhnify\ValidationSdk\ValidationClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// It is highly recommended to read the key from system environment variables&lt;/span&gt;
&lt;span class="nv"&gt;$apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LUHNIFY_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s1"&gt;'your_direct_api_key_here'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$luhnify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidationClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$apiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⚡ 3. Quick Usage Example
&lt;/h2&gt;

&lt;p&gt;Once configured, performing a document validation is exactly the same across all environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Luhnify\ValidationSdk\ValidationPayload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Luhnify\ValidationSdk\LuhnifyException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Prepare the document data you want to validate&lt;/span&gt;
&lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidationPayload&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'country_code'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'es'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'document_type'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'dni'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'document_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'12345678Z'&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 2. Execute validation against the official API&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$luhnify&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;ValidateDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Success: The document is valid."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Warning: The document did not pass validation rules."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LuhnifyException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Luhnify API Error (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getStatusCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;): "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unexpected system error: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔒 Security &amp;amp; Privacy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero PII Retention:&lt;/strong&gt; Validation is stateless—document numbers are processed in memory and never persisted to a database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment Isolation:&lt;/strong&gt; Keep your LUHNIFY_API_KEY secure using .env files and avoid committing keys to version control.&lt;/li&gt;
&lt;li&gt;All requests run securely over HTTPS targeting &lt;a href="https://api.luhnify.com/v1" rel="noopener noreferrer"&gt;https://api.luhnify.com/v1&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Useful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dashboard &amp;amp; API Keys:&lt;/strong&gt; &lt;a href="https://Luhnify.com" rel="noopener noreferrer"&gt;Luhnify.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/Luhnify/luhnify-php" rel="noopener noreferrer"&gt;Luhnify PHP SDK&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>symfony</category>
      <category>api</category>
    </item>
    <item>
      <title>How to Validate DNI, NIE, CIF, and International Tax IDs in Your Backend</title>
      <dc:creator>Jose Luis Bermejo Meléndez</dc:creator>
      <pubDate>Sat, 25 Jul 2026 10:30:05 +0000</pubDate>
      <link>https://dev.to/josebermejo/how-to-validate-dni-nie-cif-and-international-tax-ids-in-your-backend-2b9f</link>
      <guid>https://dev.to/josebermejo/how-to-validate-dni-nie-cif-and-international-tax-ids-in-your-backend-2b9f</guid>
      <description>&lt;h3&gt;
  
  
  Stop writing complex custom regexes for every country — validate format structures and checksums with a single lightweight API call.
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Global ID format validation architecture
&lt;/h2&gt;

&lt;p&gt;Handling identity document validation across international markets is a recurring pain point for software engineering teams. When onboarding users, entering invalid IDs or typos leads to corrupted database entries, failed billing pipelines, and compliance headaches. Writing custom regular expressions (regex) or local checksum algorithms (like Luhn or specific country rules) for dozens of countries quickly becomes an unmaintainable mess.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Format Validation at the Input Stage
&lt;/h2&gt;

&lt;p&gt;Instead of building and updating validation logic for every nation from scratch, you can offload structure and checksum validation to Luhnify, a free, lightweight API designed for real-time format verification across 200+ countries.&lt;/p&gt;

&lt;p&gt;It checks the structure, character rules, and mathematical validity (checksums) without storing sensitive payload data or requiring slow biometric KYC processes.&lt;br&gt;
Quick Integration Example&lt;/p&gt;

&lt;p&gt;Here is how simple it is to validate an international ID using a single HTTP request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.luhnify.com/api/v1/validate &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-KEY: your_api_key"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "document_number": "12345678A",
    "document_type": "dni",
    "country_code": "ES"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You receive a clean JSON payload indicating whether the format and checksum are valid, allowing your frontend or backend to immediately accept or prompt the user for corrections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Document is valid."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ultra-fast Performance:&lt;/strong&gt; Average response time under 50 ms, perfect for real-time form validation without slowing down user onboarding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;200+ Countries supported:&lt;/strong&gt; Standardized responses regardless of nationality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8 Official SDKs:&lt;/strong&gt; Ready-to-use packages for PHP (Laravel, Symfony, native PHP), TypeScript, Java, Go, C#, Swift, Kotlin, and Python. Drop it into your stack with zero setup friction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero PII Storage:&lt;/strong&gt; High performance without unnecessary data retention risks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer First:&lt;/strong&gt; Built by engineers to keep onboarding forms clean and database inputs valid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try it out for free at &lt;a href="https://luhnify.com" rel="noopener noreferrer"&gt;luhnify.com&lt;/a&gt; and keep your registration flow clean!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>programming</category>
      <category>software</category>
    </item>
  </channel>
</rss>
