This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
155 lines
4.7 KiB
TypeScript
155 lines
4.7 KiB
TypeScript
/**
|
|
* Payment Hooks
|
|
* React Query hooks for payment configuration management
|
|
*/
|
|
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import * as paymentsApi from '../api/payments';
|
|
|
|
// ============================================================================
|
|
// Query Keys
|
|
// ============================================================================
|
|
|
|
export const paymentKeys = {
|
|
all: ['payments'] as const,
|
|
config: () => [...paymentKeys.all, 'config'] as const,
|
|
apiKeys: () => [...paymentKeys.all, 'apiKeys'] as const,
|
|
connectStatus: () => [...paymentKeys.all, 'connectStatus'] as const,
|
|
};
|
|
|
|
// ============================================================================
|
|
// Unified Configuration Hook
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get unified payment configuration status.
|
|
* Returns the complete payment setup for the business.
|
|
*/
|
|
export const usePaymentConfig = () => {
|
|
return useQuery({
|
|
queryKey: paymentKeys.config(),
|
|
queryFn: () => paymentsApi.getPaymentConfig().then(res => res.data),
|
|
staleTime: 30 * 1000, // 30 seconds
|
|
});
|
|
};
|
|
|
|
// ============================================================================
|
|
// API Keys Hooks (Free Tier)
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get current API key configuration (masked).
|
|
*/
|
|
export const useApiKeys = () => {
|
|
return useQuery({
|
|
queryKey: paymentKeys.apiKeys(),
|
|
queryFn: () => paymentsApi.getApiKeys().then(res => res.data),
|
|
staleTime: 30 * 1000,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Validate API keys without saving.
|
|
*/
|
|
export const useValidateApiKeys = () => {
|
|
return useMutation({
|
|
mutationFn: ({ secretKey, publishableKey }: { secretKey: string; publishableKey: string }) =>
|
|
paymentsApi.validateApiKeys(secretKey, publishableKey).then(res => res.data),
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Save API keys.
|
|
*/
|
|
export const useSaveApiKeys = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ secretKey, publishableKey }: { secretKey: string; publishableKey: string }) =>
|
|
paymentsApi.saveApiKeys(secretKey, publishableKey).then(res => res.data),
|
|
onSuccess: () => {
|
|
// Invalidate payment config to refresh status
|
|
queryClient.invalidateQueries({ queryKey: paymentKeys.config() });
|
|
queryClient.invalidateQueries({ queryKey: paymentKeys.apiKeys() });
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Re-validate stored API keys.
|
|
*/
|
|
export const useRevalidateApiKeys = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: () => paymentsApi.revalidateApiKeys().then(res => res.data),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: paymentKeys.config() });
|
|
queryClient.invalidateQueries({ queryKey: paymentKeys.apiKeys() });
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Delete stored API keys.
|
|
*/
|
|
export const useDeleteApiKeys = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: () => paymentsApi.deleteApiKeys().then(res => res.data),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: paymentKeys.config() });
|
|
queryClient.invalidateQueries({ queryKey: paymentKeys.apiKeys() });
|
|
},
|
|
});
|
|
};
|
|
|
|
// ============================================================================
|
|
// Stripe Connect Hooks (Paid Tiers)
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get current Connect account status.
|
|
*/
|
|
export const useConnectStatus = () => {
|
|
return useQuery({
|
|
queryKey: paymentKeys.connectStatus(),
|
|
queryFn: () => paymentsApi.getConnectStatus().then(res => res.data),
|
|
staleTime: 30 * 1000,
|
|
// Only fetch if we might have a Connect account
|
|
enabled: true,
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Initiate Connect account onboarding.
|
|
*/
|
|
export const useConnectOnboarding = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ refreshUrl, returnUrl }: { refreshUrl: string; returnUrl: string }) =>
|
|
paymentsApi.initiateConnectOnboarding(refreshUrl, returnUrl).then(res => res.data),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: paymentKeys.config() });
|
|
queryClient.invalidateQueries({ queryKey: paymentKeys.connectStatus() });
|
|
},
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Refresh Connect onboarding link.
|
|
*/
|
|
export const useRefreshConnectLink = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ refreshUrl, returnUrl }: { refreshUrl: string; returnUrl: string }) =>
|
|
paymentsApi.refreshConnectOnboardingLink(refreshUrl, returnUrl).then(res => res.data),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: paymentKeys.connectStatus() });
|
|
},
|
|
});
|
|
};
|