lots of small things

This commit is contained in:
Djkáťo 2024-03-12 20:48:20 +01:00
parent 768a1cf7bd
commit 332084b105
17 changed files with 785 additions and 29 deletions

9
.env
View file

@ -1,8 +1,11 @@
## COMMON VARIABLES FOR ALL APPS
REQUIRED_SALEOR_VERSION="^3.13" REQUIRED_SALEOR_VERSION="^3.13"
APP_API_BASE_URL="http://10.100.110.234:3000" APP_API_BASE_URL="http://10.100.110.234:3000"
APL="Redis" APL="Redis"
APL_URL="redis://localhost:6380/2" APL_URL="redis://localhost:6380/2"
LOG_LEVEL="DEBUG" LOG_LEVEL="DEBUG"
## THESE VARIABLES ARE FOR SITEMAP-GENERATOR APP
SITEMAP_TARGET_FOLDER="./temp" SITEMAP_TARGET_FOLDER="./temp"
# Available fields can be found in ./sitemap-generator/src/queries/event_subjects_updated.rs: ProductUpdate # Available fields can be found in ./sitemap-generator/src/queries/event_subjects_updated.rs: ProductUpdate
SITEMAP_PRODUCT_TEMPLATE="https://example.com/{product.category.slug}/{product.slug}" SITEMAP_PRODUCT_TEMPLATE="https://example.com/{product.category.slug}/{product.slug}"
@ -14,3 +17,9 @@ SITEMAP_COLLECTION_TEMPLATE="https://example.com/collection/{collection.slug}"
SITEMAP_PAGES_TEMPLATE="https://example.com/{page.slug}" SITEMAP_PAGES_TEMPLATE="https://example.com/{page.slug}"
# Without trailing "/"! # Without trailing "/"!
SITEMAP_INDEX_HOSTNAME="https://example.com" SITEMAP_INDEX_HOSTNAME="https://example.com"
## THESE VARIABLES ARE FOR SIMPLE-PAYMENT-GATEWAY APP
#To see all possible options, check simple-payment-gateway/src/app:GatewayTypes
ACTIVE_GATEWAYS="cod,cash,transfer"
# only SK,EN available :). Determines what language the gateway names will be in storefront
LOCALE="SK"

27
Cargo.lock generated
View file

@ -841,6 +841,26 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "enum-iterator"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "600536cfe9e2da0820aa498e570f6b2b9223eec3ce2f835c8ae4861304fa4794"
dependencies = [
"enum-iterator-derive",
]
[[package]]
name = "enum-iterator-derive"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03cdc46ec28bd728e67540c528013c6a10eb69a02eb31078a1bda695438cbfb8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]] [[package]]
name = "envy" name = "envy"
version = "0.4.2" version = "0.4.2"
@ -2454,7 +2474,7 @@ checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]] [[package]]
name = "saleor-app-sdk" name = "saleor-app-sdk"
version = "0.1.0" version = "0.1.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -2701,6 +2721,7 @@ dependencies = [
"cynic", "cynic",
"cynic-codegen", "cynic-codegen",
"dotenvy", "dotenvy",
"enum-iterator",
"envy", "envy",
"redis", "redis",
"saleor-app-sdk", "saleor-app-sdk",
@ -2901,9 +2922,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.26.1" version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
[[package]] [[package]]
name = "strum_macros" name = "strum_macros"

View file

@ -1,3 +0,0 @@
[toolchain]
channel = "nightly"
targets = ["x86_64-unknown-linux-musl"]

4
rust-toolchain.tomln Normal file
View file

@ -0,0 +1,4 @@
[toolchain]
channel = "nightly"
#targets = ["x86_64-unknown-linux-musl"]
targets = ["x86_64-unknown-linux-gnu"]

View file

@ -1,6 +1,7 @@
pub mod apl; pub mod apl;
pub mod config; pub mod config;
pub mod headers; pub mod headers;
pub mod locales;
pub mod manifest; pub mod manifest;
pub mod middleware; pub mod middleware;
pub mod webhooks; pub mod webhooks;
@ -25,6 +26,7 @@ pub struct AuthData {
pub app_id: String, pub app_id: String,
pub jwks: Option<String>, pub jwks: Option<String>,
} }
impl std::fmt::Display for AuthData { impl std::fmt::Display for AuthData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!( write!(

47
sdk/src/locales.rs Normal file
View file

@ -0,0 +1,47 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LocaleCode {
Ar,
Az,
Bg,
Bn,
Ca,
Cs,
Da,
De,
El,
En,
Es,
EsCO,
Et,
Fa,
Fr,
Hi,
Hu,
Hy,
Id,
Is,
It,
Ja,
Ko,
Mn,
Nb,
Nl,
Pl,
Pt,
PtBR,
Ro,
Ru,
Sk,
Sl,
Sq,
Sr,
Sv,
Th,
Tr,
Uk,
Vi,
ZhHans,
ZhHant,
}

View file

@ -1,3 +1,4 @@
pub mod sync_response;
pub mod utils; pub mod utils;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View file

@ -0,0 +1,223 @@
use serde::Serialize;
//Why are these few in snake_case but rest is camelCase?
#[derive(Serialize, Debug, Clone)]
pub struct CheckoutCalculateTaxesResponse {
pub shipping_price_gross_amount: f32,
pub shipping_price_net_amount: f32,
pub shipping_tax_rate: f32,
pub lines: Vec<LinesResponse>,
}
#[derive(Serialize, Debug, Clone)]
pub struct LinesResponse {
pub total_gross_amount: f32,
pub total_net_amount: f32,
pub tax_rate: f32,
}
#[derive(Serialize, Debug, Clone)]
pub struct CheckoutFilterShippingMethodsResponse {
pub excluded_methods: Vec<ExcludedMethodsResponse>,
}
#[derive(Serialize, Debug, Clone)]
pub struct ExcludedMethodsResponse {
pub id: String,
pub reason: Option<String>,
}
#[derive(Serialize, Debug, Clone)]
pub struct OrderCalculateTaxes(CheckoutCalculateTaxesResponse);
#[derive(Serialize, Debug, Clone)]
pub struct OrderFilterShippingMethods(CheckoutFilterShippingMethodsResponse);
#[derive(Serialize, Debug, Clone)]
pub struct ShippingListMethodsForCheckout(Vec<ShippingListMethodsForCheckoutVec>);
#[derive(Serialize, Debug, Clone)]
struct ShippingListMethodsForCheckoutVec {
pub id: String,
pub name: Option<String>,
pub amount: f32,
pub currency: String,
pub maximum_delivery_days: Option<i32>,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ChargeRequestedResult {
ChargeSuccess,
ChargeFailiure,
}
#[derive(Serialize, Debug, Clone)]
pub struct TransactionChargeRequestedResponse {
pub psp_reference: String,
pub result: Option<ChargeRequestedResult>,
pub amount: Option<f32>,
pub time: Option<String>,
pub external_url: Option<String>,
pub message: Option<String>,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum RefundRequestedResult {
RefundSuccess,
RefundFailiure,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TransactionRefundRequestedResponse {
pub psp_reference: String,
pub result: Option<RefundRequestedResult>,
pub amount: Option<f32>,
pub time: Option<String>,
pub external_url: Option<String>,
pub message: Option<String>,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CancelationRequestedResult {
CancelSuccess,
CancelFailiure,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TransactionCancelationRequestedResponse {
pub psp_reference: String,
pub result: Option<CancelationRequestedResult>,
pub amount: Option<f32>,
pub time: Option<String>,
pub external_url: Option<String>,
pub message: Option<String>,
}
#[derive(Serialize, Debug, Clone)]
pub struct PaymentGatewayInitializeSessionResponse<T: Serialize> {
pub data: T,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TransactionSessionResult {
ChargeSuccess,
ChargeFailiure,
ChargeRequested,
ChargeActionRequired,
AuthorizationSuccess,
AuthorizationFailure,
AuthorizationRequested,
AuthorizationActionRequired,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TransactionInitializeSessionResponse<T: Serialize> {
pub psp_reference: Option<String>,
pub data: Option<T>,
pub result: TransactionSessionResult,
pub amount: f32,
pub time: Option<String>,
pub external_url: Option<String>,
pub message: Option<String>,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TransactionProcessSessionResponse<T: Serialize> {
pub psp_reference: Option<String>,
pub data: Option<T>,
pub result: TransactionSessionResult,
pub amount: f32,
pub time: Option<String>,
pub external_url: Option<String>,
pub message: Option<String>,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PaymentMethodTokenizationResult {
SucessfullyTokenized,
AdditionalActionRequired,
Pending,
FailedToTokenize,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PaymentMethodProcessTokenizationSession<T: Serialize> {
pub result: PaymentMethodTokenizationResult,
/**
Should be present when `PaymentMethodTokenizationResult::{SuccessfullyTokenized && AdditionalActionRequired}`
*/
pub id: Option<String>,
pub data: Option<T>,
pub error: Option<String>,
}
#[derive(Serialize, Debug, Clone)]
pub struct PaymentMethodInitializeTokenizationSession<T: Serialize>(
PaymentMethodProcessTokenizationSession<T>,
);
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PaymentGatewayTokenisationResult {
SuccessfullyInitialized,
FailedToInitialize,
}
#[derive(Serialize, Debug, Clone)]
pub struct PaymentGatewayInitializeTokenizationSession<T: Serialize> {
pub result: PaymentGatewayTokenisationResult,
pub data: Option<T>,
pub error: Option<String>,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum StoredPaymentMethodDeleteResult {
SucessfullyDeleted,
FailedToDelete,
}
#[derive(Serialize, Debug, Clone)]
pub struct StoredPaymentMethodDeleteRequested {
pub result: StoredPaymentMethodDeleteResult,
pub error: Option<String>,
}
//TODO: Dahek is Array<"INTERACTIVE"> from app-sdk/../sync-webhook-response-builder.ts:LIST_STORED_PAYMENT_METHODS?
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct PaymentMethod<T: Serialize> {
pub id: String,
pub supported_payment_flows: Vec<T>,
#[serde(rename = "type")]
pub typ: String,
pub credit_card_info: Option<CreditCardInfo>,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CreditCardInfo {
brand: String,
last_digits: String,
exp_month: String,
exp_year: String,
first_digits: Option<String>,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ListStoredPaymentMethodsResponse<T: Serialize, C: Serialize> {
payment_methods: Vec<PaymentMethod<C>>,
name: Option<String>,
data: Option<T>,
}

View file

@ -34,6 +34,7 @@ surf.workspace = true
cynic = { workspace = true, features = ["http-surf"] } cynic = { workspace = true, features = ["http-surf"] }
cynic-codegen.workspace = true cynic-codegen.workspace = true
const_format = "0.2.32" const_format = "0.2.32"
enum-iterator = "2.0.0"
[build-dependencies] [build-dependencies]
cynic-codegen.workspace = true cynic-codegen.workspace = true

View file

@ -1,11 +1,15 @@
use std::sync::Arc;
use axum::{ use axum::{
http::StatusCode, http::StatusCode,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
}; };
use enum_iterator::{all, Sequence};
use std::{fmt::Display, str::FromStr, sync::Arc};
use saleor_app_sdk::{config::Config, manifest::AppManifest, SaleorApp}; use saleor_app_sdk::{config::Config, locales::LocaleCode, manifest::AppManifest, SaleorApp};
use serde::{
de::{self, Visitor},
Deserialize, Deserializer, Serialize,
};
// Make our own error that wraps `anyhow::Error`. // Make our own error that wraps `anyhow::Error`.
pub struct AppError(anyhow::Error); pub struct AppError(anyhow::Error);
@ -38,9 +42,66 @@ pub fn trace_to_std(config: &Config) {
.init(); .init();
} }
#[derive(Debug, Clone, Sequence, Serialize)]
pub enum GatewayType {
Accreditation,
Cash,
/**
Acronym for Cash on Delivery
*/
COD,
Inkaso,
Other,
Transfer,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AppState { pub struct AppState {
pub saleor_app: Arc<tokio::sync::Mutex<SaleorApp>>, pub saleor_app: Arc<tokio::sync::Mutex<SaleorApp>>,
pub config: Config, pub config: Config,
pub manifest: AppManifest, pub manifest: AppManifest,
pub active_gateways: Vec<ActiveGateway>,
}
pub fn get_active_gateways_from_env() -> anyhow::Result<Vec<ActiveGateway>> {
dotenvy::dotenv()?;
//eg: "accreditation,cod,other,transfer"
let env_types = std::env::var("ACTIVE_GATEWAYS")?;
let locale = std::env::var("LOCALE")?;
let locale = match locale.as_str() {
"SK" => LocaleCode::Sk,
"EN" => LocaleCode::En,
l => unimplemented!("Locale {l} not implemented"),
};
let str_types: Vec<_> = env_types.split(",").collect();
let gateway_types = str_types
.iter()
.zip(all::<GatewayType>())
.filter_map(|(s, g)| match format!("{:?}", g).to_lowercase() == *s {
true => Some(g),
false => None,
})
.map(|g| )
.collect::<Vec<_>>();
todo!()
}
#[derive(Debug, Clone, Serialize)]
pub struct ActiveGateway {
pub gateway_type: GatewayType,
pub id: String,
pub name: String,
pub currencies: Vec<String>,
//don't need this one yet
pub config: [(); 0],
}
impl ActiveGateway{
fn from_gateway_type(ty: &GatewayType) -> Self {
all_currencies =
match type {
}
}
} }

View file

@ -16,11 +16,11 @@ use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::{ use crate::{
app::{trace_to_std, AppState}, app::{get_active_gateways_from_env, trace_to_std, AppState},
queries::event_transactions::{ queries::event_transactions::{
sub_payment_gateway_initialize_session, sub_transaction_charge_requested, sub_list_payment_gateways, sub_payment_gateway_initialize_session,
sub_transaction_initialize_session, sub_transaction_process_session, sub_transaction_charge_requested, sub_transaction_initialize_session,
sub_transaction_refund_requested, sub_transaction_process_session, sub_transaction_refund_requested,
}, },
routes::create_routes, routes::create_routes,
}; };
@ -63,6 +63,12 @@ async fn main() -> anyhow::Result<()> {
.add_sync_event(SyncWebhookEventType::PaymentGatewayInitializeSession) .add_sync_event(SyncWebhookEventType::PaymentGatewayInitializeSession)
.build(), .build(),
) )
.add_webhook(
WebhookManifest::new(&config)
.set_query(sub_list_payment_gateways)
.add_sync_event(SyncWebhookEventType::PaymentListGateways)
.build(),
)
.add_permissions(vec![ .add_permissions(vec![
AppPermission::HandlePayments, AppPermission::HandlePayments,
AppPermission::ManageOrders, AppPermission::ManageOrders,
@ -70,7 +76,9 @@ async fn main() -> anyhow::Result<()> {
AppPermission::HandleCheckouts, AppPermission::HandleCheckouts,
]) ])
.build(); .build();
let app_state = AppState { let app_state = AppState {
active_gateways: get_active_gateways_from_env()?,
manifest: app_manifest, manifest: app_manifest,
config: config.clone(), config: config.clone(),
saleor_app: Arc::new(Mutex::new(saleor_app)), saleor_app: Arc::new(Mutex::new(saleor_app)),

View file

@ -55,6 +55,18 @@ fragment OrderDetails on Order {
} }
"#; "#;
pub const sub_list_payment_gateways: &str = r#"
subscription ListPaymentGateways {
event {
... on PaymentListGateways {
checkout {
id
}
}
}
}
"#;
pub const sub_payment_gateway_initialize_session: &str = concatcp!( pub const sub_payment_gateway_initialize_session: &str = concatcp!(
r#" r#"
subscription PaymentGatewayInitializeSession { subscription PaymentGatewayInitializeSession {
@ -178,3 +190,246 @@ subscription transactionCancelationRequested {
"#, "#,
fragment_transaction_details fragment_transaction_details
); );
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "TransactionRefundRequested")]
pub struct TransactionRefundRequested2 {
pub action: TransactionAction,
pub transaction: Option<TransactionItem>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "TransactionProcessSession")]
pub struct TransactionProcessSession2 {
pub action: TransactionProcessAction,
pub source_object: OrderOrCheckout,
pub transaction: TransactionItem,
pub data: Option<Json>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct TransactionProcessAction {
pub amount: PositiveDecimal,
pub action_type: TransactionFlowStrategyEnum,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "TransactionInitializeSession")]
pub struct TransactionInitializeSession2 {
pub data: Option<Json>,
pub source_object: OrderOrCheckout,
pub transaction: TransactionItem,
pub action: TransactionProcessAction2,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "TransactionProcessAction")]
pub struct TransactionProcessAction2 {
pub amount: PositiveDecimal,
pub currency: String,
pub action_type: TransactionFlowStrategyEnum,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "TransactionChargeRequested")]
pub struct TransactionChargeRequested2 {
pub action: TransactionAction,
pub transaction: Option<TransactionItem>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "TransactionCancelationRequested")]
pub struct TransactionCancelationRequested2 {
pub action: TransactionAction,
pub transaction: Option<TransactionItem>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct TransactionItem {
pub id: cynic::Id,
pub actions: Vec<TransactionActionEnum>,
pub external_url: String,
pub message: String,
pub authorized_amount: Money,
pub authorize_pending_amount: Money,
pub canceled_amount: Money,
pub cancel_pending_amount: Money,
pub charged_amount: Money,
pub charge_pending_amount: Money,
pub refunded_amount: Money,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct TransactionAction {
pub amount: Option<PositiveDecimal>,
pub action_type: TransactionActionEnum,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Subscription")]
pub struct TransactionRefundRequested {
pub event: Option<Event>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Subscription")]
pub struct TransactionProcessSession {
pub event: Option<Event2>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Subscription")]
pub struct TransactionInitializeSession {
pub event: Option<Event3>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Subscription")]
pub struct TransactionChargeRequested {
pub event: Option<Event4>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Subscription")]
pub struct TransactionCancelationRequested {
pub event: Option<Event5>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Subscription")]
pub struct PaymentGatewayInitializeSession {
pub event: Option<Event6>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "PaymentGatewayInitializeSession")]
pub struct PaymentGatewayInitializeSession2 {
pub data: Option<Json>,
pub amount: Option<PositiveDecimal>,
pub source_object: OrderOrCheckout,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct Order {
pub checkout_id: Option<cynic::Id>,
pub id: cynic::Id,
pub status: OrderStatus,
pub is_paid: bool,
pub payment_status: PaymentChargeStatusEnum,
pub charge_status: OrderChargeStatusEnum,
pub can_finalize: bool,
pub total_balance: Money,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct Money {
pub currency: String,
pub amount: f64,
}
#[derive(cynic::InlineFragments, Debug)]
#[cynic(graphql_type = "Event")]
pub enum Event6 {
PaymentGatewayInitializeSession2(PaymentGatewayInitializeSession2),
#[cynic(fallback)]
Unknown,
}
#[derive(cynic::InlineFragments, Debug)]
#[cynic(graphql_type = "Event")]
pub enum Event5 {
TransactionCancelationRequested2(TransactionCancelationRequested2),
#[cynic(fallback)]
Unknown,
}
#[derive(cynic::InlineFragments, Debug)]
#[cynic(graphql_type = "Event")]
pub enum Event4 {
TransactionChargeRequested2(TransactionChargeRequested2),
#[cynic(fallback)]
Unknown,
}
#[derive(cynic::InlineFragments, Debug)]
#[cynic(graphql_type = "Event")]
pub enum Event3 {
TransactionInitializeSession2(TransactionInitializeSession2),
#[cynic(fallback)]
Unknown,
}
#[derive(cynic::InlineFragments, Debug)]
#[cynic(graphql_type = "Event")]
pub enum Event2 {
TransactionProcessSession2(TransactionProcessSession2),
#[cynic(fallback)]
Unknown,
}
#[derive(cynic::InlineFragments, Debug)]
pub enum Event {
TransactionRefundRequested2(TransactionRefundRequested2),
#[cynic(fallback)]
Unknown,
}
#[derive(cynic::InlineFragments, Debug)]
pub enum OrderOrCheckout {
Order(Order),
#[cynic(fallback)]
Unknown,
}
#[derive(cynic::Enum, Clone, Copy, Debug)]
pub enum OrderChargeStatusEnum {
None,
Partial,
Full,
Overcharged,
}
#[derive(cynic::Enum, Clone, Copy, Debug)]
pub enum OrderStatus {
Draft,
Unconfirmed,
Unfulfilled,
PartiallyFulfilled,
PartiallyReturned,
Returned,
Fulfilled,
Canceled,
Expired,
}
#[derive(cynic::Enum, Clone, Copy, Debug)]
pub enum PaymentChargeStatusEnum {
NotCharged,
Pending,
PartiallyCharged,
FullyCharged,
PartiallyRefunded,
FullyRefunded,
Refused,
Cancelled,
}
#[derive(cynic::Enum, Clone, Copy, Debug)]
pub enum TransactionActionEnum {
Charge,
Refund,
Cancel,
}
#[derive(cynic::Enum, Clone, Copy, Debug)]
pub enum TransactionFlowStrategyEnum {
Authorization,
Charge,
}
#[derive(cynic::Scalar, Debug, Clone)]
#[cynic(graphql_type = "JSON")]
pub struct Json(pub String);
#[derive(cynic::Scalar, Debug, Clone)]
pub struct PositiveDecimal(pub String);

View file

@ -1,3 +1,5 @@
#[cynic::schema("saleor")]
mod schema {}
/* /*
mutation transactionUpdate($id: ID!, $transaction: TransactionUpdateInput) { mutation transactionUpdate($id: ID!, $transaction: TransactionUpdateInput) {
transactionUpdate(id: $id, transaction: $transaction) { transactionUpdate(id: $id, transaction: $transaction) {
@ -15,3 +17,85 @@ mutation transactionUpdate($id: ID!, $transaction: TransactionUpdateInput) {
} }
} }
*/ */
#[derive(cynic::QueryVariables, Debug)]
pub struct TransactionUpdateVariables<'a> {
pub id: &'a cynic::Id,
pub transaction: Option<TransactionUpdateInput<'a>>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Mutation", variables = "TransactionUpdateVariables")]
pub struct TransactionUpdate {
#[arguments(id: $id, transaction: $transaction)]
pub transaction_update: Option<TransactionUpdate2>,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "TransactionUpdate")]
pub struct TransactionUpdate2 {
pub transaction: Option<TransactionItem>,
pub errors: Vec<TransactionUpdateError>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct TransactionUpdateError {
pub field: Option<String>,
pub message: Option<String>,
pub code: TransactionUpdateErrorCode,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct TransactionItem {
pub id: cynic::Id,
pub actions: Vec<TransactionActionEnum>,
pub external_url: String,
pub message: String,
}
#[derive(cynic::Enum, Clone, Copy, Debug)]
pub enum TransactionActionEnum {
Charge,
Refund,
Cancel,
}
#[derive(cynic::Enum, Clone, Copy, Debug)]
pub enum TransactionUpdateErrorCode {
Invalid,
GraphqlError,
NotFound,
IncorrectCurrency,
MetadataKeyRequired,
Unique,
}
#[derive(cynic::InputObject, Debug)]
pub struct TransactionUpdateInput<'a> {
pub name: Option<&'a str>,
pub message: Option<&'a str>,
pub psp_reference: Option<&'a str>,
pub available_actions: Option<Vec<TransactionActionEnum>>,
pub amount_authorized: Option<MoneyInput<'a>>,
pub amount_charged: Option<MoneyInput<'a>>,
pub amount_refunded: Option<MoneyInput<'a>>,
pub amount_canceled: Option<MoneyInput<'a>>,
pub metadata: Option<Vec<MetadataInput<'a>>>,
pub private_metadata: Option<Vec<MetadataInput<'a>>>,
pub external_url: Option<&'a str>,
}
#[derive(cynic::InputObject, Debug)]
pub struct MoneyInput<'a> {
pub currency: &'a str,
pub amount: PositiveDecimal,
}
#[derive(cynic::InputObject, Debug)]
pub struct MetadataInput<'a> {
pub key: &'a str,
pub value: &'a str,
}
#[derive(cynic::Scalar, Debug, Clone)]
pub struct PositiveDecimal(pub String);

View file

@ -22,7 +22,7 @@ pub fn create_routes(state: AppState) -> Router {
(StatusCode::NOT_FOUND, "Not found") (StatusCode::NOT_FOUND, "Not found")
} }
let service = handle_404.into_service(); let service = handle_404.into_service();
let serve_dir = ServeDir::new("saleor-app-template/public").not_found_service(service); let serve_dir = ServeDir::new("simple-payment-gateway/public").not_found_service(service);
Router::new() Router::new()
.layer(middleware::from_fn(webhook_signature_verifier)) .layer(middleware::from_fn(webhook_signature_verifier))

View file

@ -5,7 +5,7 @@ use axum::{
http::{HeaderMap, StatusCode}, http::{HeaderMap, StatusCode},
}; };
use saleor_app_sdk::{AuthData, AuthToken}; use saleor_app_sdk::{AuthData, AuthToken};
use tracing::{debug, info}; use tracing::{debug, error, info};
use crate::app::{AppError, AppState}; use crate::app::{AppError, AppState};
@ -29,12 +29,25 @@ pub async fn register(
let auth_data = AuthData { let auth_data = AuthData {
jwks: None, jwks: None,
token: auth_token.auth_token, token: auth_token.auth_token,
domain: Some(state.config.app_api_base_url), domain: Some(state.config.app_api_base_url.clone()),
app_id: state.manifest.id, app_id: state.manifest.id.clone(),
saleor_api_url: saleor_api_url.clone(), saleor_api_url: saleor_api_url.clone(),
}; };
app.apl.set(auth_data).await?; app.apl.set(auth_data.clone()).await?;
//unlock the mutex guard so state isn't borrowed anymore and it can move
std::mem::drop(app);
info!("registered app for{:?}", &saleor_api_url); info!("registered app for{:?}", &saleor_api_url);
tokio::spawn(async move {
match register_active_gateways(&state, auth_data).await {
Ok(_) => info!("Payment gateways registered"),
Err(e) => error!("Failed registering gateways, {e}"),
};
});
Ok(StatusCode::OK) Ok(StatusCode::OK)
} }
pub async fn register_active_gateways(state: &AppState, auth_data: AuthData) -> anyhow::Result<()> {
// Maybe AppFetch manifest? Tho I might not need to since I already have it
// AppsInstallations to see if it's still installing
todo!()
}

View file

@ -1,7 +1,12 @@
use std::str::FromStr;
use anyhow::Context; use anyhow::Context;
use axum::{ use axum::{
body::Body,
extract::State, extract::State,
http::{HeaderMap, StatusCode}, http::{HeaderMap, StatusCode},
response::Response,
Json,
}; };
use saleor_app_sdk::{ use saleor_app_sdk::{
headers::SALEOR_API_URL_HEADER, headers::SALEOR_API_URL_HEADER,
@ -10,15 +15,20 @@ use saleor_app_sdk::{
SyncWebhookEventType, SyncWebhookEventType,
}, },
}; };
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tracing::{debug, error, info}; use tracing::{debug, error, info};
use crate::app::{AppError, AppState}; use crate::{
app::{ActiveGateway, AppError, AppState},
queries::event_transactions::PaymentGatewayInitializeSession,
};
pub async fn webhooks( pub async fn webhooks(
headers: HeaderMap, headers: HeaderMap,
State(state): State<AppState>, State(state): State<AppState>,
body: String, body: String,
) -> Result<StatusCode, AppError> { ) -> Result<Json<Value>, AppError> {
debug!("/api/webhooks"); debug!("/api/webhooks");
debug!("req: {:?}", body); debug!("req: {:?}", body);
debug!("headers: {:?}", headers); debug!("headers: {:?}", headers);
@ -29,30 +39,47 @@ pub async fn webhooks(
.to_str()? .to_str()?
.to_owned(); .to_owned();
let event_type = get_webhook_event_type(&headers)?; let event_type = get_webhook_event_type(&headers)?;
match event_type { let res: Json<Value> = match event_type {
EitherWebhookType::Sync(a) => match a { EitherWebhookType::Sync(a) => match a {
SyncWebhookEventType::PaymentGatewayInitializeSession => { SyncWebhookEventType::PaymentGatewayInitializeSession => {
initialize_gateway(&state, &url).await?; Json::from(serde_json::to_value(JsonResponse {
data: JsonResponseData {
current_gateway: ActiveGateway::COD,
},
})?)
} }
SyncWebhookEventType::TransactionProcessSession SyncWebhookEventType::TransactionProcessSession
| SyncWebhookEventType::TransactionChargeRequested | SyncWebhookEventType::TransactionChargeRequested
| SyncWebhookEventType::TransactionRefundRequested | SyncWebhookEventType::TransactionRefundRequested
| SyncWebhookEventType::TransactionInitializeSession => { | SyncWebhookEventType::TransactionInitializeSession => {
update_transaction_response(&state, &url).await?; update_transaction_response(&state, &url).await?;
todo!()
} }
_ => (), _ => Json::from(Value::from_str("")?),
}, },
_ => (), _ => Json::from(Value::from_str("")?),
} };
info!("got webhooks!"); info!("got webhooks!");
Ok(StatusCode::OK) Ok(res)
} }
async fn initialize_gateway(state: &AppState, saleor_api_url: &str) -> anyhow::Result<()> { #[derive(Serialize, Clone, Debug)]
todo!() pub struct JsonResponse {
data: JsonResponseData,
}
#[derive(Serialize, Clone, Debug)]
pub struct JsonResponseData {
current_gateway: ActiveGateway,
} }
async fn update_transaction_response(state: &AppState, saleor_api_url: &str) -> anyhow::Result<()> { async fn update_transaction_response(state: &AppState, saleor_api_url: &str) -> anyhow::Result<()> {
todo!() todo!()
} }
async fn new_transaction_response(state: &AppState, saleor_api_url: &str) -> anyhow::Result<()> {
debug!("Creating new transaction");
todo!()
}

View file

@ -25,6 +25,9 @@ pub fn create_routes(state: AppState) -> Router {
//TODO : Fix this relative path issue in workspaces //TODO : Fix this relative path issue in workspaces
let serve_dir = ServeDir::new("./sitemap-generator/public").not_found_service(service); let serve_dir = ServeDir::new("./sitemap-generator/public").not_found_service(service);
//TODO Query for everything using the app auth token
//TODO "Failed fetching initial products: More than one channel exists, please spocify which
//one"
Router::new() Router::new()
.route("/api/webhooks", any(webhooks)) .route("/api/webhooks", any(webhooks))
.layer(middleware::from_fn(webhook_signature_verifier)) .layer(middleware::from_fn(webhook_signature_verifier))