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"
dependencies = [
"async-trait",
"derive_more",
"fulfillment_adapter",
"payment_adapter",
"payup",

View File

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

View File

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

View File

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