Update Stripe publishable key storage in Stripe Settings

1. Created src/hooks/use-stripe-config.js - New hook that:
	- Fetches publishable key from /api/config/stripe
	- Returns a pre-initialized stripePromise for use with Stripe Elements
	- Caches the result to avoid multiple API calls
	- Falls back to REACT_APP_STRIPE_PUBLISHABLE_KEY env var if API fails
2. Updated AdminSettings.js - Added publishable key input field in the Stripe settings form
3. Updated AdminPaymentMethodsPanel.js - Uses useStripeConfig hook instead of env variable
4. Updated PaymentMethodsSection.js - Uses useStripeConfig hook instead of env variable
This commit is contained in:
2026-02-02 17:55:00 +07:00
parent 82ef36b439
commit 68fc34d0a5
4 changed files with 162 additions and 12 deletions

View File

@@ -16,11 +16,13 @@ export default function AdminSettings() {
// Form state
const [formData, setFormData] = useState({
publishable_key: '',
secret_key: '',
webhook_secret: ''
});
// Show/hide sensitive values
const [showPublishableKey, setShowPublishableKey] = useState(false);
const [showSecretKey, setShowSecretKey] = useState(false);
const [showWebhookSecret, setShowWebhookSecret] = useState(false);
@@ -57,6 +59,7 @@ export default function AdminSettings() {
const handleEditClick = () => {
setIsEditing(true);
setFormData({
publishable_key: '',
secret_key: '',
webhook_secret: ''
});
@@ -65,17 +68,24 @@ export default function AdminSettings() {
const handleCancelEdit = () => {
setIsEditing(false);
setFormData({
publishable_key: '',
secret_key: '',
webhook_secret: ''
});
setShowPublishableKey(false);
setShowSecretKey(false);
setShowWebhookSecret(false);
};
const handleSave = async () => {
// Validate inputs
if (!formData.secret_key || !formData.webhook_secret) {
toast.error('Both Secret Key and Webhook Secret are required');
if (!formData.publishable_key || !formData.secret_key || !formData.webhook_secret) {
toast.error('All three keys are required: Publishable Key, Secret Key, and Webhook Secret');
return;
}
if (!formData.publishable_key.startsWith('pk_test_') && !formData.publishable_key.startsWith('pk_live_')) {
toast.error('Invalid Publishable Key format. Must start with pk_test_ or pk_live_');
return;
}
@@ -89,15 +99,25 @@ export default function AdminSettings() {
return;
}
// Check environment consistency
const pkIsLive = formData.publishable_key.startsWith('pk_live_');
const skIsLive = formData.secret_key.startsWith('sk_live_');
if (pkIsLive !== skIsLive) {
toast.error('Publishable Key and Secret Key must be from the same environment (both test or both live)');
return;
}
setSaving(true);
try {
await api.put('/admin/settings/stripe', formData);
toast.success('Stripe settings updated successfully');
setIsEditing(false);
setFormData({
publishable_key: '',
secret_key: '',
webhook_secret: ''
});
setShowPublishableKey(false);
setShowSecretKey(false);
setShowWebhookSecret(false);
// Refresh status
@@ -157,6 +177,31 @@ export default function AdminSettings() {
{isEditing ? (
/* Edit Mode */
<div className="space-y-6">
{/* Publishable Key Input */}
<div className="space-y-2">
<Label htmlFor="publishable_key">Stripe Publishable Key</Label>
<div className="relative">
<Input
id="publishable_key"
type={showPublishableKey ? 'text' : 'password'}
value={formData.publishable_key}
onChange={(e) => setFormData({ ...formData, publishable_key: e.target.value })}
placeholder="pk_test_... or pk_live_..."
className="pr-10"
/>
<button
type="button"
onClick={() => setShowPublishableKey(!showPublishableKey)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700"
>
{showPublishableKey ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
<p className="text-xs text-gray-500">
Get this from your Stripe Dashboard Developers API keys (Publishable key)
</p>
</div>
{/* Secret Key Input */}
<div className="space-y-2">
<Label htmlFor="secret_key">Stripe Secret Key</Label>
@@ -178,7 +223,7 @@ export default function AdminSettings() {
</button>
</div>
<p className="text-xs text-gray-500">
Get this from your Stripe Dashboard Developers API keys
Get this from your Stripe Dashboard Developers API keys (Secret key)
</p>
</div>
@@ -267,7 +312,7 @@ export default function AdminSettings() {
<div className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
<div>
<p className="font-semibold text-gray-900">Environment</p>
<p className="text-sm text-gray-600">Detected from secret key prefix</p>
<p className="text-sm text-gray-600">Detected from key prefixes</p>
</div>
<span className={`px-3 py-1 rounded-full text-sm font-semibold ${
stripeStatus.environment === 'live'
@@ -278,6 +323,20 @@ export default function AdminSettings() {
</span>
</div>
<div className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
<div>
<p className="font-semibold text-gray-900">Publishable Key</p>
<p className="text-sm text-gray-600 font-mono">
{stripeStatus.publishable_key_prefix}...
</p>
</div>
{stripeStatus.publishable_key_set ? (
<CheckCircle className="h-5 w-5 text-green-600" />
) : (
<AlertCircle className="h-5 w-5 text-amber-600" />
)}
</div>
<div className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
<div>
<p className="font-semibold text-gray-900">Secret Key</p>