use std::future::Future;
use serde::Deserialize;
use crate::{
client::{ClientError, VersaClient, customer_registration::CustomerRegistration},
protocol::{
Checkout, CheckoutRequest, ClientMetadata, Envelope,
customer_registration::CustomerRef,
misuse::{Misuse, MisuseCode, ReportMisuseRequest},
},
};
pub trait VersaReceiver {
fn verify_event(&self, body: bytes::Bytes, token: &str) -> Result<bytes::Bytes, ClientError>;
fn report_misuse(
&self,
receipt_id: String,
misuse: Vec<Misuse>,
) -> impl Future<Output = Result<(), ClientError>> + Send;
fn checkout_key(
&self,
receipt_id: String,
) -> impl Future<Output = Result<Checkout, ClientError>> + Send;
fn decrypt_envelope<T>(&self, envelope: Envelope, key: String) -> Result<T, MisuseCode>
where
T: for<'a> Deserialize<'a>;
}
pub struct VersaReceivingClient {
base_client: VersaClient,
#[deprecated(since = "1.1.0", note = "please use `webhook_secret()` instead")]
pub receiver_secret: String,
webhook_secret: String,
}
#[cfg(feature = "client_receiver")]
impl VersaClient {
pub fn receiving_client(self, webhook_secret: String) -> VersaReceivingClient {
VersaReceivingClient {
base_client: self,
receiver_secret: webhook_secret.clone(),
webhook_secret,
}
}
}
impl VersaReceivingClient {
pub fn client_id(&self) -> String {
self.base_client.client_id()
}
pub fn webhook_secret(&self) -> String {
self.webhook_secret.clone()
}
}
#[cfg(feature = "client_receiver")]
impl VersaReceiver for VersaReceivingClient {
fn verify_event(&self, body: bytes::Bytes, token: &str) -> Result<bytes::Bytes, ClientError> {
let secret = self.webhook_secret();
let (verified, bytes) = crate::hmac_util::verify_with_secret(body, secret, token);
if verified {
Ok(bytes)
} else {
Err(ClientError::HmacVerificationError)
}
}
async fn report_misuse(
&self,
receipt_id: String,
misuse: Vec<Misuse>,
) -> Result<(), ClientError> {
let credential = self.base_client.authorization_header_val();
let payload = ReportMisuseRequest { receipt_id, misuse };
let payload_json = serde_json::to_string(&payload).unwrap();
let client = reqwest::Client::new();
let endpoint_url = format!("{}/report_misuse", self.base_client.registry_url);
let response_result = client
.post(endpoint_url)
.header("Accept", "application/json")
.header("Authorization", credential)
.header("Content-Type", "application/json")
.body(payload_json)
.send()
.await;
let res = match response_result {
Ok(res) => res,
Err(e) => {
return Err(ClientError::NetworkError(e));
}
};
match res.status().is_success() {
true => Ok(()),
false => Err(ClientError::RegistryError(
res.status(),
res.text().await.unwrap_or_default(),
)),
}
}
async fn checkout_key(&self, receipt_id: String) -> Result<Checkout, ClientError> {
let registry_url = self.base_client.registry_url.clone();
let credential = self.base_client.authorization_header_val();
let payload = CheckoutRequest {
receipt_id,
client_metadata: Some(ClientMetadata {
client_string: self.base_client.client_string(),
}),
};
let payload_json = serde_json::to_string(&payload).unwrap();
let client = reqwest::Client::new();
let response_result = client
.post(format!("{}/checkout", registry_url))
.header("Accept", "application/json")
.header("Authorization", credential)
.header("Content-Type", "application/json")
.body(payload_json)
.send()
.await;
let res = match response_result {
Ok(res) => res,
Err(e) => {
return Err(ClientError::NetworkError(e));
}
};
if res.status().is_success() {
let data: Checkout = match res.json().await {
Ok(val) => val,
Err(e) => {
return Err(ClientError::DeserializationError(e));
}
};
return Ok(data);
} else {
let status = res.status();
let text = res.text().await.unwrap_or_default();
return Err(ClientError::RegistryError(status, text));
}
}
fn decrypt_envelope<T>(&self, envelope: Envelope, key: String) -> Result<T, MisuseCode>
where
T: for<'a> Deserialize<'a>,
{
crate::encryption::decrypt_envelope(envelope, &key)
}
}
impl CustomerRegistration for VersaReceivingClient {
async fn register_customer_reference(
&self,
customer_reference: CustomerRef,
) -> Result<(), ClientError> {
self
.base_client
.register_customer_reference(customer_reference)
.await
}
async fn deregister_customer_reference(
&self,
customer_reference: CustomerRef,
) -> Result<(), ClientError> {
self
.base_client
.register_customer_reference(customer_reference)
.await
}
}