Stripe was wired up but never used: - No STRIPE_SECRET_KEY in .env - Card payment step had a 'simulated fallback' that pretended to charge - Stripe fees (1.4% + 20p) contradict '100% goes to charity' brand promise - Bank transfer is the primary rail, GoCardless (DD) is the secondary Removed: - src/lib/stripe.ts (Stripe client, checkout sessions, webhooks) - src/app/api/stripe/checkout/route.ts - src/app/api/stripe/webhook/route.ts - src/app/p/[token]/steps/card-payment-step.tsx (263 lines) - 'stripe' and '@stripe/stripe-js' npm packages - Card option from PaymentStep (payment-step.tsx) - Card references from confirmation-step.tsx, success/page.tsx - Stripe from landing page integrations grid - Stripe from privacy policy sub-processors - Stripe from terms of service payment references Type Rail changed: 'bank' | 'gocardless' | 'card' → 'bank' | 'gocardless' Pledge flow bundle: 19.5kB → 18.2kB (-1.3kB) Payment options donors now see: 1. Bank Transfer (recommended, zero fees) 2. Direct Debit via GoCardless (1% + 20p, hassle-free)
109 lines
4.2 KiB
TypeScript
109 lines
4.2 KiB
TypeScript
"use client"
|
|
|
|
import { Building2, Landmark, Shield, CheckCircle2 } from "lucide-react"
|
|
|
|
interface Props {
|
|
onSelect: (rail: "bank" | "gocardless") => void
|
|
amount: number
|
|
}
|
|
|
|
export function PaymentStep({ onSelect, amount }: Props) {
|
|
const pounds = (amount / 100).toFixed(0)
|
|
const giftAidTotal = ((amount + amount * 0.25) / 100).toFixed(0)
|
|
|
|
const options = [
|
|
{
|
|
id: "bank" as const,
|
|
icon: Building2,
|
|
title: "Bank Transfer",
|
|
subtitle: "100% goes to charity — zero fees",
|
|
tag: "Recommended",
|
|
tagClass: "bg-success-green text-white",
|
|
detail: "We'll give you the bank details. Transfer in your own time.",
|
|
fee: "Free",
|
|
feeClass: "text-success-green font-bold",
|
|
iconBg: "bg-fulfilled-green",
|
|
highlight: true,
|
|
benefits: ["Zero fees", "Most charities prefer this"],
|
|
},
|
|
{
|
|
id: "gocardless" as const,
|
|
icon: Landmark,
|
|
title: "Direct Debit",
|
|
subtitle: "Automatic collection — set and forget",
|
|
tag: "Hassle-free",
|
|
tagClass: "bg-trust-blue/10 text-trust-blue",
|
|
detail: "GoCardless collects it for you. Protected by the DD Guarantee.",
|
|
fee: "1% + 20p",
|
|
feeClass: "text-muted-foreground",
|
|
iconBg: "bg-promise-blue",
|
|
highlight: false,
|
|
benefits: ["No action needed", "DD Guarantee"],
|
|
},
|
|
]
|
|
|
|
return (
|
|
<div className="max-w-md mx-auto pt-2 space-y-6 animate-fade-up">
|
|
<div className="text-center space-y-2">
|
|
<h1 className="text-2xl font-black text-gray-900 tracking-tight">
|
|
How would you like to pay?
|
|
</h1>
|
|
<p className="text-base text-muted-foreground">
|
|
Your pledge: <span className="font-bold text-foreground">£{pounds}</span>
|
|
<span className="text-success-green text-xs ml-1">(£{giftAidTotal} with Gift Aid)</span>
|
|
</p>
|
|
</div>
|
|
|
|
<div className="space-y-3 stagger-children">
|
|
{options.map((opt) => (
|
|
<button
|
|
key={opt.id}
|
|
onClick={() => onSelect(opt.id)}
|
|
className={`
|
|
w-full text-left rounded-lg border-2 bg-white p-5 transition-all duration-200 group
|
|
${opt.highlight
|
|
? "border-success-green/40 hover:border-success-green hover:border-fulfilled-green"
|
|
: "border-gray-200 hover:border-trust-blue/40 hover:border-promise-blue"
|
|
}
|
|
`}
|
|
>
|
|
<div className="flex items-start gap-4">
|
|
<div className={`rounded-lg ${opt.iconBg} p-3 transition-transform`}>
|
|
<opt.icon className="h-5 w-5 text-white" />
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center gap-2 flex-wrap">
|
|
<span className="font-bold text-gray-900">{opt.title}</span>
|
|
<span className={`text-[10px] font-bold px-2 py-0.5 rounded-full ${opt.tagClass}`}>
|
|
{opt.tag}
|
|
</span>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground mt-0.5">{opt.subtitle}</p>
|
|
<div className="flex items-center gap-3 mt-2">
|
|
{opt.benefits.map((b, i) => (
|
|
<span key={i} className="text-[11px] text-muted-foreground inline-flex items-center gap-1">
|
|
<CheckCircle2 className="h-3 w-3 text-success-green" />
|
|
{b}
|
|
</span>
|
|
))}
|
|
</div>
|
|
<div className="flex items-center justify-between mt-2 pt-2 border-t border-gray-100">
|
|
<span className={`text-xs ${opt.feeClass}`}>Fee: {opt.fee}</span>
|
|
<span className="text-xs text-trust-blue font-medium opacity-0 group-hover:opacity-100 transition-opacity">
|
|
Select →
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex items-center justify-center gap-2 text-xs text-muted-foreground animate-fade-in" style={{ animationDelay: "300ms" }}>
|
|
<Shield className="h-3.5 w-3.5" />
|
|
<span>All payments are encrypted and secure</span>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|