chore: Remove OpenCorporates integration (requires paid plan for commercial use)

Keep only the genuinely free APIs:
- FCA Register (free with registration)
- SRA Register (free public API)
- GitHub (free tier sufficient)
- ORCID (free public API)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-24 15:09:34 +00:00
parent 5d2965beae
commit 9ec96d4af7
6 changed files with 0 additions and 736 deletions

View File

@@ -1,376 +0,0 @@
using Microsoft.Extensions.Logging;
using RealCV.Application.Interfaces;
using RealCV.Application.Models;
using RealCV.Infrastructure.Clients;
namespace RealCV.Infrastructure.Services;
public sealed class InternationalCompanyVerifierService : IInternationalCompanyVerifierService
{
private readonly OpenCorporatesClient _openCorporatesClient;
private readonly ILogger<InternationalCompanyVerifierService> _logger;
// Common jurisdiction codes
private static readonly Dictionary<string, string> CountryToJurisdiction = new(StringComparer.OrdinalIgnoreCase)
{
["United Kingdom"] = "gb",
["UK"] = "gb",
["England"] = "gb",
["Scotland"] = "gb",
["Wales"] = "gb",
["United States"] = "us",
["USA"] = "us",
["US"] = "us",
["Germany"] = "de",
["France"] = "fr",
["Netherlands"] = "nl",
["Ireland"] = "ie",
["Spain"] = "es",
["Italy"] = "it",
["Canada"] = "ca",
["Australia"] = "au",
["New Zealand"] = "nz",
["Singapore"] = "sg",
["Hong Kong"] = "hk",
["Japan"] = "jp",
["India"] = "in",
["China"] = "cn",
["Brazil"] = "br",
["Mexico"] = "mx",
["Switzerland"] = "ch",
["Sweden"] = "se",
["Norway"] = "no",
["Denmark"] = "dk",
["Finland"] = "fi",
["Belgium"] = "be",
["Austria"] = "at",
["Poland"] = "pl",
["Portugal"] = "pt",
["UAE"] = "ae",
["South Africa"] = "za",
};
public InternationalCompanyVerifierService(
OpenCorporatesClient openCorporatesClient,
ILogger<InternationalCompanyVerifierService> logger)
{
_openCorporatesClient = openCorporatesClient;
_logger = logger;
}
public async Task<InternationalCompanyResult> VerifyCompanyAsync(
string companyName,
string? jurisdiction = null,
DateOnly? claimedStartDate = null,
DateOnly? claimedEndDate = null)
{
try
{
_logger.LogInformation("Searching OpenCorporates for: {Company} in {Jurisdiction}",
companyName, jurisdiction ?? "all jurisdictions");
string? jurisdictionCode = null;
if (!string.IsNullOrEmpty(jurisdiction) && CountryToJurisdiction.TryGetValue(jurisdiction, out var code))
{
jurisdictionCode = code;
}
else if (!string.IsNullOrEmpty(jurisdiction) && jurisdiction.Length == 2)
{
jurisdictionCode = jurisdiction.ToLowerInvariant();
}
var searchResponse = await _openCorporatesClient.SearchCompaniesAsync(
companyName,
jurisdictionCode);
if (searchResponse?.Companies == null || searchResponse.Companies.Count == 0)
{
return new InternationalCompanyResult
{
ClaimedCompany = companyName,
ClaimedJurisdiction = jurisdiction ?? "Unknown",
IsVerified = false,
VerificationNotes = "No matching companies found in OpenCorporates"
};
}
// Find the best match
var bestMatch = FindBestMatch(companyName, searchResponse.Companies);
if (bestMatch == null)
{
return new InternationalCompanyResult
{
ClaimedCompany = companyName,
ClaimedJurisdiction = jurisdiction ?? "Unknown",
IsVerified = false,
VerificationNotes = $"Found {searchResponse.Companies.Count} results but no close name matches"
};
}
// Parse dates
var incorporationDate = ParseDate(bestMatch.IncorporationDate);
var dissolutionDate = ParseDate(bestMatch.DissolutionDate);
// Calculate match score
var matchScore = CalculateMatchScore(companyName, bestMatch.Name ?? "");
// Check for timeline issues
var flags = new List<CompanyVerificationFlag>();
if (claimedStartDate.HasValue && incorporationDate.HasValue &&
claimedStartDate.Value < incorporationDate.Value)
{
flags.Add(new CompanyVerificationFlag
{
Type = "EmploymentBeforeIncorporation",
Severity = "Critical",
Message = $"Claimed start date ({claimedStartDate:yyyy-MM-dd}) is before company incorporation ({incorporationDate:yyyy-MM-dd})",
ScoreImpact = -30
});
}
if (claimedEndDate.HasValue && dissolutionDate.HasValue &&
claimedEndDate.Value > dissolutionDate.Value)
{
flags.Add(new CompanyVerificationFlag
{
Type = "EmploymentAfterDissolution",
Severity = "Warning",
Message = $"Claimed end date ({claimedEndDate:yyyy-MM-dd}) is after company dissolution ({dissolutionDate:yyyy-MM-dd})",
ScoreImpact = -20
});
}
return new InternationalCompanyResult
{
ClaimedCompany = companyName,
ClaimedJurisdiction = jurisdiction ?? "Unknown",
IsVerified = true,
MatchedCompanyName = bestMatch.Name,
CompanyNumber = bestMatch.CompanyNumber,
Jurisdiction = GetJurisdictionName(bestMatch.JurisdictionCode),
JurisdictionCode = bestMatch.JurisdictionCode,
CompanyType = bestMatch.CompanyType,
Status = bestMatch.CurrentStatus,
IncorporationDate = incorporationDate,
DissolutionDate = dissolutionDate,
RegisteredAddress = bestMatch.RegisteredAddressInFull,
OpenCorporatesUrl = bestMatch.OpencorporatesUrl,
DataLastUpdated = bestMatch.UpdatedAt,
MatchScore = matchScore,
VerificationNotes = BuildVerificationSummary(bestMatch, searchResponse.TotalCount),
Flags = flags
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error searching OpenCorporates for: {Company}", companyName);
return new InternationalCompanyResult
{
ClaimedCompany = companyName,
ClaimedJurisdiction = jurisdiction ?? "Unknown",
IsVerified = false,
VerificationNotes = $"Error during search: {ex.Message}"
};
}
}
public async Task<List<OpenCorporatesSearchResult>> SearchCompaniesAsync(
string query,
string? jurisdiction = null)
{
try
{
string? jurisdictionCode = null;
if (!string.IsNullOrEmpty(jurisdiction) && CountryToJurisdiction.TryGetValue(jurisdiction, out var code))
{
jurisdictionCode = code;
}
var searchResponse = await _openCorporatesClient.SearchCompaniesAsync(
query,
jurisdictionCode);
if (searchResponse?.Companies == null)
{
return [];
}
return searchResponse.Companies
.Where(c => c.Company != null)
.Select(c => new OpenCorporatesSearchResult
{
CompanyName = c.Company!.Name ?? "Unknown",
CompanyNumber = c.Company.CompanyNumber ?? "Unknown",
Jurisdiction = GetJurisdictionName(c.Company.JurisdictionCode),
JurisdictionCode = c.Company.JurisdictionCode,
Status = c.Company.CurrentStatus,
IncorporationDate = ParseDate(c.Company.IncorporationDate),
OpenCorporatesUrl = c.Company.OpencorporatesUrl,
MatchScore = CalculateMatchScore(query, c.Company.Name ?? "")
})
.ToList();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error searching OpenCorporates for: {Query}", query);
return [];
}
}
public async Task<List<JurisdictionInfo>> GetJurisdictionsAsync()
{
try
{
var jurisdictions = await _openCorporatesClient.GetJurisdictionsAsync();
if (jurisdictions == null)
{
return [];
}
return jurisdictions
.Select(j => new JurisdictionInfo
{
Code = j.Code ?? "Unknown",
Name = j.FullName ?? j.Name ?? "Unknown",
Country = j.Country
})
.ToList();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting OpenCorporates jurisdictions");
return [];
}
}
private static OpenCorporatesCompany? FindBestMatch(
string searchName,
List<OpenCorporatesCompanyWrapper> companies)
{
var normalizedSearch = NormalizeName(searchName);
// First try exact match
var exactMatch = companies
.Select(c => c.Company)
.Where(c => c != null)
.FirstOrDefault(c => NormalizeName(c!.Name ?? "").Equals(normalizedSearch, StringComparison.OrdinalIgnoreCase));
if (exactMatch != null)
return exactMatch;
// Then try contains match, preferring active companies
var containsMatches = companies
.Select(c => c.Company)
.Where(c => c != null && !string.IsNullOrEmpty(c.Name))
.Where(c => NormalizeName(c!.Name!).Contains(normalizedSearch, StringComparison.OrdinalIgnoreCase) ||
normalizedSearch.Contains(NormalizeName(c!.Name!), StringComparison.OrdinalIgnoreCase))
.OrderBy(c => c!.CurrentStatus?.ToLowerInvariant() == "active" ? 0 : 1)
.ThenBy(c => c!.Name!.Length)
.ToList();
return containsMatches.FirstOrDefault();
}
private static string NormalizeName(string name)
{
return name
.Replace("LIMITED", "", StringComparison.OrdinalIgnoreCase)
.Replace("LTD", "", StringComparison.OrdinalIgnoreCase)
.Replace("PLC", "", StringComparison.OrdinalIgnoreCase)
.Replace("INC", "", StringComparison.OrdinalIgnoreCase)
.Replace("CORP", "", StringComparison.OrdinalIgnoreCase)
.Replace("LLC", "", StringComparison.OrdinalIgnoreCase)
.Replace("GMBH", "", StringComparison.OrdinalIgnoreCase)
.Replace("AG", "", StringComparison.OrdinalIgnoreCase)
.Replace(".", "")
.Replace(",", "")
.Trim();
}
private static string GetJurisdictionName(string? code)
{
if (string.IsNullOrEmpty(code))
return "Unknown";
return code.ToUpperInvariant();
}
private static DateOnly? ParseDate(string? dateString)
{
if (string.IsNullOrEmpty(dateString))
return null;
if (DateOnly.TryParse(dateString, out var date))
return date;
return null;
}
private static int CalculateMatchScore(string searchName, string foundName)
{
var normalizedSearch = NormalizeName(searchName).ToLowerInvariant();
var normalizedFound = NormalizeName(foundName).ToLowerInvariant();
if (normalizedSearch == normalizedFound)
return 100;
if (normalizedFound.Contains(normalizedSearch) || normalizedSearch.Contains(normalizedFound))
return 80;
// Calculate Levenshtein similarity
var distance = LevenshteinDistance(normalizedSearch, normalizedFound);
var maxLength = Math.Max(normalizedSearch.Length, normalizedFound.Length);
var similarity = (int)((1 - (double)distance / maxLength) * 100);
return Math.Max(0, similarity);
}
private static int LevenshteinDistance(string s1, string s2)
{
var n = s1.Length;
var m = s2.Length;
var d = new int[n + 1, m + 1];
for (var i = 0; i <= n; i++)
d[i, 0] = i;
for (var j = 0; j <= m; j++)
d[0, j] = j;
for (var i = 1; i <= n; i++)
{
for (var j = 1; j <= m; j++)
{
var cost = s1[i - 1] == s2[j - 1] ? 0 : 1;
d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost);
}
}
return d[n, m];
}
private static string BuildVerificationSummary(OpenCorporatesCompany company, int totalResults)
{
var parts = new List<string>();
if (!string.IsNullOrEmpty(company.CurrentStatus))
parts.Add($"Status: {company.CurrentStatus}");
if (!string.IsNullOrEmpty(company.JurisdictionCode))
parts.Add($"Jurisdiction: {company.JurisdictionCode.ToUpperInvariant()}");
if (!string.IsNullOrEmpty(company.IncorporationDate))
parts.Add($"Incorporated: {company.IncorporationDate}");
if (!string.IsNullOrEmpty(company.CompanyType))
parts.Add($"Type: {company.CompanyType}");
if (totalResults > 1)
parts.Add($"({totalResults} total matches found)");
return string.Join(" | ", parts);
}
}