diff --git a/backend/src/lib/idempotency.js b/backend/src/lib/idempotency.js
index aedd2a46..47ac7d06 100644
--- a/backend/src/lib/idempotency.js
+++ b/backend/src/lib/idempotency.js
@@ -2,96 +2,72 @@ import crypto from "node:crypto";
import { getRedisClient } from "./redis.js";
/**
- * TTL for idempotency cache entries in seconds.
+ * TTL for idempotency keys (10 minutes)
*/
-const IDEMPOTENCY_TTL_SECONDS = 24 * 60 * 60; // 24 hours
+const IDEMPOTENCY_TTL_SECONDS = 10 * 60; // 600 seconds
-/**
- * Idempotency middleware that checks and enforces idempotent requests.
- * Tracks the key tied to the payload hash and response.
- * Returns a cached 201 response if the key matches a previous request.
- * Stores cached state in Redis for 24h.
- */
export async function idempotencyMiddleware(req, res, next) {
- // Only process POST requests as per requirements
+ // Only apply to POST requests
if (req.method !== "POST") {
return next();
}
const idempotencyKey = req.get("Idempotency-Key");
- if (idempotencyKey === undefined) {
- // Idempotency-Key is optional but allows safe retries when present
- return next();
- }
-
- if (typeof idempotencyKey !== "string" || idempotencyKey.trim().length === 0) {
+ // 🔴 NOW REQUIRED
+ if (!idempotencyKey || typeof idempotencyKey !== "string" || idempotencyKey.trim().length === 0) {
return res.status(400).json({
- error: "Idempotency-Key header must be a non-empty string",
+ error: "Idempotency-Key header is required and must be a non-empty string",
});
}
-
- // Ensure merchant context is available (assumes requireApiKeyAuth was run)
+ // Ensure merchant context exists
const merchantId = req.merchant?.id;
if (!merchantId) {
- // If authentication hasn't run or failed to set merchant, we can't safely track idempotency per merchant
- return res.status(401).json({ error: "Merchant authentication required" });
+ return res.status(401).json({
+ error: "Merchant authentication required",
+ });
}
const redisClient = getRedisClient();
const redisKey = `idempotency:${merchantId}:${idempotencyKey}`;
-
- // Calculate hash of payload to ensure consistency
+
+ // Optional: still keep hash for safety (prevents misuse)
const payloadHash = crypto
.createHash("sha256")
.update(JSON.stringify(req.body || {}))
.digest("hex");
try {
- const cachedValue = await redisClient.get(redisKey);
+ const existing = await redisClient.get(redisKey);
- if (cachedValue) {
- const { hash, response } = JSON.parse(cachedValue);
+ if (existing) {
+ const { hash } = JSON.parse(existing);
- // Verify if the payload matches the original request
+ // Same key but different payload → reject
if (hash !== payloadHash) {
return res.status(400).json({
- error: "Idempotency-Key already used with a different request payload"
+ error: "Idempotency-Key already used with a different request payload",
});
}
- // Return cached response with 201 status code as requested
- return res.status(201).json(response);
+ // 🔴 MAIN CHANGE: reject duplicates
+ return res.status(409).json({
+ error: "Duplicate request: Idempotency-Key already used",
+ });
}
- // Capture the original json method to intercept the response
- const originalJson = res.json.bind(res);
-
- res.json = function (data) {
- // Only cache successful creation-like responses (2xx)
- if (res.statusCode >= 200 && res.statusCode < 300) {
- redisClient
- .set(
- redisKey,
- JSON.stringify({
- hash: payloadHash,
- response: data,
- }),
- { EX: IDEMPOTENCY_TTL_SECONDS }
- )
- .catch((err) => {
- console.error("Failed to cache idempotency response:", err.message);
- });
- }
- return originalJson(data);
- };
+ // Store key immediately (no response caching)
+ await redisClient.set(
+ redisKey,
+ JSON.stringify({ hash: payloadHash }),
+ { EX: IDEMPOTENCY_TTL_SECONDS }
+ );
- next();
+ return next();
} catch (err) {
- // If Redis is unavailable, log error and proceed without idempotency (fail-safe)
+ // Fail-safe: allow request if Redis fails
console.error("Idempotency check failed (Redis error):", err.message);
- next();
+ return next();
}
-}
-
+}
\ No newline at end of file
diff --git a/backend/src/routes/payments.js b/backend/src/routes/payments.js
index b21c03c7..b389f44c 100644
--- a/backend/src/routes/payments.js
+++ b/backend/src/routes/payments.js
@@ -861,7 +861,7 @@ function createPaymentsRouter({
countQuery = applyPaymentFilters(countQuery, req);
countQuery = applyMetadataFilters(countQuery, req.query);
- const { count: totalCount, error: countError } = await countQuery;
+ const { count: filteredCount, error: filteredCountError } = await countQuery;
if (countError) {
countError.status = 500;
diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index 3e2940aa..228a207e 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -1,305 +1,44 @@
{
+ "Dashboard": {
+ "title": "Dashboard",
+ "welcome": "Welcome back",
+ "overview": "Payment overview",
+ "description": "Monitor your payments and activity in real time",
+ "businessOverview": "Business Overview",
+ "liveConfirmations": "Live Confirmations",
+ "viewAllPayments": "View all payments",
+ "quickActions": "Quick Actions",
+ "createPaymentLink": "Create Payment Link",
+ "withdrawFunds": "Withdraw Funds",
+ "settings": "Settings",
+ "viewDocs": "View Documentation",
+ "development": "Development",
+ "apiKeysTip": "Manage your API keys securely",
+ "webhookLogsTip": "Monitor webhook activity logs"
+ },
"localeSwitcher": {
- "label": "Lang",
+ "label": "Language",
"ariaLabel": "Select language",
"options": {
"en": "English",
- "es": "Espanol",
- "pt": "Portugues"
+ "es": "Spanish",
+ "pt": "Portuguese",
+ "fr": "French"
}
},
"nav": {
"home": "Home",
- "docs": "Docs",
+ "dashboard": "Dashboard",
+ "payments": "Payments",
+ "paymentHistory": "Payment History",
+ "Settings": {
+ "testWebhookSent": "Test webhook sent!"
+ },
+ "apiKeys": "API Keys",
+ "webhookLogs": "Webhook Logs",
"login": "Login",
"register": "Register",
+ "docs": "Docs",
"toggleMenu": "Toggle menu"
- },
- "sidebar": {
- "overview": "Overview",
- "payments": "Payments",
- "webhookLogs": "Webhook Logs",
- "apiKeys": "API Keys",
- "settings": "Settings",
- "workspace": "Authenticated workspace",
- "shortcuts": "Shortcuts",
- "createPayment": "Create Payment",
- "network": "Mainnet-testnet",
- "collapse": "Collapse sidebar",
- "expand": "Expand sidebar",
- "close": "Close navigation"
- },
- "breadcrumbs": {
- "home": "Home",
- "segments": {
- "dashboard": "Dashboard",
- "create": "Create",
- "payments": "Payments",
- "settings": "Settings",
- "webhook-logs": "Webhook Logs",
- "api-keys": "API Keys"
- }
- },
- "copyButton": {
- "ariaLabel": "Copy to clipboard",
- "copied": "Copied!",
- "copiedState": "Copied"
- },
- "dashboardPage": {
- "title": "Merchant Overview",
- "description": "Monitor your transaction volume, track payments in real-time, and manage your PLUTO integration.",
- "paymentMetrics": "Payment Metrics",
- "recentActivity": "Recent Activity",
- "viewAllPayments": "View all payments",
- "quickActions": "Quick Actions",
- "createPaymentLink": "Create Payment Link",
- "withdrawFunds": "Withdraw Funds (SEP-24)",
- "development": "Development",
- "apiKeysTip": "Use the API Keys page to rotate your secret token periodically.",
- "webhookLogsTip": "Check Webhook Logs status if payments are not triggering your backend."
- },
- "createPaymentPage": {
- "eyebrow": "Dashboard",
- "title": "Create Payment Link",
- "description": "Set an amount, choose an asset, and enter a recipient address to generate a shareable PLUTO payment link.",
- "newHere": "New here?",
- "registerMerchant": "Register a merchant account"
- },
- "createPaymentForm": {
- "invalidAmount": "Amount must be a positive number.",
- "invalidRecipient": "Recipient must be a valid Stellar public key (56 characters, starts with G).",
- "invalidHexColor": "{field} must be a valid hex color.",
- "failedCreate": "Failed to create payment link",
- "createdToast": "Payment link created!",
- "noApiKeyTitle": "No API key found",
- "noApiKeyDescription": "Register a merchant account to get your API key and start creating payment links.",
- "registerAsMerchant": "Register as Merchant",
- "readyEyebrow": "Payment Link Ready",
- "readyTitle": "Link Created Successfully",
- "readyDescription": "Share this link with your customer to collect payment.",
- "shareLink": "Share Link",
- "shareTitle": "Payment link ready",
- "shareText": "Open this PLUTO payment link.",
- "shareFailed": "Native sharing is unavailable right now.",
- "paymentLink": "Payment Link",
- "paymentId": "Payment ID",
- "status": "Status",
- "createAnother": "Create another payment link",
- "amount": "Amount",
- "amountPlaceholder": "Enter {exampleAmount} {asset}",
- "asset": "Asset",
- "selectAsset": "Select asset",
- "issuer": "Issuer",
- "trustedAddresses": "Select from Trusted Addresses",
- "selectSavedAddress": "-- Select a saved address --",
- "recipientAddress": "Recipient Address",
- "recipientPlaceholder": "Paste the {asset} destination wallet",
- "recipientPlaceholderSelected": "{label} selected for {asset}",
- "descriptionLabel": "Description",
- "optional": "optional",
- "descriptionPlaceholder": "What is this {asset} request for?",
- "brandingTitle": "Session Branding Overrides",
- "brandingDescription": "Apply custom colors to this payment link only.",
- "enabled": "Enabled",
- "disabled": "Disabled",
- "primary": "Primary",
- "secondary": "Secondary",
- "background": "Background",
- "checkoutPreview": "Checkout preview",
- "payNow": "Pay now",
- "generating": "Generating...",
- "generate": "Generate Payment Link",
- "rateLimitError": "You're creating links too quickly. Try again in {seconds} seconds.",
- "retryWait": "Wait {seconds}s…",
- "integrationCode": "Integration Code",
- "snippetsHelper": "Code snippets update automatically with your form values."
- },
- "paymentMetrics": {
- "downloadImage": "Download Image",
- "downloadPng": "Download PNG",
- "downloadSvg": "Download SVG",
- "exporting": "Exporting...",
- "exportSuccess": "Chart downloaded as {format}",
- "exportFailed": "Failed to export chart.",
- "fetchMetricsFailed": "Failed to fetch metrics",
- "fetchVolumeFailed": "Failed to fetch volume data",
- "retry": "Retry",
- "sevenDayVolume": "7-Day Volume",
- "totalPayments": "Total Payments",
- "paymentsCount": "{count, plural, one {payment} other {payments}}",
- "chartTitle": "Multi-Asset Volume Comparison",
- "chartSubtitle": "Daily transaction volume broken down by asset",
- "heatmapTitle": "Payment Density Heatmap",
- "heatmapSubtitle": "GitHub-style activity intensity across the last 365 days",
- "heatmapTotal": "{count, plural, one {# payment in the last year} other {# payments in the last year}}",
- "heatmapTooltip": "{count, plural, one {# payment} other {# payments}} on {date}",
- "heatmapAriaLabel": "365 day payment density heatmap",
- "heatmapEmptyDay": "No payments recorded",
- "heatmapLegendLess": "Less",
- "heatmapLegendMore": "More",
- "heatmapWeekdays": {
- "sun": "S",
- "mon": "M",
- "tue": "T",
- "wed": "W",
- "thu": "T",
- "fri": "F",
- "sat": "S"
- },
- "showRange": "Show {range}",
- "toggleAssetVisibility": "Toggle asset visibility",
- "showAsset": "Show {asset}",
- "hideAsset": "Hide {asset}",
- "noPayments": "No completed payments in this period.",
- "weeklyAvgLabel": "7D Avg",
- "ranges": {
- "7D": "7 Days",
- "30D": "30 Days",
- "1Y": "1 Year"
- }
- },
- "paymentsPage": {
- "title": "Payment History",
- "description": "Monitor all incoming transactions and their verification status."
- },
- "recentPayments": {
- "missingApiKey": "API key not found. Please register or log in.",
- "fetchFailed": "Failed to fetch payments",
- "loadFailed": "Failed to load payments",
- "connectionError": "Connection Error",
- "backendHint": "Make sure the backend is running and the payments endpoint is available.",
- "retryConnection": "Retry Connection",
- "testWebhook": "Test Webhook Anyway",
- "troubleshootingTip": "Troubleshooting Tip",
- "troubleshootingDescription": "You can still test webhook functionality while backend services are being restored.",
- "emptyTitle": "No payments yet",
- "emptyDescription": "Start accepting payments by creating your first payment link or testing webhooks to see transaction data flow.",
- "createPaymentLink": "Create Payment Link",
- "sendTestWebhook": "Send Test Webhook",
- "gettingStartedTitle": "Getting Started Guide",
- "gettingStartedDescription": "Use webhook tools to test payment notifications and see real-time data appear in this dashboard.",
- "search": "Search",
- "searchPlaceholder": "Search by payment ID or description...",
- "status": "Status",
- "allStatuses": "All Statuses",
- "asset": "Asset",
- "allAssets": "All Assets",
- "fromDate": "From Date",
- "toDate": "To Date",
- "activeFilters": "Active filters:",
- "searchChip": "Search: \"{value}\"",
- "statusChip": "Status: {value}",
- "assetChip": "Asset: {value}",
- "fromChip": "From: {value}",
- "toChip": "To: {value}",
- "clearSearchFilter": "Clear search filter",
- "clearStatusFilter": "Clear status filter",
- "clearAssetFilter": "Clear asset filter",
- "clearFromDateFilter": "Clear from date filter",
- "clearToDateFilter": "Clear to date filter",
- "clearAll": "Clear all",
- "linkCopied": "Payment link copied to clipboard!",
- "showingResults": "Showing {shown} of {total} payments",
- "filteredSuffix": "(filters applied)",
- "tableStatus": "Status",
- "tableAmount": "Amount",
- "tableRecipient": "Recipient",
- "tableDate": "Date",
- "tableLink": "Link",
- "view": "View",
- "emptyDescriptionValue": "-",
- "statuses": {
- "all": "All",
- "pending": "Pending",
- "confirmed": "Confirmed",
- "failed": "Failed",
- "refunded": "Refunded"
- }
- },
- "walletSelector": {
- "chooseWallet": "Choose a wallet",
- "description": "Connect your Stellar wallet to complete this payment.",
- "connecting": "Connecting...",
- "walletConnectWaiting": "Waiting for wallet...",
- "noProjectId": "(no project ID)",
- "notInstalled": "(not installed)",
- "tapToConnect": "Click to connect",
- "scanTitle": "Scan with your compatible wallet app",
- "scanDescription": "Scan with Freighter mobile or any WalletConnect-compatible wallet",
- "pairingFailed": "WalletConnect pairing failed",
- "userRejected": "Connection request was canceled. Please approve it in your wallet to continue.",
- "walletConnectUnavailable": "WalletConnect is unavailable right now. Try Freighter or enable a valid WalletConnect project ID.",
- "extensionNotFound": "We couldn't find that wallet on this device. Install the extension and try again.",
- "noAccountFound": "Your wallet connected, but no Stellar account was returned. Open a Stellar account in the wallet and retry.",
- "walletConnectFailed": "We couldn't finish the WalletConnect handshake. Please try again.",
- "connectionFailed": "We couldn't connect to that wallet right now. Please try again."
- },
- "checkout": {
- "paymentRequest": "Payment Request",
- "completePayment": "Complete Payment",
- "paymentNotFound": "Payment not found",
- "loadFailed": "Could not load payment details.",
- "paymentMissing": "Payment not found.",
- "loadPaymentFailed": "Failed to load payment.",
- "errorTitle": "Error",
- "errorDescription": "Check the payment link and try again, or contact the sender.",
- "processingFallback": "Processing transaction...",
- "doNotClose": "Do not close this tab",
- "recipient": "Recipient",
- "scanToPay": "Scan to Pay",
- "scanDescription": "Scan with Freighter or any compatible wallet",
- "openQrModal": "Pay with QR code",
- "qrModalTitle": "Pay with QR code",
- "qrModalDescription": "Scan this code on another device or share it with your wallet app.",
- "downloadQr": "Download PNG",
- "downloadQrSuccess": "QR code downloaded.",
- "downloadQrError": "Could not download QR code.",
- "viewRawIntent": "View raw intent link",
- "hideRawIntent": "Hide raw intent link",
- "created": "Created",
- "activeViewers": "{count, plural, one {# person is viewing this payment link} other {# people are viewing this payment link}}",
- "transaction": "Transaction",
- "paymentConfirmed": "Payment confirmed!",
- "paymentSent": "Payment sent!",
- "paymentFailed": "Payment failed. Please try again.",
- "connectedVia": "Connected via {provider}",
- "payInXlm": "Pay in XLM",
- "standardAssetOption": "Pay the invoice in {asset}",
- "pathPaymentOption": "Pay in XLM",
- "approximateCost": "Approximate cost in XLM",
- "merchantReceives": "Merchant receives {amount} {asset}",
- "quoteLoading": "Checking XLM path payment routes...",
- "quoteUnavailable": "XLM payment routing is unavailable for this invoice right now.",
- "quoteBuffer": "You may send up to {amount} XLM to cover price movement.",
- "pathPaymentHint": "Uses Stellar path payments under the hood through your connected wallet.",
- "approximateCostLabel": "Approximate cost in XLM",
- "approximateCostHelp": "Estimated amount to deliver {amount} {asset} to the merchant.",
- "slippageBuffer": "{percent}% safety buffer included. Max send: {sendMax} {asset}",
- "pathPaymentTogglePrefix": "Pay with",
- "pathPaymentToggleSuffix": "instead",
- "processing": "Processing...",
- "payWith": "Pay with {provider}",
- "payWithFallback": "Pay with Wallet",
- "reviewPaymentTitle": "Review Payment",
- "loadingNetworkFee": "Fetching current network fee...",
- "networkFeeLabel": "Network Fee: ~{amount} XLM",
- "networkFeeUnavailable": "Network fee unavailable right now.",
- "cancel": "Cancel",
- "confirmPayment": "Confirm Payment",
- "receivedTitle": "This payment has been received.",
- "receivedDescription": "The transaction was confirmed on-chain via PLUTO.",
- "downloadReceipt": "Download Receipt",
- "downloadReceiptLoading": "Preparing Receipt...",
- "receiptDownloaded": "Receipt download started.",
- "receiptDownloadFailed": "Could not generate the receipt PDF.",
- "receiptHashUnavailable": "Unavailable",
- "failedTitle": "This payment has failed.",
- "failedDescription": "Contact the merchant if you believe this is an error.",
- "status": {
- "pending": "Awaiting Payment",
- "confirmed": "Confirmed",
- "completed": "Completed",
- "failed": "Failed"
- }
}
-}
+}
\ No newline at end of file
diff --git a/frontend/messages/es.json b/frontend/messages/es.json
index c4b12a5e..4a285606 100644
--- a/frontend/messages/es.json
+++ b/frontend/messages/es.json
@@ -1,305 +1,44 @@
{
+ "Dashboard": {
+ "title": "Panel",
+ "welcome": "Bienvenido de nuevo",
+ "overview": "Resumen de pagos",
+ "description": "Supervisa tus pagos y actividad en tiempo real",
+ "businessOverview": "Resumen del negocio",
+ "liveConfirmations": "Confirmaciones en vivo",
+ "viewAllPayments": "Ver todos los pagos",
+ "quickActions": "Acciones rápidas",
+ "createPaymentLink": "Crear enlace de pago",
+ "withdrawFunds": "Retirar fondos",
+ "settings": "Configuración",
+ "viewDocs": "Ver documentación",
+ "development": "Desarrollo",
+ "apiKeysTip": "Administra tus claves API de forma segura",
+ "webhookLogsTip": "Monitorea los registros de webhooks"
+ },
"localeSwitcher": {
"label": "Idioma",
"ariaLabel": "Seleccionar idioma",
"options": {
- "en": "English",
- "es": "Espanol",
- "pt": "Portugues"
+ "en": "Inglés",
+ "es": "Español",
+ "pt": "Portugués",
+ "fr": "Francés"
}
},
"nav": {
"home": "Inicio",
- "docs": "Docs",
- "login": "Iniciar sesion",
- "register": "Registrarse",
- "toggleMenu": "Abrir menu"
- },
- "sidebar": {
- "overview": "Resumen",
+ "dashboard": "Panel",
"payments": "Pagos",
- "webhookLogs": "Registros Webhook",
- "apiKeys": "Claves API",
- "settings": "Configuracion",
- "workspace": "Area autenticada",
- "shortcuts": "Accesos",
- "createPayment": "Crear pago",
- "network": "Mainnet-testnet",
- "collapse": "Contraer barra lateral",
- "expand": "Expandir barra lateral",
- "close": "Cerrar navegacion"
- },
- "breadcrumbs": {
- "home": "Inicio",
- "segments": {
- "dashboard": "Panel",
- "create": "Crear",
- "payments": "Pagos",
- "settings": "Configuracion",
- "webhook-logs": "Registros Webhook",
- "api-keys": "Claves API"
- }
- },
- "copyButton": {
- "ariaLabel": "Copiar al portapapeles",
- "copied": "Copiado",
- "copiedState": "Copiado"
- },
- "dashboardPage": {
- "title": "Resumen del comercio",
- "description": "Supervisa tu volumen de transacciones, sigue pagos en tiempo real y administra tu integracion con Stellar.",
- "paymentMetrics": "Metricas de pago",
- "recentActivity": "Actividad reciente",
- "viewAllPayments": "Ver todos los pagos",
- "quickActions": "Acciones rapidas",
- "createPaymentLink": "Crear enlace de pago",
- "withdrawFunds": "Retirar fondos (SEP-24)",
- "development": "Desarrollo",
- "apiKeysTip": "Usa la pagina de Claves API para rotar tu token secreto periodicamente.",
- "webhookLogsTip": "Revisa Registros Webhook si los pagos no estan activando tu backend."
- },
- "createPaymentPage": {
- "eyebrow": "Panel",
- "title": "Crear enlace de pago",
- "description": "Define un monto, elige un activo e ingresa una direccion Stellar para generar un enlace compartible.",
- "newHere": "Eres nuevo?",
- "registerMerchant": "Registrar una cuenta de comercio"
- },
- "createPaymentForm": {
- "invalidAmount": "El monto debe ser un numero positivo.",
- "invalidRecipient": "El destinatario debe ser una clave publica valida de Stellar (56 caracteres, empieza con G).",
- "invalidHexColor": "{field} debe ser un color hexadecimal valido.",
- "failedCreate": "No se pudo crear el enlace de pago",
- "createdToast": "Enlace de pago creado",
- "noApiKeyTitle": "No se encontro una clave API",
- "noApiKeyDescription": "Registra una cuenta de comercio para obtener tu clave API y empezar a crear enlaces de pago.",
- "registerAsMerchant": "Registrarse como comercio",
- "readyEyebrow": "Enlace listo",
- "readyTitle": "Enlace creado correctamente",
- "readyDescription": "Comparte este enlace con tu cliente para cobrar el pago.",
- "shareLink": "Compartir enlace",
- "shareTitle": "Enlace de pago listo",
- "shareText": "Abre este enlace de pago de Stellar.",
- "shareFailed": "El uso compartido nativo no esta disponible ahora mismo.",
- "paymentLink": "Enlace de pago",
- "paymentId": "ID del pago",
- "status": "Estado",
- "createAnother": "Crear otro enlace de pago",
- "amount": "Monto",
- "amountPlaceholder": "Ingresa {exampleAmount} {asset}",
- "asset": "Activo",
- "selectAsset": "Seleccionar activo",
- "issuer": "Emisor",
- "trustedAddresses": "Seleccionar de direcciones confiables",
- "selectSavedAddress": "-- Selecciona una direccion guardada --",
- "recipientAddress": "Direccion del destinatario",
- "recipientPlaceholder": "Pega la billetera de destino para {asset}",
- "recipientPlaceholderSelected": "{label} seleccionado para {asset}",
- "descriptionLabel": "Descripcion",
- "optional": "opcional",
- "descriptionPlaceholder": "Para que es este cobro en {asset}?",
- "brandingTitle": "Personalizacion temporal",
- "brandingDescription": "Aplica colores personalizados solo a este enlace de pago.",
- "enabled": "Activado",
- "disabled": "Desactivado",
- "primary": "Primario",
- "secondary": "Secundario",
- "background": "Fondo",
- "checkoutPreview": "Vista previa del checkout",
- "payNow": "Pagar ahora",
- "generating": "Generando...",
- "generate": "Generar enlace de pago",
- "rateLimitError": "Estás creando enlaces demasiado rápido. Inténtalo de nuevo en {seconds} segundos.",
- "retryWait": "Espera {seconds}s…",
- "integrationCode": "Código de Integración",
- "snippetsHelper": "Los fragmentos de código se actualizan automáticamente con los valores del formulario."
- },
- "paymentMetrics": {
- "downloadImage": "Descargar imagen",
- "downloadPng": "Descargar PNG",
- "downloadSvg": "Descargar SVG",
- "exporting": "Exportando...",
- "exportSuccess": "Grafico descargado como {format}",
- "exportFailed": "No se pudo exportar el grafico.",
- "fetchMetricsFailed": "No se pudieron cargar las metricas",
- "fetchVolumeFailed": "No se pudieron cargar los datos de volumen",
- "retry": "Reintentar",
- "sevenDayVolume": "Volumen de 7 dias",
- "totalPayments": "Pagos totales",
- "paymentsCount": "{count, plural, one {pago} other {pagos}}",
- "chartTitle": "Comparacion de volumen por activo",
- "chartSubtitle": "Volumen diario de transacciones desglosado por activo",
- "heatmapTitle": "Mapa de calor de pagos",
- "heatmapSubtitle": "Intensidad de actividad estilo GitHub durante los ultimos 365 dias",
- "heatmapTotal": "{count, plural, one {# pago en el ultimo ano} other {# pagos en el ultimo ano}}",
- "heatmapTooltip": "{count, plural, one {# pago} other {# pagos}} el {date}",
- "heatmapAriaLabel": "Mapa de calor de pagos de 365 dias",
- "heatmapEmptyDay": "No se registraron pagos",
- "heatmapLegendLess": "Menos",
- "heatmapLegendMore": "Mas",
- "heatmapWeekdays": {
- "sun": "D",
- "mon": "L",
- "tue": "M",
- "wed": "X",
- "thu": "J",
- "fri": "V",
- "sat": "S"
+ "paymentHistory": "Historial de pagos",
+ "Settings": {
+ "testWebhookSent": "¡Webhook de prueba enviado!"
},
- "showRange": "Mostrar {range}",
- "toggleAssetVisibility": "Cambiar visibilidad del activo",
- "showAsset": "Mostrar {asset}",
- "hideAsset": "Ocultar {asset}",
- "noPayments": "No hay pagos completados en este periodo.",
- "weeklyAvgLabel": "Prom. 7D",
- "ranges": {
- "7D": "7 dias",
- "30D": "30 dias",
- "1Y": "1 ano"
- }
- },
- "paymentsPage": {
- "title": "Historial de pagos",
- "description": "Supervisa todas las transacciones entrantes y su estado de verificacion."
- },
- "recentPayments": {
- "missingApiKey": "No se encontro la clave API. Registrate o inicia sesion.",
- "fetchFailed": "No se pudieron obtener los pagos",
- "loadFailed": "No se pudieron cargar los pagos",
- "connectionError": "Error de conexion",
- "backendHint": "Asegurate de que el backend este activo y que el endpoint de pagos este disponible.",
- "retryConnection": "Reintentar conexion",
- "testWebhook": "Probar webhook de todos modos",
- "troubleshootingTip": "Consejo de solucion",
- "troubleshootingDescription": "Todavia puedes probar la funcionalidad webhook mientras se restauran los servicios del backend.",
- "emptyTitle": "Todavia no hay pagos",
- "emptyDescription": "Empieza a aceptar pagos creando tu primer enlace o probando webhooks para ver el flujo de datos.",
- "createPaymentLink": "Crear enlace de pago",
- "sendTestWebhook": "Enviar webhook de prueba",
- "gettingStartedTitle": "Guia de inicio",
- "gettingStartedDescription": "Usa herramientas webhook para probar notificaciones de pago y ver datos en tiempo real en este panel.",
- "search": "Buscar",
- "searchPlaceholder": "Buscar por ID de pago o descripcion...",
- "status": "Estado",
- "allStatuses": "Todos los estados",
- "asset": "Activo",
- "allAssets": "Todos los activos",
- "fromDate": "Desde",
- "toDate": "Hasta",
- "activeFilters": "Filtros activos:",
- "searchChip": "Busqueda: \"{value}\"",
- "statusChip": "Estado: {value}",
- "assetChip": "Activo: {value}",
- "fromChip": "Desde: {value}",
- "toChip": "Hasta: {value}",
- "clearSearchFilter": "Borrar filtro de busqueda",
- "clearStatusFilter": "Borrar filtro de estado",
- "clearAssetFilter": "Borrar filtro de activo",
- "clearFromDateFilter": "Borrar filtro de fecha inicial",
- "clearToDateFilter": "Borrar filtro de fecha final",
- "clearAll": "Borrar todo",
- "linkCopied": "¡Enlace de pago copiado al portapapeles!",
- "showingResults": "Mostrando {shown} de {total} pagos",
- "filteredSuffix": "(con filtros)",
- "tableStatus": "Estado",
- "tableAmount": "Monto",
- "tableRecipient": "Destinatario",
- "tableDate": "Fecha",
- "tableLink": "Enlace",
- "view": "Ver",
- "emptyDescriptionValue": "-",
- "statuses": {
- "all": "Todos",
- "pending": "Pendiente",
- "confirmed": "Confirmado",
- "failed": "Fallido",
- "refunded": "Reembolsado"
- }
- },
- "walletSelector": {
- "chooseWallet": "Elige una billetera",
- "description": "Conecta tu billetera Stellar para completar este pago.",
- "connecting": "Conectando...",
- "walletConnectWaiting": "Esperando la billetera...",
- "noProjectId": "(sin project ID)",
- "notInstalled": "(no instalada)",
- "tapToConnect": "Haz clic para conectar",
- "scanTitle": "Escanea con tu app de billetera Stellar",
- "scanDescription": "Escanea con Freighter móvil o cualquier billetera compatible con WalletConnect",
- "pairingFailed": "La vinculacion de WalletConnect fallo",
- "userRejected": "La solicitud de conexión fue cancelada. Apruébala en tu billetera para continuar.",
- "walletConnectUnavailable": "WalletConnect no está disponible en este momento. Prueba con Freighter o habilita un WalletConnect project ID válido.",
- "extensionNotFound": "No pudimos encontrar esa billetera en este dispositivo. Instala la extensión e inténtalo de nuevo.",
- "noAccountFound": "La billetera se conectó, pero no devolvió ninguna cuenta Stellar. Abre una cuenta Stellar en la billetera y vuelve a intentarlo.",
- "walletConnectFailed": "No pudimos completar la conexión con WalletConnect. Inténtalo de nuevo.",
- "connectionFailed": "No pudimos conectar esa billetera en este momento. Inténtalo de nuevo."
- },
- "checkout": {
- "paymentRequest": "Solicitud de pago",
- "completePayment": "Completar pago",
- "paymentNotFound": "Pago no encontrado",
- "loadFailed": "No se pudieron cargar los detalles del pago.",
- "paymentMissing": "Pago no encontrado.",
- "loadPaymentFailed": "No se pudo cargar el pago.",
- "errorTitle": "Error",
- "errorDescription": "Revisa el enlace de pago e intentalo de nuevo o contacta al remitente.",
- "processingFallback": "Procesando transaccion...",
- "doNotClose": "No cierres esta pestana",
- "recipient": "Destinatario",
- "scanToPay": "Escanear para pagar",
- "scanDescription": "Escanea con Freighter o cualquier billetera Stellar",
- "openQrModal": "Pagar con codigo QR",
- "qrModalTitle": "Pagar con codigo QR",
- "qrModalDescription": "Escanea este codigo en otro dispositivo o compartelo con tu app de billetera.",
- "downloadQr": "Descargar PNG",
- "downloadQrSuccess": "Codigo QR descargado.",
- "downloadQrError": "No se pudo descargar el codigo QR.",
- "viewRawIntent": "Ver enlace intent sin procesar",
- "hideRawIntent": "Ocultar enlace intent sin procesar",
- "created": "Creado",
- "activeViewers": "{count, plural, one {# persona esta viendo este enlace de pago} other {# personas estan viendo este enlace de pago}}",
- "transaction": "Transaccion",
- "paymentConfirmed": "Pago confirmado!",
- "paymentSent": "Pago enviado",
- "paymentFailed": "El pago fallo. Intentalo de nuevo.",
- "connectedVia": "Conectado mediante {provider}",
- "payInXlm": "Pagar en XLM",
- "standardAssetOption": "Pagar la factura en {asset}",
- "pathPaymentOption": "Pagar en XLM",
- "approximateCost": "Costo aproximado en XLM",
- "merchantReceives": "El comercio recibe {amount} {asset}",
- "quoteLoading": "Buscando rutas de pago en XLM...",
- "quoteUnavailable": "La ruta de pago en XLM no esta disponible para esta factura ahora mismo.",
- "quoteBuffer": "Puedes enviar hasta {amount} XLM para cubrir movimientos de precio.",
- "pathPaymentHint": "Usa Stellar path payments desde tu billetera conectada.",
- "approximateCostLabel": "Costo aproximado en XLM",
- "approximateCostHelp": "Monto estimado para entregar {amount} {asset} al comercio.",
- "slippageBuffer": "Incluye un margen de seguridad del {percent}%. Envio maximo: {sendMax} {asset}",
- "pathPaymentTogglePrefix": "Pagar con",
- "pathPaymentToggleSuffix": "en su lugar",
- "processing": "Procesando...",
- "payWith": "Pagar con {provider}",
- "payWithFallback": "Pagar con billetera",
- "reviewPaymentTitle": "Revisar pago",
- "loadingNetworkFee": "Obteniendo la comision actual de red...",
- "networkFeeLabel": "Comision de red: ~{amount} XLM",
- "networkFeeUnavailable": "La comision de red no esta disponible en este momento.",
- "cancel": "Cancelar",
- "confirmPayment": "Confirmar pago",
- "receivedTitle": "Este pago ya fue recibido.",
- "receivedDescription": "La transaccion fue confirmada en la red Stellar.",
- "downloadReceipt": "Descargar recibo",
- "downloadReceiptLoading": "Preparando recibo...",
- "receiptDownloaded": "La descarga del recibo ha comenzado.",
- "receiptDownloadFailed": "No se pudo generar el PDF del recibo.",
- "receiptHashUnavailable": "No disponible",
- "failedTitle": "Este pago ha fallado.",
- "failedDescription": "Contacta al comercio si crees que esto es un error.",
- "status": {
- "pending": "Esperando pago",
- "confirmed": "Confirmado",
- "completed": "Completado",
- "failed": "Fallido"
- }
+ "apiKeys": "Claves API",
+ "webhookLogs": "Registros de webhooks",
+ "login": "Iniciar sesión",
+ "register": "Registrarse",
+ "docs": "Documentación",
+ "toggleMenu": "Alternar menú"
}
-}
+}
\ No newline at end of file
diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json
new file mode 100644
index 00000000..3a75af95
--- /dev/null
+++ b/frontend/messages/fr.json
@@ -0,0 +1,32 @@
+{
+ "Dashboard": {
+ "title": "Tableau de bord",
+ "welcome": "Bon retour",
+ "overview": "Aperçu des paiements"
+ },
+ "localeSwitcher": {
+ "label": "Langue",
+ "ariaLabel": "Choisir la langue",
+ "options": {
+ "en": "Anglais",
+ "es": "Espagnol",
+ "pt": "Portugais",
+ "fr": "Français"
+ }
+ },
+ "nav": {
+ "home": "Accueil",
+ "dashboard": "Tableau de bord",
+ "payments": "Paiements",
+ "paymentHistory": "Historique des paiements",
+ "Settings": {
+ "testWebhookSent": "Webhook de test envoyé !"
+},
+ "apiKeys": "Clés API",
+ "webhookLogs": "Journaux de webhooks",
+ "login": "Connexion",
+ "register": "Inscription",
+ "docs": "Documentation",
+ "toggleMenu": "Basculer le menu"
+ }
+}
\ No newline at end of file
diff --git a/frontend/messages/pt.json b/frontend/messages/pt.json
index 9fb7811e..84d76b73 100644
--- a/frontend/messages/pt.json
+++ b/frontend/messages/pt.json
@@ -1,151 +1,47 @@
{
+ "Dashboard": {
+ "title": "Painel",
+ "welcome": "Bem-vindo de volta",
+ "overview": "Visão geral de pagamentos",
+ "description": "Monitore seus pagamentos e atividades em tempo real",
+ "businessOverview": "Visão geral do negócio",
+ "liveConfirmations": "Confirmações ao vivo",
+ "viewAllPayments": "Ver todos os pagamentos",
+ "quickActions": "Ações rápidas",
+ "createPaymentLink": "Criar link de pagamento",
+ "withdrawFunds": "Sacar fundos",
+ "settings": "Configurações",
+ "viewDocs": "Ver documentação",
+ "development": "Desenvolvimento",
+ "apiKeysTip": "Gerencie suas chaves de API com segurança",
+ "webhookLogsTip": "Monitore os logs de webhook"
+ },
"localeSwitcher": {
"label": "Idioma",
"ariaLabel": "Selecionar idioma",
"options": {
- "en": "English",
- "es": "Espanol",
- "pt": "Portugues"
+ "en": "Inglês",
+ "es": "Espanhol",
+ "pt": "Português",
+ "fr": "Francês"
}
},
"nav": {
- "home": "Inicio",
- "docs": "Docs",
- "login": "Entrar",
- "register": "Cadastrar",
- "toggleMenu": "Abrir menu"
- },
- "sidebar": {
- "overview": "Visao geral",
+ "home": "Início",
+ "dashboard": "Painel",
"payments": "Pagamentos",
- "webhookLogs": "Logs de Webhook",
- "apiKeys": "Chaves API",
- "settings": "Configuracoes",
- "workspace": "Area autenticada",
- "shortcuts": "Atalhos",
- "createPayment": "Criar pagamento",
- "network": "Mainnet-testnet",
- "collapse": "Recolher barra lateral",
- "expand": "Expandir barra lateral",
- "close": "Fechar navegacao"
- },
- "breadcrumbs": {
- "home": "Inicio",
- "segments": {
- "dashboard": "Painel",
- "create": "Criar",
- "payments": "Pagamentos",
- "settings": "Configuracoes",
- "webhook-logs": "Logs de Webhook",
- "api-keys": "Chaves API"
- }
- },
- "copyButton": {
- "ariaLabel": "Copiar para a area de transferencia",
- "copied": "Copiado",
- "copiedState": "Copiado"
- },
- "dashboardPage": {
- "title": "Visao geral do lojista",
- "description": "Acompanhe seu volume de transacoes, monitore pagamentos em tempo real e gerencie sua integracao Stellar.",
- "paymentMetrics": "Metricas de pagamento",
- "recentActivity": "Atividade recente",
- "viewAllPayments": "Ver todos os pagamentos",
- "quickActions": "Acoes rapidas",
- "createPaymentLink": "Criar link de pagamento",
- "withdrawFunds": "Sacar fundos (SEP-24)",
- "development": "Desenvolvimento",
- "apiKeysTip": "Use a pagina de Chaves API para rotacionar seu token secreto periodicamente.",
- "webhookLogsTip": "Confira os Logs de Webhook se os pagamentos nao estiverem acionando seu backend."
- },
- "createPaymentPage": {
- "eyebrow": "Painel",
- "title": "Criar link de pagamento",
- "description": "Defina um valor, escolha um ativo e informe um endereco Stellar para gerar um link compartilhavel.",
- "newHere": "Novo aqui?",
- "registerMerchant": "Cadastrar uma conta de lojista"
- },
- "createPaymentForm": {
- "invalidAmount": "O valor deve ser um numero positivo.",
- "invalidRecipient": "O destinatario deve ser uma chave publica Stellar valida (56 caracteres, comeca com G).",
- "invalidHexColor": "{field} deve ser uma cor hexadecimal valida.",
- "failedCreate": "Falha ao criar o link de pagamento",
- "createdToast": "Link de pagamento criado",
- "noApiKeyTitle": "Nenhuma chave API encontrada",
- "noApiKeyDescription": "Cadastre uma conta de lojista para receber sua chave API e comecar a criar links de pagamento.",
- "registerAsMerchant": "Cadastrar como lojista",
- "readyEyebrow": "Link pronto",
- "readyTitle": "Link criado com sucesso",
- "readyDescription": "Compartilhe este link com seu cliente para receber o pagamento.",
- "shareLink": "Compartilhar link",
- "shareTitle": "Link de pagamento pronto",
- "shareText": "Abra este link de pagamento Stellar.",
- "shareFailed": "O compartilhamento nativo nao esta disponivel agora.",
- "paymentLink": "Link de pagamento",
- "paymentId": "ID do pagamento",
- "status": "Status",
- "createAnother": "Criar outro link de pagamento",
- "amount": "Valor",
- "amountPlaceholder": "Digite {exampleAmount} {asset}",
- "asset": "Ativo",
- "selectAsset": "Selecionar ativo",
- "issuer": "Emissor",
- "trustedAddresses": "Selecionar entre enderecos confiaveis",
- "selectSavedAddress": "-- Selecione um endereco salvo --",
- "recipientAddress": "Endereco do destinatario",
- "recipientPlaceholder": "Cole a carteira de destino para {asset}",
- "recipientPlaceholderSelected": "{label} selecionado para {asset}",
- "descriptionLabel": "Descricao",
- "optional": "opcional",
- "descriptionPlaceholder": "Qual e o contexto desta cobranca em {asset}?",
- "brandingTitle": "Personalizacao desta sessao",
- "brandingDescription": "Aplique cores personalizadas somente neste link de pagamento.",
- "enabled": "Ativado",
- "disabled": "Desativado",
- "primary": "Primaria",
- "secondary": "Secundaria",
- "background": "Fundo",
- "checkoutPreview": "Previa do checkout",
- "payNow": "Pagar agora",
- "generating": "Gerando...",
- "generate": "Gerar link de pagamento",
- "rateLimitError": "Você está criando links rápido demais. Tente novamente em {seconds} segundos.",
- "retryWait": "Aguarde {seconds}s…",
- "integrationCode": "Código de Integração",
- "snippetsHelper": "Os trechos de código são atualizados automaticamente com os valores do formulário."
- },
- "paymentMetrics": {
- "downloadImage": "Baixar imagem",
- "downloadPng": "Baixar PNG",
- "downloadSvg": "Baixar SVG",
- "exporting": "Exportando...",
- "exportSuccess": "Grafico baixado como {format}",
- "exportFailed": "Falha ao exportar o grafico.",
- "fetchMetricsFailed": "Falha ao buscar metricas",
- "fetchVolumeFailed": "Falha ao buscar dados de volume",
- "retry": "Tentar novamente",
- "sevenDayVolume": "Volume de 7 dias",
- "totalPayments": "Total de pagamentos",
- "paymentsCount": "{count, plural, one {pagamento} other {pagamentos}}",
- "chartTitle": "Comparacao de volume por ativo",
- "chartSubtitle": "Volume diario de transacoes por ativo",
- "heatmapTitle": "Mapa de calor de pagamentos",
- "heatmapSubtitle": "Intensidade de atividade estilo GitHub nos ultimos 365 dias",
- "heatmapTotal": "{count, plural, one {# pagamento no ultimo ano} other {# pagamentos no ultimo ano}}",
- "heatmapTooltip": "{count, plural, one {# pagamento} other {# pagamentos}} em {date}",
- "heatmapAriaLabel": "Mapa de calor de pagamentos de 365 dias",
- "heatmapEmptyDay": "Nenhum pagamento registrado",
- "heatmapLegendLess": "Menos",
- "heatmapLegendMore": "Mais",
- "heatmapWeekdays": {
- "sun": "D",
- "mon": "S",
- "tue": "T",
- "wed": "Q",
- "thu": "Q",
- "fri": "S",
- "sat": "S"
+ "paymentHistory": "Histórico de pagamentos",
+ "Settings": {
+ "testWebhookSent": "Webhook de teste enviado!"
},
+<<<<<<< HEAD
+ "apiKeys": "Chaves de API",
+ "webhookLogs": "Logs de webhook",
+ "login": "Entrar",
+ "register": "Registrar",
+ "docs": "Documentação",
+ "toggleMenu": "Alternar menu"
+=======
"showRange": "Mostrar {range}",
"toggleAssetVisibility": "Alternar visibilidade do ativo",
"showAsset": "Mostrar {asset}",
@@ -301,5 +197,6 @@
"completed": "Concluido",
"failed": "Falhou"
}
+>>>>>>> upstream/main
}
-}
+}
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index b00ff56f..fe187a39 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "stellar-payment-frontend",
+ "name": "pluto-frontend",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "stellar-payment-frontend",
+ "name": "pluto-frontend",
"version": "0.1.0",
"dependencies": {
"@ducanh2912/next-pwa": "^10.2.9",
@@ -24,7 +24,7 @@
"motion": "^12.38.0",
"next": "^14.2.5",
"next-intl": "^4.8.3",
- "next-mdx-remote": "^5.0.0",
+ "next-mdx-remote": "^6.0.0",
"next-themes": "^0.4.6",
"prismjs": "^1.30.0",
"qrcode.react": "^4.2.0",
@@ -14816,15 +14816,16 @@
}
},
"node_modules/next-mdx-remote": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-5.0.0.tgz",
- "integrity": "sha512-RNNbqRpK9/dcIFZs/esQhuLA8jANqlH694yqoDBK8hkVdJUndzzGmnPHa2nyi90N4Z9VmzuSWNRpr5ItT3M7xQ==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-6.0.0.tgz",
+ "integrity": "sha512-cJEpEZlgD6xGjB4jL8BnI8FaYdN9BzZM4NwadPe1YQr7pqoWjg9EBCMv3nXBkuHqMRfv2y33SzUsuyNh9LFAQQ==",
"license": "MPL-2.0",
"dependencies": {
"@babel/code-frame": "^7.23.5",
"@mdx-js/mdx": "^3.0.1",
"@mdx-js/react": "^3.0.1",
- "unist-util-remove": "^3.1.0",
+ "unist-util-remove": "^4.0.0",
+ "unist-util-visit": "^5.1.0",
"vfile": "^6.0.1",
"vfile-matter": "^5.0.0"
},
@@ -18751,47 +18752,14 @@
}
},
"node_modules/unist-util-remove": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-3.1.1.tgz",
- "integrity": "sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^2.0.0",
- "unist-util-is": "^5.0.0",
- "unist-util-visit-parents": "^5.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/unist-util-remove/node_modules/@types/unist": {
- "version": "2.0.11",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
- "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
- "license": "MIT"
- },
- "node_modules/unist-util-remove/node_modules/unist-util-is": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz",
- "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==",
- "license": "MIT",
- "dependencies": {
- "@types/unist": "^2.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/unified"
- }
- },
- "node_modules/unist-util-remove/node_modules/unist-util-visit-parents": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz",
- "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz",
+ "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==",
"license": "MIT",
"dependencies": {
- "@types/unist": "^2.0.0",
- "unist-util-is": "^5.0.0"
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
},
"funding": {
"type": "opencollective",
diff --git a/frontend/src/app/(authenticated)/dashboard/page.tsx b/frontend/src/app/(authenticated)/dashboard/page.tsx
index ba29dcb4..017e7766 100644
--- a/frontend/src/app/(authenticated)/dashboard/page.tsx
+++ b/frontend/src/app/(authenticated)/dashboard/page.tsx
@@ -13,14 +13,12 @@ import {
} from "@/lib/merchant-store";
import { useTranslations } from "next-intl";
import FirstApiKeyModal from "@/components/FirstApiKeyModal";
-import PaymentMetrics from "@/components/PaymentMetrics";
-import RecentPayments from "@/components/RecentPayments";
-import WithdrawModal from "@/components/WithdrawModal";
+import WithdrawalModal from "@/components/WithdrawalModal";
export default function DashboardPage() {
- const t = useTranslations("dashboardPage");
- const [isFirstKeyModalOpen, setIsFirstKeyModalOpen] = useState(false);
+ const t = useTranslations("Dashboard");
const [isWithdrawOpen, setIsWithdrawOpen] = useState(false);
+ const [isFirstKeyModalOpen, setIsFirstKeyModalOpen] = useState(false);
const hydrated = useMerchantHydrated();
const apiKey = useMerchantApiKey();
const merchant = useMerchantMetadata();
@@ -30,7 +28,8 @@ export default function DashboardPage() {
useEffect(() => {
if (hydrated) {
- setLoading(false);
+ const timer = setTimeout(() => setLoading(false), 1000);
+ return () => clearTimeout(timer);
}
}, [hydrated]);
@@ -44,100 +43,163 @@ export default function DashboardPage() {
if (!hydrated || loading) return
- Overview of your Stellar payment ecosystem and performance. -
-+ Overview +
+{t("description")}
+Settings
-No API key found
+{t("noApiKey")}
Register a merchant account first to manage your credentials here.
- Register as Merchant + {t("registerMerchant")}Account
-Manage your credentials, branding, and integrations.
+{t("description")}