From 9b61b794833248c2cdac663badd60eead514fa7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Thu, 15 Dec 2022 14:42:08 +0100 Subject: [PATCH] Working create order with Pay U --- Cargo.lock | 1 + crates/payment_adapter/src/lib.rs | 44 +++++- crates/payment_adapter_pay_u/Cargo.lock | 9 +- crates/payment_adapter_pay_u/Cargo.toml | 4 +- crates/payment_adapter_pay_u/src/lib.rs | 140 +++++++++-------- crates/payment_manager/Cargo.toml | 7 +- crates/payment_manager/src/main.rs | 193 +++++++++++------------- 7 files changed, 221 insertions(+), 177 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a025d91..692d626 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3322,6 +3322,7 @@ dependencies = [ "wapc", "wapc-codec", "wapc-pool", + "wasmtime", "wasmtime-provider", ] diff --git a/crates/payment_adapter/src/lib.rs b/crates/payment_adapter/src/lib.rs index bf78267..4a59b7f 100644 --- a/crates/payment_adapter/src/lib.rs +++ b/crates/payment_adapter/src/lib.rs @@ -4,15 +4,23 @@ pub use uuid; pub const CONFIG_POS: u32 = 3; -#[derive(Debug, Clone, Copy, thiserror::Error, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, thiserror::Error, serde::Serialize, serde::Deserialize)] #[repr(C)] pub enum Error { #[error("Malformed create payment message")] MalformedCreatePayment, + #[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)] @@ -64,7 +72,7 @@ pub struct CreatePayment { pub description: String, pub cart_products: Vec, pub items: Vec, - pub order_ext_id: String, + pub order_ext_id: Option, pub notify_uri: String, pub continue_uri: String, } @@ -72,8 +80,8 @@ pub struct CreatePayment { #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[repr(C)] pub struct OrderCreated { - pub ext_order_id: ExtOrderId, - pub redirect_uri: String, + pub ext_order_id: Option, + pub redirect_uri: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -93,6 +101,34 @@ pub struct HttpRequest { pub body: Option>, } +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); +} + // #[tarpc::service] // pub trait PaymentAdapter { // async fn create_payment(msg: CreatePayment) -> Status; diff --git a/crates/payment_adapter_pay_u/Cargo.lock b/crates/payment_adapter_pay_u/Cargo.lock index 434579a..4cf9cf3 100644 --- a/crates/payment_adapter_pay_u/Cargo.lock +++ b/crates/payment_adapter_pay_u/Cargo.lock @@ -127,6 +127,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "common_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3f6d59c71e7dc3af60f0af9db32364d96a16e9310f3f5db2b55ed642162dd35" + [[package]] name = "config" version = "0.1.0" @@ -670,10 +676,11 @@ version = "0.1.0" dependencies = [ "bincode", "chrono", + "common_macros", "payment_adapter", "serde", + "serde_json", "thiserror", - "tracing", "wapc-codec", "wapc-guest", ] diff --git a/crates/payment_adapter_pay_u/Cargo.toml b/crates/payment_adapter_pay_u/Cargo.toml index 2f16e58..de1f4cc 100644 --- a/crates/payment_adapter_pay_u/Cargo.toml +++ b/crates/payment_adapter_pay_u/Cargo.toml @@ -11,7 +11,9 @@ payment_adapter = { path = "../payment_adapter" } bincode = { version = "1.3.3" } wapc-codec = { version = "1.0.0" } wapc-guest = { version = "1.0.0" } -tracing = { version = "0.1.37" } +#tracing = { version = "0.1.37" } chrono = { version = "0.4.23", features = ['alloc', 'wasmbind'] } serde = { version = "1.0.149", features = ['derive'] } thiserror = { version = "1.0.37" } +serde_json = { version = "1.0.89" } +common_macros = { version = "0.1.1" } diff --git a/crates/payment_adapter_pay_u/src/lib.rs b/crates/payment_adapter_pay_u/src/lib.rs index 47dcf54..dad6de8 100644 --- a/crates/payment_adapter_pay_u/src/lib.rs +++ b/crates/payment_adapter_pay_u/src/lib.rs @@ -28,11 +28,11 @@ impl PayU { pub fn new(client_id: String, client_secret: String, merchant_pos_id: i32) -> Self { Self { bearer: None, - sandbox: false, + sandbox: true, merchant_pos_id, client_id, client_secret, - bearer_expires_at: chrono::Utc::now(), + bearer_expires_at: chrono::DateTime::default(), } } @@ -43,6 +43,14 @@ impl PayU { "https://secure.payu.com/api/v2_1" } } + + fn auth_url(&self) -> &str { + if self.sandbox { + "https://secure.snd.payu.com/pl/standard/user/oauth/authorize" + } else { + "https://secure.payu.com/pl/standard/user/oauth/authorize" + } + } } static mut CONFIG: Option>> = None; @@ -92,11 +100,7 @@ impl PayU { _ => return Err(Box::new(Error::MalformedCreatePayment)), }; - wapc::console_log(&format!( - "IN_WASM: Received request for `ping` operation with payload : {:?}", - c - )); - eprintln!("{:?}", c); + info!("{:?}", c); let res::CreateOrder { status: _, @@ -105,8 +109,8 @@ impl PayU { ext_order_id: _, } = create_payment(c)?; Ok(serialize(OrderCreated { - redirect_uri, - ext_order_id: ExtOrderId::new(order_id), + redirect_uri: Some(redirect_uri), + ext_order_id: Some(ExtOrderId::new(order_id)), }) .unwrap()) } @@ -116,10 +120,17 @@ impl PayU { } } +fn json_header() -> Vec<(String, String)> { + vec![ + ("Content-Type".into(), "application/json".into()), + ("Accept".into(), "application/json".into()), + ] +} + fn create_payment(c: CreatePayment) -> Result { - eprintln!("Authorizing..."); + info!("Authorizing..."); authorize()?; - eprintln!("Authorized!"); + info!("Authorized!"); let config = client(); let pay_u = config.lock().unwrap(); @@ -145,7 +156,7 @@ fn create_payment(c: CreatePayment) -> Result { h }) }; - let create_order = OrderCreate::build( + let mut create_order = OrderCreate::build( { let Buyer { email, @@ -162,10 +173,10 @@ fn create_payment(c: CreatePayment) -> Result { description, ) .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); + error!("{}", e); Error::MalformedCreatePayment })? + .with_merchant_pos_id(pay_u.merchant_pos_id) .with_products(cart_products.into_iter().map(|p| { model::Product::new( p.name.to_string(), @@ -173,35 +184,37 @@ fn create_payment(c: CreatePayment) -> Result { quantities.get(&p.id).copied().unwrap_or_default() as model::Quantity, ) })) - .with_ext_order_id(order_ext_id) .with_notify_url(notify_uri) .with_continue_url(continue_uri); - eprintln!("calling host http 'create order'!"); - let res = wapc::host_call( - "binding", - "http:req", - "http_req", - &serialize(HttpRequest { + if let Some(order_ext_id) = order_ext_id { + create_order = create_order.with_ext_order_id(order_ext_id); + } + + info!("calling host http 'create order'!"); + let v = { + let msg = serialize(HttpRequest { method: HttpMethod::Post, url: format!("{}/orders", pay_u.base_url()), - headers: vec![], + headers: json_header(), bearer_auth: pay_u.bearer.clone(), - body: serialize(create_order).ok(), + body: Some( + serde_json::to_string_pretty(&create_order) + .unwrap() + .into_bytes(), + ), }) - .unwrap(), - ) - .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - Error::PaymentFailed - }) - .and_then(|v| { - deserialize(&v).map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); + .unwrap(); + info!("create order msg {:?}", msg); + wapc::host_call("create:order", "http:req", "http_req", &msg).map_err(|e| { + error!("{}", e); Error::PaymentFailed - }) + })? + }; + info!("host_call deserialize buffer {:?}", v); + let res: res::CreateOrder = serde_json::from_slice(&v).map_err(|e| { + error!("{}", e); + Error::PaymentFailed })?; Ok(res) } @@ -217,7 +230,10 @@ fn authorize() -> Result { let config = client(); let mut pay_u = config.lock().unwrap(); - if Utc::now() - Duration::seconds(1) < pay_u.bearer_expires_at { + let now = Utc::now() - Duration::seconds(1); + info!("Now is {:?}", now); + if now < pay_u.bearer_expires_at && pay_u.bearer.is_some() { + info!("Bearer ok"); return Ok(true); } #[derive(serde::Deserialize)] @@ -226,33 +242,33 @@ fn authorize() -> Result { expires_in: i64, } - eprintln!("calling host http 'authorize'!"); - let res = wapc::host_call("binding", "http:req", "http_req", &serialize(HttpRequest { - method: HttpMethod::Post, - url: format!( - "https://secure.payu.com/pl/standard/user/oauth/authorize?grant_type=client_credentials&client_id={}&client_secret={}", - pay_u.client_id, - pay_u.client_secret - ), - headers: vec![], - bearer_auth: None, - body: None, - }).map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - Error::PaymentFailed - })?).map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - Error::PaymentFailed - })?; - let res: BearerResult = deserialize(&res).map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - Error::PaymentFailed + info!("calling host http 'authorize'!"); + let res = { + let msg = serialize(HttpRequest { + method: HttpMethod::Post, + url: format!( + "{}?grant_type=client_credentials&client_id={}&client_secret={}", + pay_u.auth_url(), + pay_u.client_id, + pay_u.client_secret, + ), + headers: json_header(), + bearer_auth: None, + body: None, + }) + .unwrap(); + info!("authorize msg {:?}", msg); + wapc::host_call("authorize", "http:req", "http_req", &msg).map_err(|e| { + error!("{}", e); + Error::AuthorizeFailed + })? + }; + let res: BearerResult = serde_json::from_slice(&res).map_err(|e| { + error!("{}", e); + Error::MalformedAuthorize })?; - eprintln!("Authorized with calling host"); - tracing::trace!("Bearer is {}", res.access_token); + info!("Authorized with calling host"); + trace!("Bearer is {}", res.access_token); pay_u.bearer_expires_at = Utc::now() + Duration::seconds(res.expires_in); pay_u.bearer = Some(res.access_token); Ok(true) diff --git a/crates/payment_manager/Cargo.toml b/crates/payment_manager/Cargo.toml index f6dd983..941ec7d 100644 --- a/crates/payment_manager/Cargo.toml +++ b/crates/payment_manager/Cargo.toml @@ -31,9 +31,10 @@ tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } uuid = { version = "1.2.1", features = ["serde", "v4"] } bincode = { version = "1.3.3" } wapc = { version = "1.0.0", features = [] } -wapc-codec = { version = "1.0.0" } -wapc-pool = { version = "1.0.0" } -wasmtime-provider = { version = "1.3.2", features = [] } +wapc-codec = { version = "1.0.0", features = [] } +wapc-pool = { version = "1.0.0", features = [] } +wasmtime = { version = "3.0.1", features = ['parallel-compilation', 'async'] } +wasmtime-provider = { version = "1.3.2", features = ['wasmtime-wasi', 'wasi-common', 'wasi', 'cache'] } reqwest = { version = "0.11.13", features = ["default", "json", "blocking"] } #pay_u = { path = "../../vendor/pay_u" } diff --git a/crates/payment_manager/src/main.rs b/crates/payment_manager/src/main.rs index 93b489c..b729727 100644 --- a/crates/payment_manager/src/main.rs +++ b/crates/payment_manager/src/main.rs @@ -2,13 +2,11 @@ use std::fs::read_dir; use std::path::PathBuf; use std::str::FromStr; -use bincode::deserialize; use config::{AppConfig, UpdateConfig}; -use payment_adapter::{HttpMethod, HttpRequest}; +use payment_adapter::{HttpMethod, HttpRequest, Item, Product}; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use wapc::WasiParams; use wapc_pool::HostPoolBuilder; -// use payment_adapter::{CreatePayment, PaymentAdapter, Status}; // mod actions; // mod context; @@ -33,7 +31,10 @@ impl UpdateConfig for Opts { #[tokio::main] async fn main() { + std::env::set_var("RUST_LOG", "debug"); + tracing_subscriber::fmt::init(); + let opts: Opts = gumdrop::parse_args_default_or_exit(); let config = config::default_load(&opts); @@ -49,7 +50,7 @@ async fn main() { ) }); for file in dir.filter_map(|r| r.map(|r| r.path()).ok()) { - eprintln!("{:?}", file); + tracing::info!("{:?}", file); if file.extension().and_then(|s| s.to_str()) != Some("wasm") { continue; } @@ -67,8 +68,8 @@ async fn main() { wapc::WapcHost::new( Box::new(engine.clone()), Some(Box::new( - move |_a, _binding, _namespace, msg_name, payload| { - Ok(host_call(msg_name, payload)?) + move |_a, binding, _namespace, msg_name, payload| { + Ok(host_call(binding, msg_name, payload)?) }, )), ) @@ -86,28 +87,40 @@ async fn main() { .cloned() .unwrap_or_default(); + tracing::info!("Start init"); pool.call("init", wapc_codec::messagepack::serialize(msg).unwrap()) .await .unwrap(); let msg = payment_adapter::CreatePayment { buyer: payment_adapter::Buyer { - email: "email".to_string(), - phone: "phone".to_string(), - first_name: "first_name".to_string(), - last_name: "last_name".to_string(), - language: "language".to_string(), + email: "hello@example.com".to_string(), + phone: "530698478".to_string(), + first_name: "Joe".to_string(), + last_name: "Doe".to_string(), + language: "pl".to_string(), }, - customer_ip: "customer_ip".to_string(), - currency: "currency".to_string(), - description: "description".to_string(), - cart_products: vec![], - items: Default::default(), - order_ext_id: "order_ext_id".to_string(), - notify_uri: "notify_uri".to_string(), - continue_uri: "continue_uri".to_string(), + customer_ip: "12.22.34.54".to_string(), + currency: "PLN".to_string(), + description: "Nesciunt fugit libero quis dolorum quo. Tempore aut nisi voluptatem. Odio et aspernatur est. Sint vel molestias sunt cumque quibusdam reprehenderit est.".to_string(), + cart_products: vec![Product { + id: 23, + name: "Socks".to_string(), + unit_price: 1542, + quantity_unit: "Unit".to_string(), + quantity: 2, + }], + items: vec![Item { + product_id: 23, + quantity: 2, + quantity_unit: "Unit".to_string(), + }], + order_ext_id: None, + notify_uri: "https://localhost:3030/notify_uri".to_string(), + continue_uri: "https://localhost:3030/continue_uri".to_string(), }; + tracing::info!("Start create_payment"); let call_result = pool .call( "create_payment", @@ -115,10 +128,10 @@ async fn main() { ) .await .unwrap(); - let result: payment_adapter::Status = + let result: payment_adapter::OrderCreated = wapc_codec::messagepack::deserialize(&call_result).unwrap(); - eprintln!("create payment res {:?}", result) + tracing::info!("create payment res {:?}", result) } } } @@ -129,20 +142,27 @@ async fn main() { // rpc::start(config, db, mqtt_client).await; } -fn host_call(name: &str, payload: &[u8]) -> Result, payment_adapter::Error> { +// #[tracing::instrument] +fn host_call(binding: &str, name: &str, payload: &[u8]) -> Result, payment_adapter::Error> { match name { "http_req" => { - let req: HttpRequest = match deserialize(payload) { + let req: HttpRequest = match wapc_codec::messagepack::deserialize(payload) { Ok(req) => req, - _ => return Ok(vec![]), + Err(e) => { + tracing::error!("{:?} payload {:?}", binding, payload); + tracing::error!("Failed to deserialize. {}", e); + return Err(payment_adapter::Error::MsgPackDeserializationFailed); + } }; http_request(req) } - _ => Ok(vec![]), + _ => Err(payment_adapter::Error::UnknownOperation(name.to_string())), } } +#[tracing::instrument] fn http_request(req: HttpRequest) -> Result, payment_adapter::Error> { + tracing::info!("{:?}", req); let HttpRequest { method, url, @@ -152,7 +172,6 @@ fn http_request(req: HttpRequest) -> Result, payment_adapter::Error> { } = req; let client = reqwest::blocking::ClientBuilder::default() .user_agent("curl/7.82.0") - // .use_native_tls() // Do not follow redirect! .redirect(reqwest::redirect::Policy::none()) .connection_verbose(true) @@ -173,84 +192,46 @@ fn http_request(req: HttpRequest) -> Result, payment_adapter::Error> { }) }; - match (method, bearer_auth) { - (HttpMethod::Get, Some(bearer)) => { - let text = client - .get(url) - .headers(headers) - .bearer_auth(bearer) - .send() - .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - payment_adapter::Error::HttpFailed - })? - .text() - .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - payment_adapter::Error::HttpFailed - })?; - Ok(text.into_bytes()) - } - (HttpMethod::Get, _) => { - let text = client - .get(url) - .headers(headers) - .send() - .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - payment_adapter::Error::HttpFailed - })? - .text() - .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - payment_adapter::Error::HttpFailed - })?; - Ok(text.into_bytes()) - } - (HttpMethod::Post, Some(bearer)) => { - let body = body.unwrap_or_default(); - let body = reqwest::blocking::Body::from(body); - let text = client - .post(url) - .headers(headers) - .bearer_auth(bearer) - .body(body) - .send() - .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - payment_adapter::Error::HttpFailed - })? - .text() - .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - payment_adapter::Error::HttpFailed - })?; - Ok(text.into_bytes()) - } - (HttpMethod::Post, None) => { - let text = client - .post(url) - .headers(headers) - .body(body.unwrap_or_default()) - .send() - .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - payment_adapter::Error::HttpFailed - })? - .text() - .map_err(|e| { - eprintln!("{}", e); - tracing::error!("{}", e); - payment_adapter::Error::HttpFailed - })?; - Ok(text.into_bytes()) - } - } + 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()) }