<?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: China Sourcing Guard</title>
    <description>The latest articles on DEV Community by China Sourcing Guard (@chinasourcingguard).</description>
    <link>https://dev.to/chinasourcingguard</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%2F4218742%2Fb7074822-22e5-4e00-bd54-fac16233ce8c.png</url>
      <title>DEV Community: China Sourcing Guard</title>
      <link>https://dev.to/chinasourcingguard</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chinasourcingguard"/>
    <language>en</language>
    <item>
      <title>Validating Chinese Business License Codes (USCC) in TypeScript</title>
      <dc:creator>China Sourcing Guard</dc:creator>
      <pubDate>Sat, 31 Oct 2026 09:48:21 +0000</pubDate>
      <link>https://dev.to/chinasourcingguard/validating-chinese-business-license-codes-uscc-in-typescript-1g35</link>
      <guid>https://dev.to/chinasourcingguard/validating-chinese-business-license-codes-uscc-in-typescript-1g35</guid>
      <description>&lt;p&gt;If you build anything that onboards Chinese companies (B2B marketplaces, KYB/KYC flows, procurement tools, invoicing, supplier portals), sooner or later a form asks for a &lt;strong&gt;统一社会信用代码&lt;/strong&gt;, the &lt;em&gt;Unified Social Credit Code&lt;/em&gt; (USCC). It's China's equivalent of a company registration number + tax ID in one.&lt;/p&gt;

&lt;p&gt;It is also one of those identifiers that people paste with spaces, lowercase letters, a stray &lt;code&gt;O&lt;/code&gt; instead of &lt;code&gt;0&lt;/code&gt;, or simply make up. The good news: it has a checksum, so you can reject a lot of garbage at the form layer before it hits your database or a manual review queue.&lt;/p&gt;

&lt;p&gt;This post covers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The structure of the 18-character code&lt;/li&gt;
&lt;li&gt;The GB 32100-2015 check-digit algorithm (with a from-scratch TypeScript implementation)&lt;/li&gt;
&lt;li&gt;Using the open-source &lt;a href="https://github.com/steplead/uscc-validator" rel="noopener noreferrer"&gt;&lt;code&gt;uscc-validator&lt;/code&gt;&lt;/a&gt; library to validate &lt;em&gt;and decode&lt;/em&gt; codes&lt;/li&gt;
&lt;li&gt;Wiring it into Zod / a form&lt;/li&gt;
&lt;li&gt;What a checksum &lt;strong&gt;can't&lt;/strong&gt; tell you&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Anatomy of a USCC
&lt;/h2&gt;

&lt;p&gt;Since 2015 every legal entity in mainland China gets one, defined by national standard &lt;strong&gt;GB 32100-2015&lt;/strong&gt;. It's printed on the business license (营业执照).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;9 1 440300 MA5XXXXXX X
│ │   │        │      └─ 18: check digit (mod 31)
│ │   │        └──────── 9–17: organization code (GB 11714)
│ │   └───────────────── 3–8: administrative division (GB/T 2260), e.g. 440300 = Shenzhen
│ └───────────────────── 2: organization type (under "9": 1 = enterprise, 2 = individual business)
└─────────────────────── 1: registration authority (9 = market regulation / SAMR)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The alphabet is &lt;code&gt;0-9&lt;/code&gt; plus uppercase letters &lt;strong&gt;excluding &lt;code&gt;I&lt;/code&gt;, &lt;code&gt;O&lt;/code&gt;, &lt;code&gt;S&lt;/code&gt;, &lt;code&gt;V&lt;/code&gt;, &lt;code&gt;Z&lt;/code&gt;&lt;/strong&gt;, so 31 symbols in total:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USCC_CHARSET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0123456789ABCDEFGHJKLMNPQRTUWXY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A neat practical detail: the first two characters are business-relevant. &lt;code&gt;91…&lt;/code&gt; is a company; &lt;code&gt;92…&lt;/code&gt; is an 个体工商户 (individual / sole-trader business). If your users are buyers vetting suppliers, surfacing "this is a sole trader" next to a supplier claiming 500 employees is genuinely useful UX.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The check digit, from scratch
&lt;/h2&gt;

&lt;p&gt;Each of the first 17 characters is mapped to its index in the charset, multiplied by a fixed positional weight, summed, and reduced mod 31.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CHARSET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0123456789ABCDEFGHJKLMNPQRTUWXY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WEIGHTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;first17&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&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="nx"&gt;first17&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CHARSET&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;first17&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// illegal character (I, O, S, V, Z, lowercase…)&lt;/span&gt;
    &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;WEIGHTS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;CHARSET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isValidUscc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;0-9A-HJ-NPQRTUWXY&lt;/span&gt;&lt;span class="se"&gt;]{18}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;checkDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;17&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;p&gt;(The weights are &lt;code&gt;3^i mod 31&lt;/code&gt;, which is why they look random.)&lt;/p&gt;

&lt;p&gt;That's ~20 lines and it works. But in production you quickly want more: input normalization, human-readable errors, decoding the authority/type/region, and the secondary GB 11714 org-code check. That's what the library is for.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Using &lt;code&gt;uscc-validator&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/steplead/uscc-validator" rel="noopener noreferrer"&gt;&lt;code&gt;uscc-validator&lt;/code&gt;&lt;/a&gt; is MIT-licensed, zero-dependency, pure functions, no network calls, and runs in Node and the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install straight from GitHub&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;github:steplead/uscc-validator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;validateUscc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;computeUsccCheckDigit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uscc-validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validateUscc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;91110000802100433B&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   formatOk: true,&lt;/span&gt;
&lt;span class="c1"&gt;//   checkDigitOk: true,&lt;/span&gt;
&lt;span class="c1"&gt;//   authority: 'Market regulation authority (SAMR / AIC) — commercial companies',&lt;/span&gt;
&lt;span class="c1"&gt;//   orgType: 'Enterprise (company)',&lt;/span&gt;
&lt;span class="c1"&gt;//   isCompany: true,&lt;/span&gt;
&lt;span class="c1"&gt;//   isSoleTrader: false,&lt;/span&gt;
&lt;span class="c1"&gt;//   regionCode: '110000',&lt;/span&gt;
&lt;span class="c1"&gt;//   province: 'Beijing',&lt;/span&gt;
&lt;span class="c1"&gt;//   city: 'Beijing',&lt;/span&gt;
&lt;span class="c1"&gt;//   orgCode: '802100433',&lt;/span&gt;
&lt;span class="c1"&gt;//   errors: []&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="nf"&gt;validateUscc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;914403001922038217&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ['Check digit mismatch: the 18th character should be "6" but is "7". ...']&lt;/span&gt;

&lt;span class="nf"&gt;computeUsccCheckDigit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;91110000802100433&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 'B'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things it handles that the naive version doesn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Normalization&lt;/strong&gt;: strips spaces/dashes, upper-cases, and fixes the classic &lt;code&gt;O&lt;/code&gt;→&lt;code&gt;0&lt;/code&gt; and &lt;code&gt;I&lt;/code&gt;→&lt;code&gt;1&lt;/code&gt; typos. Users copy codes from photos of paper licenses and OCR output, so this matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actionable errors&lt;/strong&gt;: "the 18th character should be 6" is something a user can act on, and it often reveals a single-character transcription mistake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoding&lt;/strong&gt;: authority, org type, &lt;code&gt;isCompany&lt;/code&gt; / &lt;code&gt;isSoleTrader&lt;/code&gt;, province/city for common manufacturing hubs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;orgCodeCheckOk&lt;/code&gt;&lt;/strong&gt;: the secondary GB 11714 check on positions 9–17 (only meaningful for converted legacy codes; newer &lt;code&gt;MA…&lt;/code&gt; codes may not satisfy it, so treat it as a soft signal).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Plugging it into a form schema
&lt;/h2&gt;

&lt;p&gt;With Zod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;validateUscc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uscc-validator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SupplierSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;legalNameZh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;uscc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;validateUscc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;superRefine&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formatOk&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checkDigitOk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addIssue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZodIssueCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid USCC&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isSoleTrader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSoleTrader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;province&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;province&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;p&gt;Store the &lt;strong&gt;normalized&lt;/strong&gt; code, not the raw input, so de-duplication and lookups work.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. What a valid checksum does &lt;em&gt;not&lt;/em&gt; mean
&lt;/h2&gt;

&lt;p&gt;This is the part worth putting in your UI copy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A correct check digit means the code is &lt;strong&gt;well-formed&lt;/strong&gt;. It does not mean the company exists.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Anyone can generate a checksum-valid code in one line. The library can't tell you the company name, legal representative, status (active / revoked / deregistered) or business scope. For that you need the official registry, &lt;strong&gt;GSXT&lt;/strong&gt; (国家企业信用信息公示系统, gsxt.gov.cn), which has no public API, is Chinese-only, and sits behind a captcha. So a sensible pipeline looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Offline validation&lt;/strong&gt; (this library) at input time: instant, free, catches typos and fabricated codes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registry confirmation&lt;/strong&gt; (manual or via a licensed data provider): name, status, scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business-rule checks&lt;/strong&gt;: does the Chinese legal name on the registry match the contract and the bank beneficiary?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want to see the same logic in a UI before integrating it, there's a browser-based &lt;a href="https://chinasourcingguard.com/tools/china-business-license-checker" rel="noopener noreferrer"&gt;China business license checker&lt;/a&gt; built on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;USCC = 18 chars, 31-symbol alphabet, weighted mod-31 check digit.&lt;/li&gt;
&lt;li&gt;Validate and normalize at the edge; store the normalized code.&lt;/li&gt;
&lt;li&gt;Surface decoded hints (sole trader vs. company, region) because they're cheap and useful.&lt;/li&gt;
&lt;li&gt;Never present "valid checksum" as "verified company".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issues and PRs are welcome on &lt;a href="https://github.com/steplead/uscc-validator" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, especially for the region table, which covers provinces plus major manufacturing prefectures but not the full GB/T 2260 list yet.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://chinasourcingguard.hashnode.dev/how-to-verify-a-chinese-business-license-uscc-before-paying-a-supplier" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;, where it's written for buyers vetting suppliers rather than developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>validation</category>
      <category>china</category>
    </item>
  </channel>
</rss>
