From 8e142c813de032319bf45eac5998648217c64915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Djk=C3=A1=C5=A5o?= Date: Mon, 15 Apr 2024 19:51:15 +0200 Subject: [PATCH] finally it works --- simple-payment-gateway/src/app.rs | 4 +- .../src/queries/event_transactions.rs | 24 ++- .../queries/mutation_transaction_update.rs | 11 ++ simple-payment-gateway/src/routes/webhooks.rs | 175 +++++++++++------- 4 files changed, 135 insertions(+), 79 deletions(-) diff --git a/simple-payment-gateway/src/app.rs b/simple-payment-gateway/src/app.rs index 7347a2b..7fc8756 100644 --- a/simple-payment-gateway/src/app.rs +++ b/simple-payment-gateway/src/app.rs @@ -49,7 +49,7 @@ pub fn trace_to_std(config: &Config) -> anyhow::Result<()> { Ok(()) } -#[derive(Debug, Clone, Sequence, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Copy, Sequence, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "lowercase")] pub enum PaymentMethodType { Accreditation, @@ -94,7 +94,7 @@ pub fn get_active_payment_methods_from_env() -> anyhow::Result None, }) .map(|g| ActivePaymentMethod { - typ: g.clone(), + typ: g, name: match (g, &locale) { (PaymentMethodType::COD, LocaleCode::Sk) => "Dobierka".to_owned(), (PaymentMethodType::Cash, LocaleCode::Sk) => "Hotovosť".to_owned(), diff --git a/simple-payment-gateway/src/queries/event_transactions.rs b/simple-payment-gateway/src/queries/event_transactions.rs index 65043fe..b9960f4 100644 --- a/simple-payment-gateway/src/queries/event_transactions.rs +++ b/simple-payment-gateway/src/queries/event_transactions.rs @@ -1,4 +1,8 @@ use const_format::concatcp; +use serde::{Deserialize, Serialize}; + +use crate::app::PaymentMethodType; + #[cynic::schema("saleor")] mod schema {} @@ -7,6 +11,7 @@ fragment CheckoutDetails on Checkout { id isShippingRequired deliveryMethod { + __typename ... on Warehouse { name id @@ -94,6 +99,7 @@ subscription PaymentGatewayInitializeSession { data amount sourceObject { + __typename ... on Checkout { ...CheckoutDetails } @@ -117,6 +123,7 @@ subscription transactionInitializeSession { ... on TransactionInitializeSession { data sourceObject { + __typename ... on Checkout { ...CheckoutDetails } @@ -151,6 +158,7 @@ subscription transactionProcessSession { actionType } sourceObject { + __typename ... on Checkout { ...CheckoutDetails } @@ -247,7 +255,7 @@ pub struct TransactionProcessSession2 { pub action: TransactionProcessAction, pub source_object: OrderOrCheckout, pub transaction: TransactionItem, - pub data: Option, + pub data: Option, } #[derive(cynic::QueryFragment, Debug)] @@ -259,7 +267,7 @@ pub struct TransactionProcessAction { #[derive(cynic::QueryFragment, Debug)] #[cynic(graphql_type = "TransactionInitializeSession")] pub struct TransactionInitializeSession2 { - pub data: Option, + pub data: Option, pub source_object: OrderOrCheckout, pub transaction: TransactionItem, pub action: TransactionProcessAction2, @@ -353,7 +361,7 @@ pub struct ShippingMethod { #[derive(cynic::QueryFragment, Debug)] #[cynic(graphql_type = "PaymentGatewayInitializeSession")] pub struct PaymentGatewayInitializeSession2 { - pub data: Option, + pub data: Option, pub amount: Option, pub source_object: OrderOrCheckout, } @@ -494,9 +502,11 @@ pub enum TransactionFlowStrategyEnum { Charge, } -#[derive(cynic::Scalar, Debug, Clone)] -#[cynic(graphql_type = "JSON")] -pub struct Json(pub String); +#[derive(Serialize, Deserialize, Clone, Copy, Debug)] +pub struct ChosenPaymentMethodData { + pub payment_method: PaymentMethodType, +} +cynic::impl_scalar!(ChosenPaymentMethodData, schema::JSON); #[derive(cynic::Scalar, Debug, Clone)] -pub struct PositiveDecimal(pub String); +pub struct PositiveDecimal(pub f32); diff --git a/simple-payment-gateway/src/queries/mutation_transaction_update.rs b/simple-payment-gateway/src/queries/mutation_transaction_update.rs index e2a8b23..5cc4142 100644 --- a/simple-payment-gateway/src/queries/mutation_transaction_update.rs +++ b/simple-payment-gateway/src/queries/mutation_transaction_update.rs @@ -72,16 +72,27 @@ pub enum TransactionUpdateErrorCode { #[derive(cynic::InputObject, Debug, Default)] pub struct TransactionUpdateInput<'a> { + #[cynic(skip_serializing_if = "Option::is_none")] pub name: Option<&'a str>, + #[cynic(skip_serializing_if = "Option::is_none")] pub message: Option<&'a str>, + #[cynic(skip_serializing_if = "Option::is_none")] pub psp_reference: Option<&'a str>, + #[cynic(skip_serializing_if = "Option::is_none")] pub available_actions: Option>, + #[cynic(skip_serializing_if = "Option::is_none")] pub amount_authorized: Option>, + #[cynic(skip_serializing_if = "Option::is_none")] pub amount_charged: Option>, + #[cynic(skip_serializing_if = "Option::is_none")] pub amount_refunded: Option>, + #[cynic(skip_serializing_if = "Option::is_none")] pub amount_canceled: Option>, + #[cynic(skip_serializing_if = "Option::is_none")] pub metadata: Option>>, + #[cynic(skip_serializing_if = "Option::is_none")] pub private_metadata: Option>>, + #[cynic(skip_serializing_if = "Option::is_none")] pub external_url: Option<&'a str>, } diff --git a/simple-payment-gateway/src/routes/webhooks.rs b/simple-payment-gateway/src/routes/webhooks.rs index 63343fd..2dbfd4c 100644 --- a/simple-payment-gateway/src/routes/webhooks.rs +++ b/simple-payment-gateway/src/routes/webhooks.rs @@ -1,7 +1,7 @@ use anyhow::Context; use axum::{extract::State, http::HeaderMap, Json}; use cynic::{http::SurfExt, MutationBuilder}; -use rust_decimal::Decimal; +use rust_decimal::{prelude::FromPrimitive, Decimal}; use saleor_app_sdk::{ headers::SALEOR_API_URL_HEADER, webhooks::{ @@ -52,8 +52,29 @@ pub async fn webhooks( .to_str()? .to_owned(); let event_type = get_webhook_event_type(&headers)?; + debug!("event: {:?}", event_type); - let res: Json = match event_type { + + let res = match create_response(event_type, body, state, &saleor_api_url).await { + Ok(r) => r, + Err(e) => { + error!("Response creation failed: {:?}", e); + return Err(AppError(anyhow::anyhow!(e))); + } + }; + + debug!("res: {}", &res.to_string()); + info!("got webhooks!"); + Ok(res) +} + +async fn create_response( + event_type: EitherWebhookType, + body: String, + state: AppState, + saleor_api_url: &str, +) -> anyhow::Result> { + Ok(match event_type { EitherWebhookType::Sync(a) => match a { // SyncWebhookEventType::PaymentListGateways => { // let gateways = state @@ -110,71 +131,53 @@ pub async fn webhooks( } SyncWebhookEventType::TransactionInitializeSession => { let session_data = serde_json::from_str::(&body)?; - let payment_method: TransactionInitializeSessionData = serde_json::from_str( - &session_data - .data - .context("Missing data field on TransactionInitializeSession2 body")? - .0, - )?; + debug!( "Transaction session initialised with '{:?}' payment method.", - &payment_method.payment_method + &session_data.data ); - let str_payment_method = serde_json::to_string(&payment_method)?; + let payment_method = session_data + .data + .context("Missing Payment Method in request")? + .payment_method; - let app = state.saleor_app.lock().await; - let auth_data = app.apl.get(&saleor_api_url).await?; + let apl_token = state + .saleor_app + .lock() + .await + .apl + .get(saleor_api_url) + .await? + .token; - let operation = TransactionUpdate::build(TransactionUpdateVariables { - id: &session_data.transaction.id, - transaction: Some(TransactionUpdateInput { - message: Some(&str_payment_method), - ..Default::default() - }), - }); - let mut res = surf::post(&saleor_api_url) - .header("authorization-bearer", auth_data.token) - .run_graphql(operation) - .await; + let str_payment_method = + serde_json::to_string(&TransactionInitializeSessionData { payment_method })?; - let mut webhook_result = WebhookResult::Failiure; - if let Ok(r) = &mut res - && let Some(data) = &mut r.data - && let Some(q_res) = &mut data.transaction_update - { - if !q_res.errors.is_empty() { - q_res - .errors - .iter() - .for_each(|e| error!("failed update transaction, {:?}", e)); - } else if q_res.transaction.is_some() { - webhook_result = WebhookResult::Success; - } - } + update_transaction_message( + session_data.transaction.id, + str_payment_method.clone(), + apl_token, + saleor_api_url.to_owned(), + ); Json::from(serde_json::to_value( TransactionInitializeSessionResponse:: { data: None, time: None, - psp_reference: None, + psp_reference: Some( + "New transaction from ".to_owned() + &state.manifest.name, + ), external_url: None, - message: None, - amount: Decimal::from_str(&session_data.action.amount.0)?, - result: match (session_data.action.action_type, webhook_result) { - (TransactionFlowStrategyEnum::Charge, WebhookResult::Success) => { + message: Some(str_payment_method), + amount: Decimal::from_f32(session_data.action.amount.0) + .context("failed to convert f32 to dec")?, + result: match session_data.action.action_type { + TransactionFlowStrategyEnum::Charge => { TransactionSessionResult::ChargeSuccess } - ( - TransactionFlowStrategyEnum::Authorization, - WebhookResult::Success, - ) => TransactionSessionResult::AuthorizationSuccess, - (TransactionFlowStrategyEnum::Charge, WebhookResult::Failiure) => { - TransactionSessionResult::ChargeFailiure + TransactionFlowStrategyEnum::Authorization => { + TransactionSessionResult::AuthorizationSuccess } - ( - TransactionFlowStrategyEnum::Authorization, - WebhookResult::Failiure, - ) => TransactionSessionResult::AuthorizationFailure, }, }, )?) @@ -186,10 +189,7 @@ pub async fn webhooks( psp_reference: "".to_owned(), external_url: None, message: None, - amount: data - .action - .amount - .and_then(|a| Decimal::from_str(&a.0).ok()), + amount: data.action.amount.and_then(|a| Decimal::from_f32(a.0)), result: Some(ChargeRequestedResult::ChargeSuccess), })?) } @@ -200,10 +200,7 @@ pub async fn webhooks( psp_reference: "".to_owned(), external_url: None, message: None, - amount: data - .action - .amount - .and_then(|a| Decimal::from_str(&a.0).ok()), + amount: data.action.amount.and_then(|a| Decimal::from_f32(a.0)), result: Some(RefundRequestedResult::RefundSuccess), })?) } @@ -216,10 +213,7 @@ pub async fn webhooks( psp_reference: "".to_owned(), external_url: None, message: None, - amount: data - .action - .amount - .and_then(|a| Decimal::from_str(&a.0).ok()), + amount: data.action.amount.and_then(|a| Decimal::from_f32(a.0)), result: Some(CancelationRequestedResult::CancelSuccess), }, )?) @@ -234,7 +228,8 @@ pub async fn webhooks( psp_reference: None, external_url: None, message: None, - amount: Decimal::from_str(&data.action.amount.0)?, + amount: Decimal::from_f32(data.action.amount.0) + .context("failed f32 to Decimal")?, result: match data.action.action_type { TransactionFlowStrategyEnum::Charge => { TransactionSessionResult::ChargeSuccess @@ -248,13 +243,53 @@ pub async fn webhooks( _ => Json::from(Value::from_str("")?), }, _ => Json::from(Value::from_str("")?), - }; - debug!("{:?}", res.to_string()); - info!("got webhooks!"); - Ok(res) + }) +} + +fn update_transaction_message( + trans_id: cynic::Id, + str_payment_method: String, + apl_token: String, + saleor_api_url: String, +) { + tokio::spawn(async move { + let operation = TransactionUpdate::build(TransactionUpdateVariables { + id: &trans_id, + transaction: Some(TransactionUpdateInput { + message: Some(&str_payment_method), + ..Default::default() + }), + }); + + debug!("operation: {:?}", serde_json::to_string(&operation)); + + let mut res = surf::post(saleor_api_url) + .header("authorization-bearer", apl_token) + .run_graphql(operation) + .await; + + match &mut res { + Ok(r) => { + if let Some(data) = &mut r.data + && let Some(q_res) = &mut data.transaction_update + { + if !q_res.errors.is_empty() { + q_res + .errors + .iter() + .for_each(|e| error!("failed update transaction, {:?}", e)); + } else if q_res.transaction.is_some() { + debug!("sucessfully set transactions message to payment method"); + } + } + } + Err(e) => error!("Failed updating transaction through gql: {:?}", e), + } + }); } enum WebhookResult { Success, - Failiure, + // NeedsMessageUpdate(&'a str), + Failure, }