Skip to content

Commit

Permalink
docs: inline documentation and readme (#34)
Browse files Browse the repository at this point in the history
### Why?

Prepare the crate to be used by others by adding documentation on using
the library.

### What changed?
- Inline documentation
- README
  • Loading branch information
Mollemoll authored May 16, 2024
1 parent 773a420 commit 78d802c
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 9 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
name = "tax_ids"
version = "0.1.0"
edition = "2021"
authors = ["Jonas Molander"]
description = "A library to validate and verify Tax Ids. Handle European, British, Norwegian or Swiss VAT numbers."
license = "MIT OR Apache-2.0"
repository = "https://github.com/Mollemoll/tax-ids"
keywords = ["tax", "vat", "vies", "eu"]
categories = ["finance", "api-bindings", "localization", "parser-implementations"]

[dependencies]
lazy_static = "1.4.0"
Expand All @@ -14,7 +19,6 @@ serde_json = "1.0.116"
thiserror = "1.0.60"
toml = { version = "0.8.12", optional = true }


[features]
default = ["eu_vat"]
eu_vat = ["roxmltree"]
Expand Down
157 changes: 149 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,159 @@
# tax-ids
# Tax Ids

## License
This crate offers a solution for validating tax IDs (VAT/GST) for businesses operating within the European Union,
the United Kingdom, Switzerland, and Norway.

Currently, the library provides the following functionalities:
- Validates the syntax of a tax ID against its type-specific regex pattern.
- Verifies the tax ID in the relevant government database (based on the tax ID type).

The library has been inspired by the [valvat](https://github.com/yolk/valvat) library for Ruby.

### Available features / tax id types

| Feature | Description | Default |
|----------|--------------------|---------|
| `eu_vat` | European Union VAT ||
| `gb_vat` | United Kingdom VAT | |
| `ch_vat` | Switzerland VAT | |
| `no_vat` | Norway VAT | |

More info at [Tax Id Types](#tax-id-types).

### Installation

With default feature `eu_vat`:
```toml
[dependencies]
tax_ids = "0.1.0"
```

With `eu_vat` and `gb_vat` features enabled:
```toml
[dependencies]
tax_ids = { version = "0.1.0", features = ["eu_vat", "gb_vat"] }
```

## Usage

```rust
use tax_ids::TaxId;
use tax_ids::VerificationStatus::{Verified, Unverified, Unavailable};
use tax_ids::UnavailableReason::{ServiceUnavailable, Timeout, Block, RateLimit};

fn main() {
// Instantiate a new TaxId object. This can raise a ValidationError.
let tax_id = match TaxId::new("SE556703748501") {
Ok(tax_id) => tax_id,
Err(e) => {
println!("ValidationError: {}", e);
return;
}
};

assert_eq!(tax_id.value(), "SE556703748501");
assert_eq!(tax_id.country_code(), "SE");
assert_eq!(tax_id.tax_country_code(), "SE");
assert_eq!(tax_id.local_value(), "556703748501");
assert_eq!(tax_id.tax_id_type(), "eu_vat");

// The country code is the 2-char ISO code of the country.
// It's often the same as the tax country code, but not always.
// For example, the country code for Greece is GR, but EL for the Greek VAT number.

// The United Kingdom has a country code GB and tax country code GB.
// However, due to Brexit, businesses in Northern Ireland
// have a country code GB but use VAT number/tax country code XI when trading
// with the EU.

// Verification

// Perform a verification request against the country's tax ID database.
// This can raise a VerificationError.
let verification = match tax_id.verify() {
Ok(verification) => verification,
Err(e) => {
println!("VerificationError: {}", e);
return;
}
};

assert_eq!(verification.status(), &Verified);

// VerificationStatus can take one out of three different statuses:
// - Verified - The tax ID is legitimate.
// - Unverified - The tax ID is not legitimate.
// - Unavailable(UnavailableReason) - The verification couldn't be performed due to some reason.

// These statuses are what you want to act upon.
match verification.status() {
Verified => {
// Proceed with payment
}
Unverified => {
// Ask the customer to provide a proper tax ID
}
Unavailable(reason) => {
// Process payment and verify the tax ID later?

match reason {
ServiceUnavailable | Timeout => {},
Block => {
// Adapt to your IP / VAT being blocked
}
RateLimit => {
// Consider how to avoid rate limiting
}
}
}
}

// The full verification object:

println!("{:?}", verification);

// The data field is experimental and subject to change or removal.
// It will contain different data depending on what tax ID type is being verified.
// And what response the verification service provides.

// Verification status: Verified
// Verification {
// performed_at: 2024-05-15T14:38:31.388914+02:00,
// status: Verified,
// data: Object {
// "address": String("REGERINGSGATAN 19 \n111 53 STOCKHOLM"),
// "countryCode": String("SE"),
// "name": String("Spotify AB"),
// "requestDate": String("2024-05-15+02:00"),
// "valid": String("true"),
// "vatNumber": String("556703748501"
// )}
// }
}
```

### Tax Id Types

| Tax Id Type | Authority | Manual lookup | Documentation |
|-------------|-------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `eu_vat` | [VIES](https://ec.europa.eu/taxation_customs/vies/#/faq) | [🔍](https://ec.europa.eu/taxation_customs/vies/) | [📖](https://ec.europa.eu/taxation_customs/vies/#/technical-information) + [Availability](https://ec.europa.eu/taxation_customs/vies/#/help) |
| `gb_vat` | [HMRC](https://www.gov.uk/government/organisations/hm-revenue-customs) | [🔍](https://www.tax.service.gov.uk/check-vat-number/enter-vat-details) | [📖](https://developer.service.hmrc.gov.uk/api-documentation/docs/api/service/vat-registered-companies-api/1.0/oas/page) |
| `ch_vat` | [BFS](https://www.bfs.admin.ch/bfs/en/home/registers/enterprise-register/business-enterprise-register.html) | [🔍](https://www.uid.admin.ch/Search.aspx?lang=en) | [📖](https://www.bfs.admin.ch/bfs/fr/home/registres/registre-entreprises/numero-identification-entreprises/registre-ide/interfaces-ide.assetdetail.11007266.html) |
| `no_vat` | [Brønnøysundregistrene](https://www.brreg.no/) | [🔍](https://data.brreg.no/enhetsregisteret/oppslag/enheter) | [📖](https://data.brreg.no/enhetsregisteret/api/dokumentasjon/no/index.html#tag/Enheter/operation/hentEnhet) |

### License

Licensed under either of

* Apache License, Version 2.0
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
- Apache License, Version 2.0
([LICENSE-APACHE](https://github.com/Mollemoll/tax-ids?tab=Apache-2.0-1-ov-file) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license
([LICENSE-MIT](https://github.com/Mollemoll/tax-ids?tab=MIT-2-ov-file) or <http://opensource.org/licenses/MIT>)

at your option.

## Contribution
### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
dual licensed as above, without any additional terms or conditions.
2 changes: 2 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use std::fmt::Debug;
#[derive(thiserror::Error, Debug, PartialEq)]
pub enum ValidationError {
#[error("Country code {0} is not supported")]
/// The country code is not supported
UnsupportedCountryCode(String),

#[error("Invalid syntax")]
/// The syntax of the tax id is invalid for the given country
InvalidSyntax,
}

Expand Down
20 changes: 20 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![doc = include_str!("../README.md")]

mod errors;
mod verification;
mod syntax;
Expand Down Expand Up @@ -66,6 +68,8 @@ impl fmt::Debug for TaxId {
}

impl TaxId {
/// Use this associated function to validate the syntax of a given tax id number against
/// its country-specific regex pattern without creating any TaxId.
pub fn validate_syntax(value: &str) -> Result<(), ValidationError> {
let tax_country_code = &value[0..2];
SYNTAX.get(tax_country_code)
Expand All @@ -79,6 +83,9 @@ impl TaxId {
})
}

/// Constructs a TaxId after validating its syntax based on the country-specific regex pattern.
/// If the syntax validation is successful, the returned TaxId can be used for further
/// verification against the corresponding government database.
pub fn new(value: &str) -> Result<TaxId, ValidationError> {
let tax_country_code = &value[0..2];
let local_value = &value[2..];
Expand Down Expand Up @@ -106,15 +113,28 @@ impl TaxId {
})
}

/// Performs a request to verify the tax id against the corresponding government database.
pub fn verify(&self) -> Result<Verification, VerificationError> {
self.id_type.verifier().verify(self)
}

/// Returns the full tax id value. IE: SE556703748501
pub fn value(&self) -> &str { &self.value }
/// Returns the country code. IE: SE
pub fn country_code(&self) -> &str { &self.country_code }
/// Returns the tax country code. IE: SE
///
/// This is the same as the country code for most countries, but not for XI and EL.
///
/// XI is the tax country code that Northern Ireland business (the United Kingdom) should use
/// while trading with the EU. A consequence of Brexit.
///
/// EL is the tax country code for Greece.
pub fn tax_country_code(&self) -> &str { &self.tax_country_code }
/// Returns the local value of the tax id. IE: 556703748501
pub fn local_value(&self) -> &str { &self.local_value }

/// Returns the type of tax id in snake_case. IE: eu_vat, gb_vat, ch_va or no_vat
pub fn tax_id_type(&self) -> &str { self.id_type.name() }
fn id_type(&self) -> &Box<dyn TaxIdType> { &self.id_type }
}
Expand Down
Empty file removed src/tax_id.rs
Empty file.
18 changes: 18 additions & 0 deletions src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ impl VerificationResponse {

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum VerificationStatus {
/// Represents a successful verification where the government database confirmed the ID as legitimate.
Verified,
/// Represents an unsuccessful verification where the government database identified the ID as illegitimate.
Unverified,
/// Represents a case where verification was not possible due to certain reasons (e.g., government database was unavailable).
Unavailable(UnavailableReason),
}

Expand All @@ -43,6 +46,7 @@ pub struct Verification {
}

impl Verification {
#[doc(hidden)]
pub fn new(status: VerificationStatus, data: serde_json::Value) -> Verification {
Verification {
performed_at: Local::now(),
Expand All @@ -51,7 +55,21 @@ impl Verification {
}
}

/// This VerificationStatus is what the crate user should use to determine how to proceed.
///
/// A checkout example:
/// - Enable/process the transaction upon `VerificationStatus::Verified`.
/// - Block transaction/provide a validation msg upon `VerificationStatus::Unverified`.
/// - Enable/process the transaction upon `VerificationStatus::Unavailable` but perform a
/// re-verification at a later stage.
pub fn status(&self) -> &VerificationStatus { &self.status }
/// Additional data selected by the crate owner from the government database response.
/// This data can be used to provide more context about the verification.
/// The data is in JSON format.
///
/// Includes error details in case of an unsuccessful verification.
///
/// Subject to change in future versions.
pub fn data(&self) -> &serde_json::Value { &self.data }
}

Expand Down

0 comments on commit 78d802c

Please sign in to comment.