From 88f82b366a1ab7c180cb3e8f46526553b561af8e Mon Sep 17 00:00:00 2001 From: Zeeshan Tariq Date: Thu, 6 Nov 2025 10:43:03 +0500 Subject: [PATCH 1/4] remove alex_backend from alex_frontend dependency so dfx deploy works --- dfx.json | 1 - 1 file changed, 1 deletion(-) diff --git a/dfx.json b/dfx.json index a67e46212..db11b4cd4 100644 --- a/dfx.json +++ b/dfx.json @@ -94,7 +94,6 @@ "id": "ysy5f-2qaaa-aaaap-qkmmq-cai" }, "alex_frontend": { - "dependencies": ["alex_backend"], "frontend": { "entrypoint": "src/alex_frontend/public/index.html" }, From 245f84277ca2d933de4237dcc7f5bf7d36b88d14 Mon Sep 17 00:00:00 2001 From: Zeeshan Tariq Date: Thu, 6 Nov 2025 10:44:11 +0500 Subject: [PATCH 2/4] use checkout session instead of payment intent webhook event --- src/stripe/src/webhook.rs | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/stripe/src/webhook.rs b/src/stripe/src/webhook.rs index 366c7d3d0..b57274d94 100644 --- a/src/stripe/src/webhook.rs +++ b/src/stripe/src/webhook.rs @@ -103,7 +103,7 @@ fn handle_stripe_webhook(request: &HttpRequest) -> HttpResponse { println!("Stripe event type: {}", event_type); match event_type { - "payment_intent.succeeded" => handle_payment_intent_succeeded(&webhook_data), + "checkout.session.completed" => handle_checkout_session_completed(&webhook_data), _ => { println!("Unhandled webhook event type: {}", event_type); create_success_response("Webhook received but not processed", None) @@ -117,28 +117,27 @@ fn handle_stripe_webhook(request: &HttpRequest) -> HttpResponse { } } -fn handle_payment_intent_succeeded(webhook_data: &Value) -> HttpResponse { - println!("Processing payment_intent.succeeded event"); +fn handle_checkout_session_completed(webhook_data: &Value) -> HttpResponse { + println!("Processing checkout.session.completed event"); let data = &webhook_data["data"]["object"]; - let amount = data["amount"].as_u64().unwrap_or(0); - let payment_intent_id = data["id"].as_str().unwrap_or(""); - let metadata = &data["metadata"]; - let user_id = metadata["user_id"].as_str().unwrap_or(""); + let amount_total = data["amount_total"].as_u64().unwrap_or(0); + let session_id = data["id"].as_str().unwrap_or(""); + let client_reference_id = data["client_reference_id"].as_str().unwrap_or(""); println!( - "Amount: {} cents, Payment Intent: {}, User: {}", - amount, payment_intent_id, user_id + "Amount: {} cents, Session ID: {}, Client Reference ID: {}", + amount_total, session_id, client_reference_id ); - if !user_id.is_empty() { - match Principal::from_text(user_id) { + if !client_reference_id.is_empty() { + match Principal::from_text(client_reference_id) { Ok(user_principal) => { println!("Updating balance for user: {}", user_principal.to_text()); let updated_balance = update_user_balance(&user_principal, |balance| { - balance.balance += amount; - balance.total_deposits += amount; + balance.balance += amount_total; + balance.total_deposits += amount_total; balance.deposit_count += 1; }); @@ -148,17 +147,17 @@ fn handle_payment_intent_succeeded(webhook_data: &Value) -> HttpResponse { ); create_success_response( - "Payment intent processed successfully", + "Checkout session processed successfully", Some(serde_json::json!({ "user": user_principal.to_text(), - "payment_intent_id": payment_intent_id, - "amount_deposited": amount, + "session_id": session_id, + "amount_deposited": amount_total, "new_balance": updated_balance.balance, })), ) } Err(e) => { - println!("Invalid Principal in user_id: {}", e); + println!("Invalid Principal in client_reference_id: {}", e); create_error_response( 400, "Invalid user reference", @@ -167,8 +166,8 @@ fn handle_payment_intent_succeeded(webhook_data: &Value) -> HttpResponse { } } } else { - println!("Payment intent succeeded but no user_id in metadata"); - create_success_response("Payment intent received but not processed", None) + println!("Checkout session completed but no client_reference_id"); + create_success_response("Checkout session received but not processed", None) } } From 76ef9f94de65a05f2aa30ab5f50486e96ddaf419 Mon Sep 17 00:00:00 2001 From: Zeeshan Tariq Date: Thu, 6 Nov 2025 10:53:06 +0500 Subject: [PATCH 3/4] add protected provider settings --- .../auth/components/ArweaveSettings.tsx | 502 +++++++++++++++++ .../components/AuthenticationSettings.tsx | 93 ++++ .../auth/components/EthereumSettings.tsx | 500 +++++++++++++++++ .../features/auth/components/OisySettings.tsx | 408 ++++++++++++++ .../auth/components/SolanaSettings.tsx | 505 ++++++++++++++++++ .../src/pages/librarian/index.tsx | 7 + 6 files changed, 2015 insertions(+) create mode 100644 src/alex_frontend/src/features/auth/components/ArweaveSettings.tsx create mode 100644 src/alex_frontend/src/features/auth/components/AuthenticationSettings.tsx create mode 100644 src/alex_frontend/src/features/auth/components/EthereumSettings.tsx create mode 100644 src/alex_frontend/src/features/auth/components/OisySettings.tsx create mode 100644 src/alex_frontend/src/features/auth/components/SolanaSettings.tsx diff --git a/src/alex_frontend/src/features/auth/components/ArweaveSettings.tsx b/src/alex_frontend/src/features/auth/components/ArweaveSettings.tsx new file mode 100644 index 000000000..58bce4f4e --- /dev/null +++ b/src/alex_frontend/src/features/auth/components/ArweaveSettings.tsx @@ -0,0 +1,502 @@ +import React, { useState } from "react"; +import { useFormik } from "formik"; +import * as Yup from "yup"; +import { Button } from "@/lib/components/button"; +import { Input } from "@/lib/components/input"; +import { + LoaderCircle, + Save, + CheckCircle, + XCircle, + RotateCcw, +} from "lucide-react"; +import useAuthentication from "@/hooks/actors/useAuthentication"; +import type { ArweaveSettings } from "../../../../../declarations/authentication/authentication.did"; + +const ArweaveSettingsSchema = Yup.object().shape({ + uri: Yup.string() + .matches( + /^https?:\/\/.+/, + "URI must be a valid URL (http:// or https://)" + ) + .required("URI is required"), + domain: Yup.string() + .min(2, "Domain is too short") + .required("Domain is required"), + statement: Yup.string() + .min(10, "Statement is too short") + .required("Statement is required"), + salt: Yup.string() + .min(8, "Salt must be at least 8 characters") + .required("Salt is required"), + network: Yup.string() + .oneOf(["mainnet", "testnet"], "Invalid network") + .required("Network is required"), + version: Yup.string().required("Version is required"), + session_ttl: Yup.number() + .min(60, "Session TTL must be at least 60 seconds") + .required("Session TTL is required"), + message_ttl: Yup.number() + .min(60, "Message TTL must be at least 60 seconds") + .required("Message TTL is required"), +}); + +const ArweaveSettingsComponent = () => { + const { actor } = useAuthentication(); + const [loading, setLoading] = useState(false); + const [success, setSuccess] = useState(false); + const [error, setError] = useState(null); + + // Default values matching backend defaults + const defaultValues = { + uri: "http://localhost:8080", + domain: "localhost:8080", + statement: "Sign in to authenticate with your Arweave wallet", + salt: "arweave-mulauth-salt", + version: "1", + network: "mainnet", + session_ttl: 28800, // 8 hours in seconds + message_ttl: 600, // 10 minutes in seconds + }; + + const formik = useFormik({ + initialValues: defaultValues, + validationSchema: ArweaveSettingsSchema, + validateOnBlur: true, + validateOnChange: true, + onSubmit: async (values) => { + if (!actor) { + setError("Authentication actor not available"); + return; + } + + setLoading(true); + setError(null); + setSuccess(false); + + try { + const settings: ArweaveSettings = { + uri: values.uri, + domain: values.domain, + statement: values.statement, + salt: values.salt, + network: values.network, + version: values.version, + session_ttl: BigInt(values.session_ttl * 1_000_000_000), // Convert seconds to nanoseconds + message_ttl: BigInt(values.message_ttl * 1_000_000_000), // Convert seconds to nanoseconds + }; + + const result = await actor.update_arweave_settings(settings); + + if ("Ok" in result) { + setSuccess(true); + setTimeout(() => setSuccess(false), 3000); + } else { + setError(result.Err); + } + } catch (err) { + setError( + err instanceof Error + ? err.message + : "Unknown error occurred" + ); + } finally { + setLoading(false); + } + }, + }); + + const resetMessages = () => { + setError(null); + setSuccess(false); + }; + + const resetToDefaults = () => { + formik.setValues(defaultValues); + resetMessages(); + }; + + return ( +
+ {success && ( +
+ + + Settings updated successfully + +
+ )} + + {error && ( +
+ + {error} +
+ )} + +
+
+

+ Arweave Authentication +

+

+ Configure SIWA settings for your application +

+
+ +
+
+
+
+ + + {formik.touched.uri && formik.errors.uri ? ( +

+ {formik.errors.uri} +

+ ) : ( +

+ Base URL where your application is + hosted +

+ )} +
+ +
+ + + {formik.touched.domain && + formik.errors.domain ? ( +

+ {formik.errors.domain} +

+ ) : ( +

+ Domain displayed in SIWA authentication + message +

+ )} +
+
+ +
+ +