2024-03-11 13:11:47 +00:00
|
|
|
use anyhow::Context;
|
|
|
|
use axum::{
|
2024-03-12 19:48:20 +00:00
|
|
|
body::Body,
|
2024-03-11 13:11:47 +00:00
|
|
|
extract::State,
|
|
|
|
http::{HeaderMap, StatusCode},
|
2024-03-12 19:48:20 +00:00
|
|
|
response::Response,
|
|
|
|
Json,
|
2024-03-11 13:11:47 +00:00
|
|
|
};
|
2024-03-14 13:33:22 +00:00
|
|
|
use cynic::{http::SurfExt, MutationBuilder};
|
|
|
|
use rust_decimal::Decimal;
|
2024-03-11 13:11:47 +00:00
|
|
|
use saleor_app_sdk::{
|
|
|
|
headers::SALEOR_API_URL_HEADER,
|
|
|
|
webhooks::{
|
2024-03-14 13:33:22 +00:00
|
|
|
sync_response::{
|
|
|
|
ChargeRequestedResult, PaymentGatewayInitializeSessionResponse, RefundRequestedResult,
|
|
|
|
TransactionChargeRequestedResponse, TransactionInitializeSessionResponse,
|
|
|
|
TransactionProcessSessionResponse, TransactionRefundRequestedResponse,
|
|
|
|
TransactionSessionResult,
|
|
|
|
},
|
2024-03-11 13:11:47 +00:00
|
|
|
utils::{get_webhook_event_type, EitherWebhookType},
|
|
|
|
SyncWebhookEventType,
|
|
|
|
},
|
|
|
|
};
|
2024-03-12 19:48:20 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use serde_json::Value;
|
2024-03-14 13:33:22 +00:00
|
|
|
use std::str::FromStr;
|
2024-03-11 13:11:47 +00:00
|
|
|
use tracing::{debug, error, info};
|
|
|
|
|
2024-03-12 19:48:20 +00:00
|
|
|
use crate::{
|
2024-03-14 13:33:22 +00:00
|
|
|
app::{ActiveGateway, AppError, AppState, GatewayType},
|
|
|
|
queries::{
|
|
|
|
event_transactions::{
|
|
|
|
PaymentGatewayInitializeSession, TransactionActionEnum, TransactionChargeRequested2,
|
|
|
|
TransactionFlowStrategyEnum, TransactionInitializeSession2, TransactionProcessSession,
|
|
|
|
TransactionProcessSession2, TransactionRefundRequested2,
|
|
|
|
},
|
|
|
|
mutation_transaction_update::{
|
|
|
|
TransactionUpdate, TransactionUpdateInput, TransactionUpdateVariables,
|
|
|
|
},
|
|
|
|
},
|
2024-03-12 19:48:20 +00:00
|
|
|
};
|
2024-03-11 13:11:47 +00:00
|
|
|
|
|
|
|
pub async fn webhooks(
|
|
|
|
headers: HeaderMap,
|
|
|
|
State(state): State<AppState>,
|
|
|
|
body: String,
|
2024-03-12 19:48:20 +00:00
|
|
|
) -> Result<Json<Value>, AppError> {
|
2024-03-11 13:11:47 +00:00
|
|
|
debug!("/api/webhooks");
|
|
|
|
debug!("req: {:?}", body);
|
|
|
|
debug!("headers: {:?}", headers);
|
|
|
|
|
2024-03-14 13:33:22 +00:00
|
|
|
let saleor_api_url = headers
|
2024-03-11 13:11:47 +00:00
|
|
|
.get(SALEOR_API_URL_HEADER)
|
|
|
|
.context("missing saleor api url header")?
|
|
|
|
.to_str()?
|
|
|
|
.to_owned();
|
|
|
|
let event_type = get_webhook_event_type(&headers)?;
|
2024-03-12 19:48:20 +00:00
|
|
|
let res: Json<Value> = match event_type {
|
2024-03-11 13:11:47 +00:00
|
|
|
EitherWebhookType::Sync(a) => match a {
|
2024-03-14 13:33:22 +00:00
|
|
|
SyncWebhookEventType::PaymentGatewayInitializeSession => Json::from(
|
|
|
|
serde_json::to_value(PaymentGatewayInitializeSessionResponse::<u8> { data: None })?,
|
|
|
|
),
|
|
|
|
SyncWebhookEventType::TransactionProcessSession => {
|
|
|
|
let data = serde_json::from_str::<TransactionProcessSession2>(&body)?;
|
|
|
|
Json::from(serde_json::to_value(TransactionProcessSessionResponse::<
|
|
|
|
u8,
|
|
|
|
> {
|
|
|
|
data: None,
|
|
|
|
time: None,
|
|
|
|
psp_reference: None,
|
|
|
|
external_url: None,
|
|
|
|
message: None,
|
|
|
|
amount: Decimal::from_str(&data.action.amount.0)?,
|
|
|
|
result: match data.action.action_type {
|
|
|
|
TransactionFlowStrategyEnum::Charge => {
|
|
|
|
TransactionSessionResult::ChargeSuccess
|
|
|
|
}
|
|
|
|
TransactionFlowStrategyEnum::Authorization => {
|
|
|
|
TransactionSessionResult::AuthorizationSuccess
|
|
|
|
}
|
2024-03-12 19:48:20 +00:00
|
|
|
},
|
|
|
|
})?)
|
2024-03-11 13:11:47 +00:00
|
|
|
}
|
2024-03-14 13:33:22 +00:00
|
|
|
SyncWebhookEventType::TransactionChargeRequested => {
|
|
|
|
let data = serde_json::from_str::<TransactionChargeRequested2>(&body)?;
|
|
|
|
Json::from(serde_json::to_value(TransactionChargeRequestedResponse {
|
|
|
|
time: None,
|
|
|
|
psp_reference: "".to_owned(),
|
|
|
|
external_url: None,
|
|
|
|
message: None,
|
|
|
|
amount: data
|
|
|
|
.action
|
|
|
|
.amount
|
|
|
|
.and_then(|a| Decimal::from_str(&a.0).ok()),
|
|
|
|
result: Some(ChargeRequestedResult::ChargeSuccess),
|
|
|
|
})?)
|
|
|
|
}
|
|
|
|
SyncWebhookEventType::TransactionRefundRequested => {
|
|
|
|
let data = serde_json::from_str::<TransactionRefundRequested2>(&body)?;
|
|
|
|
Json::from(serde_json::to_value(TransactionRefundRequestedResponse {
|
|
|
|
time: None,
|
|
|
|
psp_reference: "".to_owned(),
|
|
|
|
external_url: None,
|
|
|
|
message: None,
|
|
|
|
amount: data
|
|
|
|
.action
|
|
|
|
.amount
|
|
|
|
.and_then(|a| Decimal::from_str(&a.0).ok()),
|
|
|
|
result: Some(RefundRequestedResult::RefundSuccess),
|
|
|
|
})?)
|
|
|
|
}
|
|
|
|
SyncWebhookEventType::TransactionInitializeSession => {
|
|
|
|
let data = serde_json::from_str::<TransactionInitializeSession2>(&body)?;
|
|
|
|
let app = state.saleor_app.lock().await;
|
|
|
|
let auth_data = app.apl.get(&saleor_api_url).await?;
|
|
|
|
let operation = TransactionUpdate::build(TransactionUpdateVariables {
|
|
|
|
id: &data.transaction.id,
|
|
|
|
transaction: Some(TransactionUpdateInput {
|
|
|
|
message: Some(""),
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
let mut res = surf::post(&saleor_api_url).run_graphql(operation).await;
|
|
|
|
|
|
|
|
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 let Some(tr) = &mut q_res.transaction {
|
|
|
|
tr.message = serde_json::to_string(&PaymentMethod {
|
|
|
|
payment_method: GatewayType::COD,
|
|
|
|
})?;
|
|
|
|
webhook_result = WebhookResult::Success;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::from(serde_json::to_value(
|
|
|
|
TransactionInitializeSessionResponse::<u8> {
|
|
|
|
data: None,
|
|
|
|
time: None,
|
|
|
|
psp_reference: None,
|
|
|
|
external_url: None,
|
|
|
|
message: None,
|
|
|
|
amount: Decimal::from_str(&data.action.amount.0)?,
|
|
|
|
result: match (data.action.action_type, webhook_result) {
|
|
|
|
(TransactionFlowStrategyEnum::Charge, WebhookResult::Success) => {
|
|
|
|
TransactionSessionResult::ChargeSuccess
|
|
|
|
}
|
|
|
|
(
|
|
|
|
TransactionFlowStrategyEnum::Authorization,
|
|
|
|
WebhookResult::Success,
|
|
|
|
) => TransactionSessionResult::AuthorizationSuccess,
|
|
|
|
(TransactionFlowStrategyEnum::Charge, WebhookResult::Failiure) => {
|
|
|
|
TransactionSessionResult::ChargeFailiure
|
|
|
|
}
|
|
|
|
(
|
|
|
|
TransactionFlowStrategyEnum::Authorization,
|
|
|
|
WebhookResult::Failiure,
|
|
|
|
) => TransactionSessionResult::AuthorizationFailure,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
)?)
|
2024-03-11 13:11:47 +00:00
|
|
|
}
|
2024-03-12 19:48:20 +00:00
|
|
|
_ => Json::from(Value::from_str("")?),
|
2024-03-11 13:11:47 +00:00
|
|
|
},
|
2024-03-12 19:48:20 +00:00
|
|
|
_ => Json::from(Value::from_str("")?),
|
|
|
|
};
|
2024-03-11 13:11:47 +00:00
|
|
|
|
|
|
|
info!("got webhooks!");
|
2024-03-12 19:48:20 +00:00
|
|
|
Ok(res)
|
2024-03-11 13:11:47 +00:00
|
|
|
}
|
|
|
|
|
2024-03-12 19:48:20 +00:00
|
|
|
#[derive(Serialize, Clone, Debug)]
|
|
|
|
pub struct JsonResponse {
|
|
|
|
data: JsonResponseData,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Clone, Debug)]
|
|
|
|
pub struct JsonResponseData {
|
|
|
|
current_gateway: ActiveGateway,
|
2024-03-11 13:11:47 +00:00
|
|
|
}
|
2024-03-14 13:33:22 +00:00
|
|
|
enum WebhookResult {
|
|
|
|
Success,
|
|
|
|
Failiure,
|
2024-03-11 13:11:47 +00:00
|
|
|
}
|
2024-03-12 19:48:20 +00:00
|
|
|
|
2024-03-14 13:33:22 +00:00
|
|
|
#[derive(Serialize, Clone, Debug)]
|
|
|
|
struct PaymentMethod {
|
|
|
|
payment_method: GatewayType,
|
2024-03-12 19:48:20 +00:00
|
|
|
}
|