Implement dynamic payment session

This commit is contained in:
Adrian Woźniak 2023-05-16 21:34:46 +02:00
parent 30d0baebc1
commit 5801dbea2d
4 changed files with 67 additions and 68 deletions

1
Cargo.lock generated
View File

@ -4604,6 +4604,7 @@ name = "stripe_adapter"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"derive_more",
"fulfillment_adapter", "fulfillment_adapter",
"payment_adapter", "payment_adapter",
"payup", "payup",

View File

@ -2,7 +2,6 @@
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::DerefMut;
pub use config::PaymentProviderConfig; pub use config::PaymentProviderConfig;
pub use model::v3::*; pub use model::v3::*;
@ -32,14 +31,13 @@ pub struct UpdateRequests {
pub customer_metadata: Option<CustomerMetadata>, pub customer_metadata: Option<CustomerMetadata>,
} }
pub struct PaymentSessionData { pub trait PaymentSessionData: Send + Sync {
pub id: Option<String>, fn id(&self) -> Option<String>;
pub meta: HashMap<String, AnyData>,
} }
pub struct PaymentProcessorSessionResponse { pub struct PaymentProcessorSessionResponse {
pub update_requests: Option<UpdateRequests>, pub update_requests: Option<UpdateRequests>,
pub session_data: PaymentSessionData, pub session_data: Box<dyn PaymentSessionData>,
} }
pub struct PaymentProcessorContext { pub struct PaymentProcessorContext {
@ -50,7 +48,7 @@ pub struct PaymentProcessorContext {
pub resource_id: String, pub resource_id: String,
pub customer: Option<Customer>, pub customer: Option<Customer>,
pub context: PaymentProcessCtx, pub context: PaymentProcessCtx,
pub payment_session_data: PaymentSessionData, pub payment_session_data: Box<dyn PaymentSessionData>,
} }
#[derive(Debug, serde::Serialize, serde::Deserialize)] #[derive(Debug, serde::Serialize, serde::Deserialize)]
@ -129,6 +127,8 @@ pub enum PError {
ChargeNotExists(String), ChargeNotExists(String),
#[error("Failed capture charge with id {0:?}")] #[error("Failed capture charge with id {0:?}")]
FailedCapture(String), FailedCapture(String),
#[error("Invalid charge for given payment adapter")]
InvalidType,
} }
pub type PResult<T> = Result<T, PError>; pub type PResult<T> = Result<T, PError>;
@ -166,82 +166,68 @@ pub trait PaymentAdapter {
*/ */
async fn refund_payment( async fn refund_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
refund_amount: Amount, refund_amount: Amount,
) -> PResult<PaymentSessionData>; ) -> PResult<()>;
/** /**
* Authorize an existing session if it is not already authorized * Authorize an existing session if it is not already authorized
*/ */
async fn authorize_payment( async fn authorize_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
data: PaymentProcessCtx, data: PaymentProcessCtx,
) -> PResult<(PaymentSessionStatus, PaymentSessionData)>; ) -> PResult<(PaymentSessionStatus, ())>;
/** /**
* Capture an existing session * Capture an existing session
*/ */
async fn capture_payment( async fn capture_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionData>; ) -> PResult<()>;
/** /**
* Delete an existing session * Delete an existing session
*/ */
async fn delete_payment( async fn delete_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionData>; ) -> PResult<()>;
/** /**
* Retrieve an existing session * Retrieve an existing session
*/ */
async fn retrieve_payment( async fn retrieve_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionData>; ) -> PResult<()>;
/** /**
* Cancel an existing session * Cancel an existing session
*/ */
async fn cancel_payment( async fn cancel_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionData>; ) -> PResult<()>;
/** /**
* Return the status of the session * Return the status of the session
*/ */
async fn payment_status( async fn payment_status(
&mut self, &mut self,
payment_session_data: &PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionStatus>; ) -> PResult<PaymentSessionStatus>;
} }
// use traitcast::{Traitcast, TraitcastFrom}; pub fn session_ref<T: PaymentSessionData + Any>(
session: &Box<dyn PaymentSessionData>,
pub trait APaymentSessionData: Any { ) -> Option<&T> {
fn id(&self) -> Option<String>; <dyn Any>::downcast_ref(session)
} }
pub struct BPSD(Box<dyn APaymentSessionData>); pub fn session_mut_ref<T: PaymentSessionData + Any>(
session: &mut Box<dyn PaymentSessionData>,
impl BPSD { ) -> Option<&mut T> {
pub fn as_mut_ref<T>(&mut self) -> Option<&mut T> { <dyn Any>::downcast_mut(session)
use std::boxed::Box;
let r = self.0.deref_mut();
if <dyn Any>::is::<&T>(r) {
Some(unsafe { r as &mut T })
} else {
None
}
}
}
async fn f(mut psd: &mut BPSD) {
use std::boxed::Box;
let c = psd.deref_mut();
} }

View File

@ -17,3 +17,4 @@ payup = { version = "*", features = [] }
tracing = { version = "0.1.37" } tracing = { version = "0.1.37" }
async-trait = { version = "0.1.68" } async-trait = { version = "0.1.68" }
serde = { version = "1.0.162", features = ['derive'] } serde = { version = "1.0.162", features = ['derive'] }
derive_more = { version = "0.99.17" }

View File

@ -1,9 +1,9 @@
#![crate_type = "rlib"] #![crate_type = "rlib"]
use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::DerefMut; use std::ops::DerefMut;
use derive_more::DerefMut;
use fulfillment_adapter::*; use fulfillment_adapter::*;
use payment_adapter::*; use payment_adapter::*;
use payup::stripe; use payup::stripe;
@ -105,7 +105,7 @@ impl PaymentAdapter for StripePrzelewy24 {
captured: self.config.capture.clone(), captured: self.config.capture.clone(),
currency: Some(currency_code.into()), currency: Some(currency_code.into()),
description: desc, description: desc,
customer: customer.map(|c| c.id.0.to_string()), customer: customer.as_ref().map(|c| c.id.0.to_string()),
receipt_email: Some(email.0), receipt_email: Some(email.0),
..stripe::Charge::new() ..stripe::Charge::new()
} }
@ -113,11 +113,11 @@ impl PaymentAdapter for StripePrzelewy24 {
.await .await
.unwrap(); .unwrap();
let update_requests = match customer { let update_requests = match customer.as_ref() {
Some(c) if !c.has_stripe_id() => Some(UpdateRequests { Some(c) if !c.has_stripe_id() => Some(UpdateRequests {
customer_metadata: Some(CustomerMetadata(HashMap::from([( customer_metadata: Some(CustomerMetadata(HashMap::from([(
"id".to_string(), "id".to_string(),
AnyData(change.customer.unwrap_or_default().into_bytes()), AnyData(change.customer.clone().unwrap_or_default().into_bytes()),
)]))), )]))),
}), }),
_ => None, _ => None,
@ -125,7 +125,7 @@ impl PaymentAdapter for StripePrzelewy24 {
Ok(PaymentProcessorSessionResponse { Ok(PaymentProcessorSessionResponse {
update_requests, update_requests,
session_data: payment_session_data, session_data: Box::new(Charge(change)),
}) })
} }
@ -138,30 +138,30 @@ impl PaymentAdapter for StripePrzelewy24 {
async fn refund_payment( async fn refund_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
refund_amount: Amount, refund_amount: Amount,
) -> PResult<PaymentSessionData> { ) -> PResult<()> {
todo!() todo!()
} }
async fn authorize_payment( async fn authorize_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
data: PaymentProcessCtx, data: PaymentProcessCtx,
) -> PResult<(PaymentSessionStatus, PaymentSessionData)> { ) -> PResult<(PaymentSessionStatus, ())> {
self.payment_status(&payment_session_data) self.payment_status(payment_session_data)
.await .await
.map(|status| (status, payment_session_data)) .map(|status| (status, ()))
} }
async fn capture_payment( async fn capture_payment(
&mut self, &mut self,
mut payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionData> { ) -> PResult<()> {
let id = payment_session_data let Some(session) = session_mut_ref::<Charge>(payment_session_data) else {
.id return Err(PError::InvalidType);
.clone() };
.ok_or_else(|| PError::NoChargeId)?; let id = session.id().ok_or_else(|| PError::NoChargeId)?;
let charge = stripe::Charge::async_get(self.stripe.clone(), id.clone()) let charge = stripe::Charge::async_get(self.stripe.clone(), id.clone())
.await .await
.map_err(|_| PError::ChargeNotExists(id.clone()))?; .map_err(|_| PError::ChargeNotExists(id.clone()))?;
@ -172,38 +172,40 @@ impl PaymentAdapter for StripePrzelewy24 {
tracing::warn!("{e}"); tracing::warn!("{e}");
PError::FailedCapture(id) PError::FailedCapture(id)
})?; })?;
payment_session_data.id = charge.id; session.0 = change;
Ok(payment_session_data) Ok(())
} }
async fn delete_payment( async fn delete_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionData> { ) -> PResult<()> {
todo!() todo!()
} }
async fn retrieve_payment( async fn retrieve_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionData> { ) -> PResult<()> {
todo!() todo!()
} }
async fn cancel_payment( async fn cancel_payment(
&mut self, &mut self,
payment_session_data: PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionData> { ) -> PResult<()> {
todo!() todo!()
} }
async fn payment_status( async fn payment_status(
&mut self, &mut self,
payment_session_data: &PaymentSessionData, payment_session_data: &mut Box<dyn PaymentSessionData>,
) -> PResult<PaymentSessionStatus> { ) -> PResult<PaymentSessionStatus> {
payment_status( payment_status(
self.stripe.clone(), self.stripe.clone(),
payment_session_data.id.clone().unwrap_or_default(), payment_session_data
.id()
.ok_or_else(|| PError::NoChargeId)?,
) )
.await .await
.map_err(|_| PError::HttpError) .map_err(|_| PError::HttpError)
@ -240,3 +242,12 @@ impl StripeCustomerMetadata for Customer {
.and_then(|v| String::from_utf8(v.clone()).ok()) .and_then(|v| String::from_utf8(v.clone()).ok())
} }
} }
#[derive(Debug, derive_more::Deref, derive_more::DerefMut)]
struct Charge(stripe::Charge);
impl PaymentSessionData for Charge {
fn id(&self) -> Option<String> {
self.0.id.clone()
}
}