This commit is contained in:
Adrian Woźniak 2023-05-15 22:55:09 +02:00
parent 8e1f9625d1
commit 30d0baebc1
11 changed files with 4988 additions and 657 deletions

74
Cargo.lock generated
View File

@ -336,6 +336,12 @@ version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "anymap"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344"
[[package]] [[package]]
name = "argon2" name = "argon2"
version = "0.4.1" version = "0.4.1"
@ -1101,6 +1107,16 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "ctor"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
dependencies = [
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "cxx" name = "cxx"
version = "1.0.94" version = "1.0.94"
@ -1791,6 +1807,17 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "ghost"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e77ac7b51b8e6313251737fcef4b1c01a2ea102bde68415b62c0ee9268fec357"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]] [[package]]
name = "gimli" name = "gimli"
version = "0.26.2" version = "0.26.2"
@ -2293,6 +2320,28 @@ dependencies = [
"unic-langid", "unic-langid",
] ]
[[package]]
name = "inventory"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0eb5160c60ba1e809707918ee329adb99d222888155835c6feedba19f6c3fd4"
dependencies = [
"ctor",
"ghost",
"inventory-impl",
]
[[package]]
name = "inventory-impl"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e41b53715c6f0c4be49510bb82dee2c1e51c8586d885abe65396e82ed518548"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "io-extras" name = "io-extras"
version = "0.15.0" version = "0.15.0"
@ -3184,12 +3233,15 @@ name = "payment_adapter"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"chrono",
"config", "config",
"futures", "futures",
"model", "model",
"serde", "serde",
"thiserror", "thiserror",
"toml 0.7.3",
"tracing", "tracing",
"traitcast",
"uuid 1.3.2", "uuid 1.3.2",
] ]
@ -4555,6 +4607,7 @@ dependencies = [
"fulfillment_adapter", "fulfillment_adapter",
"payment_adapter", "payment_adapter",
"payup", "payup",
"serde",
"tokio 1.28.0", "tokio 1.28.0",
"tracing", "tracing",
] ]
@ -5170,6 +5223,27 @@ dependencies = [
"tracing-subscriber", "tracing-subscriber",
] ]
[[package]]
name = "traitcast"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f80b1cde694e5ff2dcb33875530f2f031a9a34dec8ba2744cacaf80a88658740"
dependencies = [
"inventory",
"lazy_static",
"traitcast_core",
]
[[package]]
name = "traitcast_core"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabba8f4a83963f61a84d8cfc5829b4fad692aa6c6ad5d7b08b9549777e3cc4a"
dependencies = [
"anymap",
"inventory",
]
[[package]] [[package]]
name = "trust-dns-native-tls" name = "trust-dns-native-tls"
version = "0.20.4" version = "0.20.4"

View File

@ -1,4 +1,6 @@
#[derive(Debug, thiserror::Error)]
pub enum FError { pub enum FError {
#[error("Payment gate request failed")]
HttpError, HttpError,
} }
@ -9,6 +11,7 @@ pub struct FulfillmentOption {}
pub trait FulfillmentPayload {} pub trait FulfillmentPayload {}
pub trait FulfillmentCart {} pub trait FulfillmentCart {}
#[async_trait::async_trait]
pub trait FulfillmentAdapter { pub trait FulfillmentAdapter {
fn identifier() -> &'static str; fn identifier() -> &'static str;
@ -22,7 +25,7 @@ pub trait FulfillmentAdapter {
* to create shipping options in Medusa that can be chosen between by * to create shipping options in Medusa that can be chosen between by
* the customer. * the customer.
*/ */
fn fulfillment_options(&mut self) -> FResult<&[FulfillmentOption]>; async fn fulfillment_options(&mut self) -> FResult<Vec<FulfillmentOption>>;
/** /**
* Called before a shipping method is set on a cart to ensure that the * Called before a shipping method is set on a cart to ensure that the
@ -31,7 +34,7 @@ pub trait FulfillmentAdapter {
* point. It is up to the fulfillment provider to enforce that the * point. It is up to the fulfillment provider to enforce that the
* correct data is being sent through. * correct data is being sent through.
*/ */
fn validate_fulfillment_payload<P: FulfillmentPayload, C: FulfillmentCart>( async fn validate_fulfillment_payload<P: FulfillmentPayload, C: FulfillmentCart>(
&mut self, &mut self,
payload: P, payload: P,
cart: C, cart: C,
@ -41,33 +44,35 @@ pub trait FulfillmentAdapter {
* Called before a shipping option is created in Admin. Use this to * Called before a shipping option is created in Admin. Use this to
* ensure that a fulfillment option does in fact exist. * ensure that a fulfillment option does in fact exist.
*/ */
fn validate_option<P: FulfillmentPayload>(&mut self, payload: P) -> FResult<bool>; async fn validate_option<P: FulfillmentPayload>(&mut self, payload: P) -> FResult<bool>;
fn can_calculate<P: FulfillmentPayload>(&mut self, payload: P) -> FResult<bool>; async fn can_calculate<P: FulfillmentPayload>(&mut self, payload: P) -> FResult<bool>;
/** /**
* Used to calculate a price for a given shipping option. * Used to calculate a price for a given shipping option.
*/ */
fn calculate_price<P: FulfillmentPayload, C: FulfillmentCart>(data: P, cart: C) async fn calculate_price<P: FulfillmentPayload, C: FulfillmentCart>(
-> FResult<u64>; data: P,
cart: C,
) -> FResult<u64>;
// FULFILLMENT // FULFILLMENT
fn create_fulfillment(&mut self); async fn create_fulfillment(&mut self);
fn cancel_fulfillment(&mut self); async fn cancel_fulfillment(&mut self);
/** /**
* Used to retrieve documents associated with a fulfillment. * Used to retrieve documents associated with a fulfillment.
* Will default to returning no documents. * Will default to returning no documents.
*/ */
fn fulfillment_documents(&mut self); async fn fulfillment_documents(&mut self);
// REFUND // REFUND
fn create_return(&mut self); async fn create_return(&mut self);
fn return_documents(&mut self); async fn return_documents(&mut self);
// //
fn shipment_documents(&mut self); async fn shipment_documents(&mut self);
fn documents(&mut self); async fn documents(&mut self);
} }

View File

@ -3,6 +3,7 @@
pub mod api; pub mod api;
pub mod encrypt; pub mod encrypt;
pub mod v3;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::ops; use std::ops;

4031
crates/model/src/v3.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -58,35 +58,72 @@ impl PayUPayment {
} }
impl PaymentAdapter for PayUPayment { impl PaymentAdapter for PayUPayment {
fn init(&mut self) -> PResult<Status> { async fn new(config: Config) -> Self {
todo!() todo!()
} }
fn teardown(&mut self) -> PResult<Status> { async fn initialize_payment(
&mut self,
ctx: PaymentProcessorContext,
) -> PResult<PaymentProcessorSessionResponse> {
todo!() todo!()
} }
fn create_payment(&mut self, msg: CreatePayment) -> PResult<PaymentCreated> { async fn update_payment(
&mut self,
ctx: PaymentProcessorContext,
) -> PResult<Option<PaymentProcessorSessionResponse>> {
todo!() todo!()
} }
fn retrieve_payment(&mut self) { async fn refund_payment(
&mut self,
payment_session_data: PaymentSessionData,
refund_amount: Amount,
) -> PResult<PaymentSessionData> {
todo!() todo!()
} }
fn authorize_payment(&mut self) { async fn authorize_payment(
&mut self,
payment_session_data: PaymentSessionData,
data: PaymentProcessCtx,
) -> PResult<(PaymentSessionStatus, PaymentSessionData)> {
todo!() todo!()
} }
fn capture_payment(&mut self) { async fn capture_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData> {
todo!() todo!()
} }
fn refund_payment(&mut self, msg: RefundPayment) -> PResult<Refunded> { async fn delete_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData> {
todo!() todo!()
} }
fn delete_payment(&mut self, msg: DeletePayment) -> PResult<Deleted> { async fn retrieve_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData> {
todo!()
}
async fn cancel_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData> {
todo!()
}
async fn payment_status(
&mut self,
payment_session_data: &PaymentSessionData,
) -> PResult<PaymentSessionStatus> {
todo!() todo!()
} }
} }

View File

@ -12,3 +12,6 @@ thiserror = { version = "1" }
tracing = { version = "0" } tracing = { version = "0" }
uuid = { version = "1", features = ['v4'] } uuid = { version = "1", features = ['v4'] }
async-trait = { version = "0.1.68" } async-trait = { version = "0.1.68" }
chrono = { version = "0.4.24" }
toml = { version = "0.7.3" }
traitcast = { version = "0.5.0" }

View File

@ -1,29 +1,13 @@
#![feature(trait_upcasting)]
use std::any::Any;
use std::collections::HashMap;
use std::ops::DerefMut;
pub use config::PaymentProviderConfig; pub use config::PaymentProviderConfig;
pub use model::*; pub use model::v3::*;
pub use uuid; pub use uuid;
use uuid::Uuid;
pub const CONFIG_POS: u32 = 3;
#[derive(Debug, Clone, thiserror::Error, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub enum Error {
#[error("Malformed create payment message")]
MalformedCreatePayment,
#[error("Malformed payment status update message")]
MalformedStatusUpdate,
#[error("Malformed authorize response")]
MalformedAuthorize,
#[error("Message pack: malformed create payment message")]
MsgPackDeserializationFailed,
#[error("Provider rejected authorization")]
AuthorizeFailed,
#[error("Provider rejected order")]
PaymentFailed,
#[error("HTTP request failed")]
HttpFailed,
#[error("Unknown host operation {0:?}")]
UnknownOperation(String),
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
#[repr(C)] #[repr(C)]
@ -32,59 +16,99 @@ pub enum Status {
Failure = 1, Failure = 1,
} }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct AnyData(pub Vec<u8>);
#[repr(C)]
pub struct Buyer { pub struct PaymentProcessorError {
/// Required customer e-mail pub error: String,
pub email: String, pub code: Option<String>,
/// Required customer phone number pub detail: Option<AnyData>,
pub phone: String,
/// Required customer first name
pub first_name: String,
/// Required customer last name
pub last_name: String,
/// Required customer language
pub language: String,
} }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct CustomerMetadata(pub HashMap<String, AnyData>);
#[repr(C)]
pub struct Product { pub struct PaymentProcessCtx(pub HashMap<String, AnyData>);
pub id: i32,
pub name: String, pub struct UpdateRequests {
pub unit_price: i32, pub customer_metadata: Option<CustomerMetadata>,
pub quantity_unit: String,
pub quantity: i32,
} }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct PaymentSessionData {
#[repr(C)] pub id: Option<String>,
pub struct Item { pub meta: HashMap<String, AnyData>,
pub product_id: i32,
pub quantity: i32,
pub quantity_unit: String,
} }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct PaymentProcessorSessionResponse {
pub update_requests: Option<UpdateRequests>,
pub session_data: PaymentSessionData,
}
pub struct PaymentProcessorContext {
pub billing_address: Option<Address>,
pub email: Email,
pub currency_code: CurrencyCode,
pub amount: Amount,
pub resource_id: String,
pub customer: Option<Customer>,
pub context: PaymentProcessCtx,
pub payment_session_data: PaymentSessionData,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct CreatePayment { pub struct CreatePayment {
pub buyer: Buyer, // pub buyer: Buyer,
pub customer_ip: String, pub customer_ip: String,
pub currency: String, pub currency: String,
pub description: String, pub description: String,
pub cart_products: Vec<Product>, pub cart_products: Vec<Product>,
pub items: Vec<Item>, pub items: Vec<LineItem>,
//
pub cart: Cart,
pub customer: Option<Customer>,
pub customer_groups: Option<CustomerGroup>,
pub gift_cards: Vec<GiftCard>,
pub region: Region,
pub related_discounts: HashMap<DiscountId, Discount>,
pub related_shipping_methods: HashMap<Uuid, ShippingMethod>,
// cart line items and [LineItem::original_item_id]
pub related_items: HashMap<Uuid, LineItem>,
/// [Payment::swap_id]
pub related_payments: HashMap<PaymentId, Payment>,
pub related_line_item_adjustments: HashMap<LineItemAdjustmentId, LineItemAdjustment>,
pub related_tax_lines: HashMap<Uuid, LineItemTaxLine>,
pub related_currencies: HashMap<CurrencyCode, Currency>,
/// [Return::swap_id]
pub related_returns: HashMap<ReturnId, Return>,
//
pub order_ext_id: Option<String>, pub order_ext_id: Option<String>,
pub notify_uri: String, pub notify_uri: String,
pub continue_uri: String, pub continue_uri: String,
} }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug)]
pub struct PaymentCreated { pub struct PaymentCreated {
pub ext_order_id: Option<ExtOrderId>, pub ext_order_id: Option<ExtOrderId>,
pub redirect_uri: Option<String>, pub redirect_uri: Option<String>,
} }
#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug)]
pub struct DeletePayment {
pub ext_order_id: Uuid,
}
#[derive(Debug)]
pub struct Deleted {
pub ext_order_id: Uuid,
}
#[derive(Debug)]
pub struct RefundPayment {
pub refund: RefundType,
pub provider_order_id: Uuid,
pub description: String,
}
#[derive(Debug)]
pub enum RefundType { pub enum RefundType {
/// Refund entire payment /// Refund entire payment
Full, Full,
@ -92,97 +116,132 @@ pub enum RefundType {
Partial(Price), Partial(Price),
} }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug)]
pub struct RefundPayment {
pub refund: RefundType,
pub provider_order_id: String,
pub description: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Refunded {} pub struct Refunded {}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[derive(Debug, thiserror::Error)]
pub struct UpdateStatus { pub enum PError {
pub payload: Vec<u8>, #[error("Payment gate request failed")]
HttpError,
#[error("Operation requires Charge id")]
NoChargeId,
#[error("There's no charge with id {0:?}")]
ChargeNotExists(String),
#[error("Failed capture charge with id {0:?}")]
FailedCapture(String),
} }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StatusUpdated {}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DeletePayment {
pub ext_order_id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Deleted {
pub ext_order_id: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum HttpMethod {
Get,
Post,
}
pub enum PaymentSessionStatus {
Pending,
RequiresMore,
Canceled,
Authorized,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub struct HttpRequest {
pub method: HttpMethod,
pub url: String,
pub headers: Vec<(String, String)>,
pub bearer_auth: Option<String>,
pub body: Option<Vec<u8>>,
}
pub mod tracing {
macro_rules! log_levels {
($($lvl: ident),+) => {
$(
#[macro_export]
macro_rules! $lvl {
($fmt: expr) => {
wapc::console_log($fmt);
};
($fmt: expr, $arg1: expr) => {
wapc::console_log(&format!($fmt, $arg1));
};
($fmt: expr, $arg1: expr, $arg2: expr) => {
wapc::console_log(&format!($fmt, $arg1, $arg2));
};
($fmt: expr, $arg1: expr, $arg2: expr, $arg3: expr) => {
wapc::console_log(&format!($fmt, $arg1, $arg2, $arg3));
};
($fmt: expr, $arg1: expr, $arg2: expr, $arg3: expr, $arg4: expr) => {
wapc::console_log(&format!($fmt, $arg1, $arg2, $arg3, $arg4));
};
}
)+
};
}
log_levels!(error, warn, debug, info, trace, log);
}
pub enum PError {}
pub type PResult<T> = Result<T, PError>; pub type PResult<T> = Result<T, PError>;
pub trait PaymentAdapter { pub struct Config(String);
fn init(&mut self) -> PResult<Status>;
fn teardown(&mut self) -> PResult<Status>;
fn create_payment(&mut self, msg: CreatePayment) -> PResult<PaymentCreated>; impl Config {
fn retrieve_payment(&mut self); pub fn config<S: serde::de::DeserializeOwned>(self) -> Result<S, toml::de::Error> {
fn authorize_payment(&mut self); toml::from_str(&self.0)
fn capture_payment(&mut self); }
fn refund_payment(&mut self, msg: RefundPayment) -> PResult<Refunded>; }
fn delete_payment(&mut self, msg: DeletePayment) -> PResult<Deleted>;
#[async_trait::async_trait]
pub trait PaymentAdapter {
async fn new(config: Config) -> Self;
/**
* Initiate a payment session with the external provider
*/
async fn initialize_payment(
&mut self,
ctx: PaymentProcessorContext,
) -> PResult<PaymentProcessorSessionResponse>;
/**
* Update an existing payment session
*/
async fn update_payment(
&mut self,
ctx: PaymentProcessorContext,
) -> PResult<Option<PaymentProcessorSessionResponse>>;
/**
* Refund an existing session
*/
async fn refund_payment(
&mut self,
payment_session_data: PaymentSessionData,
refund_amount: Amount,
) -> PResult<PaymentSessionData>;
/**
* Authorize an existing session if it is not already authorized
*/
async fn authorize_payment(
&mut self,
payment_session_data: PaymentSessionData,
data: PaymentProcessCtx,
) -> PResult<(PaymentSessionStatus, PaymentSessionData)>;
/**
* Capture an existing session
*/
async fn capture_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData>;
/**
* Delete an existing session
*/
async fn delete_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData>;
/**
* Retrieve an existing session
*/
async fn retrieve_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData>;
/**
* Cancel an existing session
*/
async fn cancel_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData>;
/**
* Return the status of the session
*/
async fn payment_status(
&mut self,
payment_session_data: &PaymentSessionData,
) -> PResult<PaymentSessionStatus>;
}
// use traitcast::{Traitcast, TraitcastFrom};
pub trait APaymentSessionData: Any {
fn id(&self) -> Option<String>;
}
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();
} }

View File

@ -1,396 +1,399 @@
use std::collections::HashMap; // use std::collections::HashMap;
use std::fs::read_dir; // use std::fs::read_dir;
use std::path::PathBuf; // use std::path::PathBuf;
use std::str::FromStr; // use std::str::FromStr;
use std::sync::mpsc::Sender; // use std::sync::mpsc::Sender;
use std::sync::{Arc, LockResult, MutexGuard}; // use std::sync::{Arc, LockResult, MutexGuard};
//
use config::{AppConfig, SharedAppConfig, UpdateConfig}; // use config::{AppConfig, SharedAppConfig, UpdateConfig};
use payment_adapter::{HttpMethod, HttpRequest}; // use payment_adapter::{HttpMethod, HttpRequest};
use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; // use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use wapc::WasiParams; // use wapc::WasiParams;
use wapc_pool::{HostPool, HostPoolBuilder}; // use wapc_pool::{HostPool, HostPoolBuilder};
//
use crate::rpc::ModuleSender; // use crate::rpc::ModuleSender;
//
// mod actions; // // mod actions;
// mod context; // // mod context;
// mod db; // // mod db;
mod mqtt; // mod mqtt;
// pub mod pay_u_adapter; // // pub mod pay_u_adapter;
mod rpc; // mod rpc;
// pub mod t_pay_adapter; // // pub mod t_pay_adapter;
//
#[derive(Clone)] // #[derive(Clone)]
pub enum WasmMsg { // pub enum WasmMsg {
CreateOrder { // CreateOrder {
create_payment: payment_adapter::CreatePayment, // create_payment: payment_adapter::CreatePayment,
tx: Sender<ResultMsg>, // tx: Sender<ResultMsg>,
}, // },
UpdateStatus { // UpdateStatus {
update_status: payment_adapter::UpdateStatus, // update_status: payment_adapter::UpdateStatus,
tx: Sender<ResultMsg>, // tx: Sender<ResultMsg>,
}, // },
Cancel { // Cancel {
cancel: payment_adapter::RefundPayment, // cancel: payment_adapter::RefundPayment,
tx: Sender<ResultMsg>, // tx: Sender<ResultMsg>,
}, // },
} // }
//
#[derive(Clone)] // #[derive(Clone)]
pub enum ResultMsg { // pub enum ResultMsg {
OrderCreated(payment_adapter::PaymentCreated), // OrderCreated(payment_adapter::PaymentCreated),
StatusUpdated(payment_adapter::StatusUpdated), // StatusUpdated(payment_adapter::StatusUpdated),
Cancel(payment_adapter::Refunded), // Cancel(payment_adapter::Refunded),
} // }
//
pub struct HostChannel { // pub struct HostChannel {
pub host: HostPool, // pub host: HostPool,
pub channel: std::sync::mpsc::Receiver<WasmMsg>, // pub channel: std::sync::mpsc::Receiver<WasmMsg>,
} // }
//
impl HostChannel { // impl HostChannel {
pub fn start(self) { // pub fn start(self) {
tokio::spawn(async move { // tokio::spawn(async move {
let HostChannel { // let HostChannel {
host: pool, // host: pool,
channel, // channel,
} = self; // } = self;
//
loop { // loop {
let msg = match channel.recv() { // let msg = match channel.recv() {
Ok(msg) => msg, // Ok(msg) => msg,
_ => continue, // _ => continue,
}; // };
match msg { // match msg {
WasmMsg::CreateOrder { // WasmMsg::CreateOrder {
create_payment: msg, // create_payment: msg,
tx, // tx,
} => { // } => {
Self::send_and_rec( // Self::send_and_rec(
"create_payment", // "create_payment",
msg, // msg,
tx, // tx,
&pool, // &pool,
ResultMsg::OrderCreated, // ResultMsg::OrderCreated,
) // )
.await // .await
.ok(); // .ok();
} // }
WasmMsg::UpdateStatus { // WasmMsg::UpdateStatus {
update_status: msg, // update_status: msg,
tx, // tx,
} => { // } => {
Self::send_and_rec( // Self::send_and_rec(
"update_status", // "update_status",
msg, // msg,
tx, // tx,
&pool, // &pool,
ResultMsg::StatusUpdated, // ResultMsg::StatusUpdated,
) // )
.await // .await
.ok(); // .ok();
} // }
WasmMsg::Cancel { cancel: msg, tx } => { // WasmMsg::Cancel { cancel: msg, tx } => {
Self::send_and_rec("cancel_order", msg, tx, &pool, ResultMsg::Cancel) // Self::send_and_rec("cancel_order", msg, tx, &pool,
.await // ResultMsg::Cancel) .await
.ok(); // .ok();
} // }
} // }
} // }
}); // });
} // }
//
async fn send_and_rec<R, T, F>( // async fn send_and_rec<R, T, F>(
name: &str, // name: &str,
msg: R, // msg: R,
tx: Sender<ResultMsg>, // tx: Sender<ResultMsg>,
pool: &HostPool, // pool: &HostPool,
f: F, // f: F,
) -> Result<(), ()> // ) -> Result<(), ()>
where // where
R: serde::Serialize, // R: serde::Serialize,
T: serde::de::DeserializeOwned, // T: serde::de::DeserializeOwned,
F: FnOnce(T) -> ResultMsg, // F: FnOnce(T) -> ResultMsg,
{ // {
macro_rules! ok_or_bail { // macro_rules! ok_or_bail {
($run: expr) => { // ($run: expr) => {
match $run { // match $run {
Ok(r) => r, // Ok(r) => r,
_ => return Err(()), // _ => return Err(()),
} // }
}; // };
} // }
//
let msg = ok_or_bail!(wapc_codec::messagepack::serialize(msg)); // let msg = ok_or_bail!(wapc_codec::messagepack::serialize(msg));
let call_result = ok_or_bail!(pool.call(name, msg).await); // let call_result = ok_or_bail!(pool.call(name, msg).await);
let result: T = ok_or_bail!(wapc_codec::messagepack::deserialize(&call_result)); // let result: T =
if let Err(e) = tx.send(f(result)) { // ok_or_bail!(wapc_codec::messagepack::deserialize(&call_result)); if
tracing::error!("{e:?}"); // let Err(e) = tx.send(f(result)) { tracing::error!("{e:?}");
Err(()) // Err(())
} else { // } else {
Ok(()) // Ok(())
} // }
} // }
} // }
//
#[derive(Clone)] // #[derive(Clone)]
pub struct Modules(Arc<std::sync::Mutex<HashMap<String, ModuleSender>>>); // pub struct Modules(Arc<std::sync::Mutex<HashMap<String, ModuleSender>>>);
//
impl Modules { // impl Modules {
pub fn new(h: HashMap<String, ModuleSender>) -> Self { // pub fn new(h: HashMap<String, ModuleSender>) -> Self {
Self(Arc::new(std::sync::Mutex::new(h))) // Self(Arc::new(std::sync::Mutex::new(h)))
} // }
//
pub fn lock(&self) -> LockResult<MutexGuard<'_, HashMap<String, ModuleSender>>> { // pub fn lock(&self) -> LockResult<MutexGuard<'_, HashMap<String,
self.0.lock() // ModuleSender>>> { self.0.lock()
} // }
} // }
//
#[derive(gumdrop::Options)] // #[derive(gumdrop::Options)]
pub struct Opts { // pub struct Opts {
pub adapters_path: Option<PathBuf>, // pub adapters_path: Option<PathBuf>,
} // }
//
impl UpdateConfig for Opts { // impl UpdateConfig for Opts {
fn update_config(&self, config: &mut AppConfig) { // fn update_config(&self, config: &mut AppConfig) {
if let Some(path) = self.adapters_path.clone() { // if let Some(path) = self.adapters_path.clone() {
config.payment_mut().adapters_path = Some(path); // config.payment_mut().adapters_path = Some(path);
} // }
} // }
} // }
//
#[tokio::main] // #[tokio::main]
async fn main() { // async fn main() {
config::init_tracing("payments"); // config::init_tracing("payments");
//
let opts: Opts = gumdrop::parse_args_default_or_exit(); // let opts: Opts = gumdrop::parse_args_default_or_exit();
//
let config = config::default_load(&opts); // let config = config::default_load(&opts);
let files = scan_adapters_dir(&config); // let files = scan_adapters_dir(&config);
let mut modules = HashMap::with_capacity(files.len()); // let mut modules = HashMap::with_capacity(files.len());
//
load_adapters(&config, &mut modules, files).await; // load_adapters(&config, &mut modules, files).await;
//
// for pool in modules.values() { // // for pool in modules.values() {
// let msg = payment_adapter::CreatePayment { // // let msg = payment_adapter::CreatePayment {
// buyer: payment_adapter::Buyer { // // buyer: payment_adapter::Buyer {
// email: "hello@example.com".to_string(), // // email: "hello@example.com".to_string(),
// phone: "530698478".to_string(), // // phone: "530698478".to_string(),
// first_name: "Joe".to_string(), // // first_name: "Joe".to_string(),
// last_name: "Doe".to_string(), // // last_name: "Doe".to_string(),
// language: "pl".to_string(), // // language: "pl".to_string(),
// }, // // },
// customer_ip: "12.22.34.54".to_string(), // // customer_ip: "12.22.34.54".to_string(),
// currency: "PLN".to_string(), // // currency: "PLN".to_string(),
// description: "Nesciunt fugit libero quis dolorum quo. // // description: "Nesciunt fugit libero quis dolorum quo.
// Tempore aut nisi voluptatem. Odio et aspernatur est. Sint vel // // Tempore aut nisi voluptatem. Odio et aspernatur est. Sint vel
// molestias sunt cumque quibusdam reprehenderit est.".to_string(), // // molestias sunt cumque quibusdam reprehenderit est.".to_string(),
// cart_products: vec![Product { // // cart_products: vec![Product {
// id: 23, // // id: 23,
// name: "Socks".to_string(), // // name: "Socks".to_string(),
// unit_price: 1542, // // unit_price: 1542,
// quantity_unit: "Unit".to_string(), // // quantity_unit: "Unit".to_string(),
// quantity: 2, // // quantity: 2,
// }], // // }],
// items: vec![Item { // // items: vec![Item {
// product_id: 23, // // product_id: 23,
// quantity: 2, // // quantity: 2,
// quantity_unit: "Unit".to_string(), // // quantity_unit: "Unit".to_string(),
// }], // // }],
// order_ext_id: None, // // order_ext_id: None,
// notify_uri: "https://localhost:3030/notify_uri".to_string(), // // notify_uri: "https://localhost:3030/notify_uri".to_string(),
// continue_uri: "https://localhost:3030/continue_uri".to_string(), // // continue_uri: "https://localhost:3030/continue_uri".to_string(),
// }; // // };
// // //
// tracing::info!("Start create_payment"); // // tracing::info!("Start create_payment");
// let call_result = pool // // let call_result = pool
// .call( // // .call(
// "create_payment", // // "create_payment",
// wapc_codec::messagepack::serialize(msg).unwrap(), // // wapc_codec::messagepack::serialize(msg).unwrap(),
// ) // // )
// .await // // .await
// .unwrap(); // // .unwrap();
// let result: payment_adapter::OrderCreated = // // let result: payment_adapter::OrderCreated =
// wapc_codec::messagepack::deserialize(&call_result).unwrap(); // // wapc_codec::messagepack::deserialize(&call_result).unwrap();
// // //
// tracing::info!("create payment res {:?}", result) // // tracing::info!("create payment res {:?}", result)
// } // // }
//
let modules = Modules::new(modules); // let modules = Modules::new(modules);
let mqtt_client = mqtt::start(config.clone(), modules.clone()).await; // let mqtt_client = mqtt::start(config.clone(), modules.clone()).await;
rpc::start(config, mqtt_client, modules.clone()).await; // rpc::start(config, mqtt_client, modules.clone()).await;
} // }
//
async fn load_adapters( // async fn load_adapters(
config: &SharedAppConfig, // config: &SharedAppConfig,
modules: &mut HashMap<String, ModuleSender>, // modules: &mut HashMap<String, ModuleSender>,
files: Vec<PathBuf>, // files: Vec<PathBuf>,
) { // ) {
for file in files { // for file in files {
let module = std::fs::read(&file).unwrap(); // let module = std::fs::read(&file).unwrap();
let engine = wasmtime_provider::WasmtimeEngineProviderBuilder::new() // let engine = wasmtime_provider::WasmtimeEngineProviderBuilder::new()
.module_bytes(&module) // .module_bytes(&module)
.wasi_params(WasiParams::default()) // .wasi_params(WasiParams::default())
.build() // .build()
.unwrap(); // .unwrap();
//
let pool = HostPoolBuilder::new() // let pool = HostPoolBuilder::new()
.name("pool") // .name("pool")
.factory(move || { // .factory(move || {
wapc::WapcHost::new( // wapc::WapcHost::new(
Box::new(engine.clone()), // Box::new(engine.clone()),
Some(Box::new( // Some(Box::new(
move |_a, binding, _namespace, msg_name, payload| { // move |_a, binding, _namespace, msg_name, payload| {
Ok(host_call(binding, msg_name, payload)?) // Ok(host_call(binding, msg_name, payload)?)
}, // },
)), // )),
) // )
.unwrap() // .unwrap()
}) // })
.max_threads(5) // .max_threads(5)
.build(); // .build();
let name = pool // let name = pool
.call("name", vec![]) // .call("name", vec![])
.await // .await
.unwrap_or_else(|e| panic!("Failed to load adapter {file:?} `name`. {e}")); // .unwrap_or_else(|e| panic!("Failed to load adapter {file:?}
let name: String = wapc_codec::messagepack::deserialize(&name) // `name`. {e}")); let name: String =
.unwrap_or_else(|e| panic!("Adapter `name` ({name:?}) is not valid string. {e}")); // wapc_codec::messagepack::deserialize(&name) .unwrap_or_else(|e|
// panic!("Adapter `name` ({name:?}) is not valid string. {e}"));
let msg = config //
.lock() // let msg = config
.payment() // .lock()
.providers // .payment()
.get(&name) // .providers
.cloned() // .get(&name)
.unwrap_or_default(); // .cloned()
// .unwrap_or_default();
tracing::info!("Start init"); //
pool.call("init", wapc_codec::messagepack::serialize(msg).unwrap()) // tracing::info!("Start init");
.await // pool.call("init", wapc_codec::messagepack::serialize(msg).unwrap())
.unwrap(); // .await
// .unwrap();
let (tx, rx) = std::sync::mpsc::channel(); //
HostChannel { // let (tx, rx) = std::sync::mpsc::channel();
host: pool, // HostChannel {
channel: rx, // host: pool,
} // channel: rx,
.start(); // }
// .start();
modules.insert(name, ModuleSender::new(tx)); //
} // modules.insert(name, ModuleSender::new(tx));
} // }
// }
fn scan_adapters_dir(config: &SharedAppConfig) -> Vec<PathBuf> { //
let adapters_path = config.lock().payment().adapters_path.clone(); // fn scan_adapters_dir(config: &SharedAppConfig) -> Vec<PathBuf> {
let adapters = adapters_path.unwrap_or_else(|| panic!("No payment adapters path provided")); // let adapters_path = config.lock().payment().adapters_path.clone();
let dir = read_dir(&adapters).unwrap_or_else(|e| { // let adapters = adapters_path.unwrap_or_else(|| panic!("No payment
panic!( // adapters path provided")); let dir =
"Failed to load payment adapters at path {:?}. {}", // read_dir(&adapters).unwrap_or_else(|e| { panic!(
adapters, e // "Failed to load payment adapters at path {:?}. {}",
) // adapters, e
}); // )
let files = dir // });
.filter_map(|r| r.map(|r| r.path()).ok()) // let files = dir
.filter(|file| file.extension().and_then(|s| s.to_str()) == Some("wasm")) // .filter_map(|r| r.map(|r| r.path()).ok())
.collect::<Vec<_>>(); // .filter(|file| file.extension().and_then(|s| s.to_str()) ==
// Some("wasm")) .collect::<Vec<_>>();
if files.is_empty() { //
panic!("No payment adapters found in adapters directory"); // if files.is_empty() {
} // panic!("No payment adapters found in adapters directory");
files // }
} // files
// }
//
// // #[tracing::instrument]
// fn host_call(binding: &str, name: &str, payload: &[u8]) -> Result<Vec<u8>,
// payment_adapter::Error> { match name {
// "http_req" => {
// let req: HttpRequest = match
// wapc_codec::messagepack::deserialize(payload) { Ok(req) =>
// req, Err(e) => {
// tracing::error!("{:?} payload {:?}", binding, payload);
// tracing::error!("Failed to deserialize. {}", e);
// return
// Err(payment_adapter::Error::MsgPackDeserializationFailed); }
// };
// http_request(req)
// }
// _ => Err(payment_adapter::Error::UnknownOperation(name.to_string())),
// }
// }
//
// #[tracing::instrument] // #[tracing::instrument]
fn host_call(binding: &str, name: &str, payload: &[u8]) -> Result<Vec<u8>, payment_adapter::Error> { // fn http_request(req: HttpRequest) -> Result<Vec<u8>, payment_adapter::Error>
match name { // { tracing::info!("{:?}", req);
"http_req" => { // let HttpRequest {
let req: HttpRequest = match wapc_codec::messagepack::deserialize(payload) { // method,
Ok(req) => req, // url,
Err(e) => { // headers,
tracing::error!("{:?} payload {:?}", binding, payload); // bearer_auth,
tracing::error!("Failed to deserialize. {}", e); // body,
return Err(payment_adapter::Error::MsgPackDeserializationFailed); // } = req;
} // let client = reqwest::blocking::ClientBuilder::default()
}; // .user_agent("curl/7.82.0")
http_request(req) // // Do not follow redirect!
} // .redirect(reqwest::redirect::Policy::none())
_ => Err(payment_adapter::Error::UnknownOperation(name.to_string())), // .connection_verbose(true)
} // .build()
} // .expect("Failed to create client");
//
// let headers = {
// let len = headers.len();
// headers
// .into_iter()
// .fold(HeaderMap::with_capacity(len), |mut m, (k, v)| {
// if let (Ok(v), Ok(k)) =
// (HeaderValue::from_str(v.as_str()),
// HeaderName::from_str(&k)) {
// m.insert(k, v);
// }
// m
// })
// };
//
// let response = match (method, bearer_auth) {
// (HttpMethod::Get, Some(bearer)) => client
// .get(url)
// .headers(headers)
// .bearer_auth(bearer)
// .send()
// .map_err(|e| {
// tracing::error!("{}", e);
// payment_adapter::Error::HttpFailed
// })?,
// (HttpMethod::Get, _) =>
// client.get(url).headers(headers).send().map_err(|e| {
// tracing::error!("{}", e); payment_adapter::Error::HttpFailed
// })?,
// (HttpMethod::Post, Some(bearer)) => client
// .post(url)
// .headers(headers)
// .bearer_auth(bearer)
// .body(body.unwrap_or_default())
// .send()
// .map_err(|e| {
// tracing::error!("{}", e);
// payment_adapter::Error::HttpFailed
// })?,
// (HttpMethod::Post, None) => client
// .post(url)
// .headers(headers)
// .body(body.unwrap_or_default())
// .send()
// .map_err(|e| {
// eprintln!("POST NONE {}", e);
// tracing::error!("{}", e);
// payment_adapter::Error::HttpFailed
// })?,
// };
// tracing::info!("HTTP response status {:?}", response.status());
// let text = response.bytes().map_err(|e| {
// tracing::error!("{}", e);
// payment_adapter::Error::HttpFailed
// })?;
// tracing::info!("HTTP Result {:?}", text);
// Ok(text.to_vec())
// }
#[tracing::instrument] fn main() {}
fn http_request(req: HttpRequest) -> Result<Vec<u8>, payment_adapter::Error> {
tracing::info!("{:?}", req);
let HttpRequest {
method,
url,
headers,
bearer_auth,
body,
} = req;
let client = reqwest::blocking::ClientBuilder::default()
.user_agent("curl/7.82.0")
// Do not follow redirect!
.redirect(reqwest::redirect::Policy::none())
.connection_verbose(true)
.build()
.expect("Failed to create client");
let headers = {
let len = headers.len();
headers
.into_iter()
.fold(HeaderMap::with_capacity(len), |mut m, (k, v)| {
if let (Ok(v), Ok(k)) =
(HeaderValue::from_str(v.as_str()), HeaderName::from_str(&k))
{
m.insert(k, v);
}
m
})
};
let response = match (method, bearer_auth) {
(HttpMethod::Get, Some(bearer)) => client
.get(url)
.headers(headers)
.bearer_auth(bearer)
.send()
.map_err(|e| {
tracing::error!("{}", e);
payment_adapter::Error::HttpFailed
})?,
(HttpMethod::Get, _) => client.get(url).headers(headers).send().map_err(|e| {
tracing::error!("{}", e);
payment_adapter::Error::HttpFailed
})?,
(HttpMethod::Post, Some(bearer)) => client
.post(url)
.headers(headers)
.bearer_auth(bearer)
.body(body.unwrap_or_default())
.send()
.map_err(|e| {
tracing::error!("{}", e);
payment_adapter::Error::HttpFailed
})?,
(HttpMethod::Post, None) => client
.post(url)
.headers(headers)
.body(body.unwrap_or_default())
.send()
.map_err(|e| {
eprintln!("POST NONE {}", e);
tracing::error!("{}", e);
payment_adapter::Error::HttpFailed
})?,
};
tracing::info!("HTTP response status {:?}", response.status());
let text = response.bytes().map_err(|e| {
tracing::error!("{}", e);
payment_adapter::Error::HttpFailed
})?;
tracing::info!("HTTP Result {:?}", text);
Ok(text.to_vec())
}

View File

@ -7,7 +7,7 @@ use config::SharedAppConfig;
use payment_adapter::RefundPayment; use payment_adapter::RefundPayment;
use tarpc::context; use tarpc::context;
use crate::{Modules, ResultMsg, WasmMsg}; use crate::{Modules, ResultMsg};
#[derive(Clone)] #[derive(Clone)]
pub struct ModuleSender { pub struct ModuleSender {
@ -59,7 +59,7 @@ impl PaymentsServer {
.clone()) .clone())
} }
} }
/*
#[tarpc::server] #[tarpc::server]
impl Payments for PaymentsServer { impl Payments for PaymentsServer {
async fn start_payment( async fn start_payment(
@ -156,3 +156,4 @@ pub async fn start(config: SharedAppConfig, mqtt_client: AsyncClient, modules: M
}) })
.await; .await;
} }
*/

View File

@ -13,6 +13,7 @@ rustflags = ["-C", "prefer-dynamic", "-C", "rpath"]
payment_adapter = { path = "../payment_adapter" } payment_adapter = { path = "../payment_adapter" }
fulfillment_adapter = { path = "../fulfillment_adapter" } fulfillment_adapter = { path = "../fulfillment_adapter" }
tokio = { version = "1.27.0" } tokio = { version = "1.27.0" }
payup = { version = "*" } 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'] }

View File

@ -1,126 +1,242 @@
#![crate_type = "rlib"] #![crate_type = "rlib"]
use fulfillment_adapter::{ use std::any::Any;
FError, FResult, FulfillmentAdapter, FulfillmentCart, FulfillmentOption, FulfillmentPayload, use std::collections::HashMap;
}; use std::ops::DerefMut;
use payment_adapter::{
CreatePayment, DeletePayment, Deleted, PResult, PaymentAdapter, PaymentCreated,
PaymentSessionStatus, RefundPayment, Refunded, Status,
};
pub struct StripeAdapter { use fulfillment_adapter::*;
stripe: payup::stripe::Auth, use payment_adapter::*;
use payup::stripe;
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub enum CaptureMethod {
Automatic,
Manual,
} }
impl StripeAdapter { #[derive(Debug, serde::Deserialize, serde::Serialize)]
fn payment_status(&mut self, ext_id: String) -> FResult<PaymentSessionStatus> { pub enum SetupFutureUsage {
let charge = payup::stripe::Charge::get(self.stripe.clone(), ext_id).map_err(|e| { OnSession,
OffSession,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct StripeConfig {
pub api_key: String,
pub client: String,
}
#[derive(Debug)]
pub struct StripeIntent {
pub capture_method: CaptureMethod,
pub setup_future_usage: SetupFutureUsage,
pub payment_method_types: &'static [&'static str],
}
pub struct StripeAdapter {
stripe: stripe::Auth,
config: StripeConfig,
}
async fn payment_status(stripe: stripe::Auth, ext_id: String) -> FResult<PaymentSessionStatus> {
let charge: stripe::Charge = stripe::Charge::async_get(stripe, ext_id)
.await
.map_err(|e| {
tracing::warn!("{e}"); tracing::warn!("{e}");
FError::HttpError FError::HttpError
})?; })?;
Ok(match charge.status.as_deref() { Ok(match charge.status.as_deref() {
Some("requires_payment_method" | "requires_confirmation" | "processing") => { Some("requires_payment_method" | "requires_confirmation" | "processing") => {
PaymentSessionStatus::Pending PaymentSessionStatus::Pending
} }
Some("requires_action") => PaymentSessionStatus::RequiresMore, Some("requires_action") => PaymentSessionStatus::RequiresMore,
Some("canceled") => PaymentSessionStatus::Canceled, Some("canceled") => PaymentSessionStatus::Canceled,
Some("requires_capture" | "succeeded") => PaymentSessionStatus::Authorized, Some("requires_capture" | "succeeded") => PaymentSessionStatus::Authorized,
_ => PaymentSessionStatus::Pending, _ => PaymentSessionStatus::Pending,
})
}
#[derive(serde::Deserialize, serde::Serialize)]
pub struct StripePrzelewy24Config {
pub api_key: String,
pub client: String,
pub webhook_secret: String,
pub capture: Option<bool>,
pub automatic_payment_methods: Option<bool>,
pub payment_description: Option<String>,
}
pub struct StripePrzelewy24 {
stripe: stripe::Auth,
config: StripePrzelewy24Config,
}
#[async_trait::async_trait]
impl PaymentAdapter for StripePrzelewy24 {
async fn new(config: Config) -> Self {
let config: StripePrzelewy24Config =
config.config().expect("Malformed Stripe Przelewy24 config");
let stripe = stripe::Auth::new(config.client.clone(), config.api_key.clone());
Self { config, stripe }
}
async fn initialize_payment(
&mut self,
ctx: PaymentProcessorContext,
) -> PResult<PaymentProcessorSessionResponse> {
let PaymentProcessorContext {
billing_address: _,
email,
currency_code,
amount,
resource_id: _,
customer,
context,
payment_session_data,
} = ctx;
let desc = context
.0
.get("payment_description")
.and_then(|v| String::from_utf8(v.0.clone()).ok())
.or_else(|| self.config.payment_description.clone());
let change: stripe::Charge = stripe::Charge {
amount: Some(amount.0.to_string()),
captured: self.config.capture.clone(),
currency: Some(currency_code.into()),
description: desc,
customer: customer.map(|c| c.id.0.to_string()),
receipt_email: Some(email.0),
..stripe::Charge::new()
}
.async_post(self.stripe.clone())
.await
.unwrap();
let update_requests = match customer {
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()),
)]))),
}),
_ => None,
};
Ok(PaymentProcessorSessionResponse {
update_requests,
session_data: payment_session_data,
}) })
} }
}
impl PaymentAdapter for StripeAdapter { async fn update_payment(
fn init(&mut self) -> PResult<Status> {
todo!()
}
fn teardown(&mut self) -> PResult<Status> {
todo!()
}
async fn create_payment(&mut self, msg: CreatePayment) -> PResult<PaymentCreated> {
todo!()
}
async fn retrieve_payment(&mut self) {
todo!()
}
async fn authorize_payment(&mut self) {
todo!()
}
fn capture_payment(&mut self) {
todo!()
}
async fn refund_payment(&mut self, msg: RefundPayment) -> PResult<Refunded> {
todo!()
}
async fn delete_payment(&mut self, msg: DeletePayment) -> PResult<Deleted> {
todo!()
}
}
impl FulfillmentAdapter for StripeAdapter {
fn identifier() -> &'static str {
"stripe"
}
fn fulfillment_options(&mut self) -> FResult<&[FulfillmentOption]> {
todo!()
}
fn validate_fulfillment_payload<P: FulfillmentPayload, C: FulfillmentCart>(
&mut self, &mut self,
payload: P, ctx: PaymentProcessorContext,
cart: C, ) -> PResult<Option<PaymentProcessorSessionResponse>> {
) -> FResult<bool> {
todo!() todo!()
} }
fn validate_option<P: FulfillmentPayload>(&mut self, payload: P) -> FResult<bool> { async fn refund_payment(
&mut self,
payment_session_data: PaymentSessionData,
refund_amount: Amount,
) -> PResult<PaymentSessionData> {
todo!() todo!()
} }
fn can_calculate<P: FulfillmentPayload>(&mut self, payload: P) -> FResult<bool> { async fn authorize_payment(
&mut self,
payment_session_data: PaymentSessionData,
data: PaymentProcessCtx,
) -> PResult<(PaymentSessionStatus, PaymentSessionData)> {
self.payment_status(&payment_session_data)
.await
.map(|status| (status, payment_session_data))
}
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)?;
let charge = stripe::Charge::async_get(self.stripe.clone(), id.clone())
.await
.map_err(|_| PError::ChargeNotExists(id.clone()))?;
let change = charge
.async_capture(self.stripe.clone())
.await
.map_err(|e| {
tracing::warn!("{e}");
PError::FailedCapture(id)
})?;
payment_session_data.id = charge.id;
Ok(payment_session_data)
}
async fn delete_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData> {
todo!() todo!()
} }
fn calculate_price<P: FulfillmentPayload, C: FulfillmentCart>( async fn retrieve_payment(
data: P, &mut self,
cart: C, payment_session_data: PaymentSessionData,
) -> FResult<u64> { ) -> PResult<PaymentSessionData> {
todo!() todo!()
} }
fn create_fulfillment(&mut self) { async fn cancel_payment(
&mut self,
payment_session_data: PaymentSessionData,
) -> PResult<PaymentSessionData> {
todo!() todo!()
} }
fn cancel_fulfillment(&mut self) { async fn payment_status(
todo!() &mut self,
} payment_session_data: &PaymentSessionData,
) -> PResult<PaymentSessionStatus> {
fn fulfillment_documents(&mut self) { payment_status(
todo!() self.stripe.clone(),
} payment_session_data.id.clone().unwrap_or_default(),
)
fn create_return(&mut self) { .await
todo!() .map_err(|_| PError::HttpError)
} }
}
fn return_documents(&mut self) {
todo!() impl StripePrzelewy24 {
} pub fn payment_intent_options() -> StripeIntent {
StripeIntent {
fn shipment_documents(&mut self) { capture_method: CaptureMethod::Automatic,
todo!() setup_future_usage: SetupFutureUsage::OnSession,
} payment_method_types: &["p24"],
}
fn documents(&mut self) { }
todo!() }
pub trait StripeCustomerMetadata {
fn has_stripe_id(&self) -> bool;
fn stripe_id(&self) -> Option<String>;
}
impl StripeCustomerMetadata for Customer {
fn has_stripe_id(&self) -> bool {
self.metadata
.as_ref()
.map_or_else(|| false, |h| h.contains_key("stripe_id"))
}
fn stripe_id(&self) -> Option<String> {
self.metadata
.as_ref()?
.get("stripe_id")
.and_then(|v| String::from_utf8(v.clone()).ok())
} }
} }