wip
This commit is contained in:
238
src/objects/kyc/client.rs
Normal file
238
src/objects/kyc/client.rs
Normal file
@@ -0,0 +1,238 @@
|
||||
/// KYC Client
|
||||
///
|
||||
/// Actual API client for making KYC provider API calls.
|
||||
/// Currently implements Idenfy API but designed to be extensible for other providers.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::{KycInfo, KycSession, session::SessionStatus};
|
||||
|
||||
/// KYC Client for making API calls to KYC providers
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KycClient {
|
||||
/// Provider name (e.g., "idenfy", "sumsub", "onfido")
|
||||
pub provider: String,
|
||||
|
||||
/// API key
|
||||
pub api_key: String,
|
||||
|
||||
/// API secret
|
||||
pub api_secret: String,
|
||||
|
||||
/// Base URL for API (optional, uses provider default if not set)
|
||||
pub base_url: Option<String>,
|
||||
}
|
||||
|
||||
/// Idenfy-specific API request/response structures
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct IdenfyTokenRequest {
|
||||
#[serde(rename = "clientId")]
|
||||
pub client_id: String,
|
||||
|
||||
#[serde(rename = "firstName")]
|
||||
pub first_name: String,
|
||||
|
||||
#[serde(rename = "lastName")]
|
||||
pub last_name: String,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub email: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub phone: Option<String>,
|
||||
|
||||
#[serde(rename = "dateOfBirth", skip_serializing_if = "Option::is_none")]
|
||||
pub date_of_birth: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub nationality: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub address: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub city: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub country: Option<String>,
|
||||
|
||||
#[serde(rename = "zipCode", skip_serializing_if = "Option::is_none")]
|
||||
pub zip_code: Option<String>,
|
||||
|
||||
#[serde(rename = "successUrl", skip_serializing_if = "Option::is_none")]
|
||||
pub success_url: Option<String>,
|
||||
|
||||
#[serde(rename = "errorUrl", skip_serializing_if = "Option::is_none")]
|
||||
pub error_url: Option<String>,
|
||||
|
||||
#[serde(rename = "callbackUrl", skip_serializing_if = "Option::is_none")]
|
||||
pub callback_url: Option<String>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub locale: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct IdenfyTokenResponse {
|
||||
#[serde(rename = "authToken")]
|
||||
pub auth_token: String,
|
||||
|
||||
#[serde(rename = "scanRef")]
|
||||
pub scan_ref: String,
|
||||
|
||||
#[serde(rename = "clientId")]
|
||||
pub client_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct IdenfyVerificationStatus {
|
||||
pub status: String,
|
||||
|
||||
#[serde(rename = "scanRef")]
|
||||
pub scan_ref: String,
|
||||
|
||||
#[serde(rename = "clientId")]
|
||||
pub client_id: String,
|
||||
}
|
||||
|
||||
impl KycClient {
|
||||
/// Create a new KYC client
|
||||
pub fn new(provider: String, api_key: String, api_secret: String) -> Self {
|
||||
Self {
|
||||
provider,
|
||||
api_key,
|
||||
api_secret,
|
||||
base_url: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an Idenfy client
|
||||
pub fn idenfy(api_key: String, api_secret: String) -> Self {
|
||||
Self {
|
||||
provider: "idenfy".to_string(),
|
||||
api_key,
|
||||
api_secret,
|
||||
base_url: Some("https://ivs.idenfy.com/api/v2".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set custom base URL
|
||||
pub fn with_base_url(mut self, base_url: String) -> Self {
|
||||
self.base_url = Some(base_url);
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the base URL for the provider
|
||||
fn get_base_url(&self) -> String {
|
||||
if let Some(url) = &self.base_url {
|
||||
return url.clone();
|
||||
}
|
||||
|
||||
match self.provider.as_str() {
|
||||
"idenfy" => "https://ivs.idenfy.com/api/v2".to_string(),
|
||||
"sumsub" => "https://api.sumsub.com".to_string(),
|
||||
"onfido" => "https://api.onfido.com/v3".to_string(),
|
||||
_ => panic!("Unknown provider: {}", self.provider),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a verification session (Idenfy implementation)
|
||||
pub async fn create_verification_session(
|
||||
&self,
|
||||
kyc_info: &KycInfo,
|
||||
session: &mut KycSession,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
match self.provider.as_str() {
|
||||
"idenfy" => self.create_idenfy_session(kyc_info, session).await,
|
||||
_ => Err(format!("Provider {} not yet implemented", self.provider).into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an Idenfy verification session
|
||||
async fn create_idenfy_session(
|
||||
&self,
|
||||
kyc_info: &KycInfo,
|
||||
session: &mut KycSession,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let url = format!("{}/token", self.get_base_url());
|
||||
|
||||
let request = IdenfyTokenRequest {
|
||||
client_id: kyc_info.client_id.clone(),
|
||||
first_name: kyc_info.first_name.clone(),
|
||||
last_name: kyc_info.last_name.clone(),
|
||||
email: kyc_info.email.clone(),
|
||||
phone: kyc_info.phone.clone(),
|
||||
date_of_birth: kyc_info.date_of_birth.clone(),
|
||||
nationality: kyc_info.nationality.clone(),
|
||||
address: kyc_info.address.clone(),
|
||||
city: kyc_info.city.clone(),
|
||||
country: kyc_info.country.clone(),
|
||||
zip_code: kyc_info.postal_code.clone(),
|
||||
success_url: session.success_url.clone(),
|
||||
error_url: session.error_url.clone(),
|
||||
callback_url: session.callback_url.clone(),
|
||||
locale: session.locale.clone(),
|
||||
};
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.post(&url)
|
||||
.basic_auth(&self.api_key, Some(&self.api_secret))
|
||||
.json(&request)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let error_text = response.text().await?;
|
||||
return Err(format!("Idenfy API error: {}", error_text).into());
|
||||
}
|
||||
|
||||
let token_response: IdenfyTokenResponse = response.json().await?;
|
||||
|
||||
// Update session with token and URL
|
||||
session.set_session_token(token_response.auth_token.clone());
|
||||
|
||||
// Construct verification URL
|
||||
let verification_url = format!(
|
||||
"https://ivs.idenfy.com/api/v2/redirect?authToken={}",
|
||||
token_response.auth_token
|
||||
);
|
||||
session.set_verification_url(verification_url.clone());
|
||||
session.set_status(SessionStatus::Active);
|
||||
|
||||
Ok(verification_url)
|
||||
}
|
||||
|
||||
/// Get verification status (Idenfy implementation)
|
||||
pub async fn get_verification_status(
|
||||
&self,
|
||||
scan_ref: &str,
|
||||
) -> Result<IdenfyVerificationStatus, Box<dyn std::error::Error>> {
|
||||
match self.provider.as_str() {
|
||||
"idenfy" => self.get_idenfy_status(scan_ref).await,
|
||||
_ => Err(format!("Provider {} not yet implemented", self.provider).into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get Idenfy verification status
|
||||
async fn get_idenfy_status(
|
||||
&self,
|
||||
scan_ref: &str,
|
||||
) -> Result<IdenfyVerificationStatus, Box<dyn std::error::Error>> {
|
||||
let url = format!("{}/status/{}", self.get_base_url(), scan_ref);
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let response = client
|
||||
.get(&url)
|
||||
.basic_auth(&self.api_key, Some(&self.api_secret))
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let error_text = response.text().await?;
|
||||
return Err(format!("Idenfy API error: {}", error_text).into());
|
||||
}
|
||||
|
||||
let status: IdenfyVerificationStatus = response.json().await?;
|
||||
Ok(status)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user