The weeb for the next gen discord boat - Wamellow wamellow.com
bot discord

fix: billing payment method rendering

shi.gg ef236a1d efda0038

verified
+33 -19
+20 -12
app/profile/billing/page.tsx
··· 20 20 import { useRef, useState } from "react"; 21 21 import { GrAmex } from "react-icons/gr"; 22 22 import { HiCreditCard, HiLightningBolt } from "react-icons/hi"; 23 - import { SiDinersclub, SiDiscover, SiJcb, SiMastercard, SiStripe, SiVisa } from "react-icons/si"; 23 + import { SiDinersclub, SiDiscover, SiJcb, SiMastercard, SiPaypal, SiStripe, SiVisa } from "react-icons/si"; 24 24 25 25 function isActive(status: ApiV1UsersMeBillingGetResponse["status"]): status is "active" | "trialing" | "past_due" { 26 26 return status === "active" || status === "trialing" || status === "past_due"; ··· 31 31 const [changeDonationModalOpen, setChangeDonationModalOpen] = useState(false); 32 32 33 33 const { data, isLoading, error, edit } = useApi<ApiV1UsersMeBillingGetResponse>("/users/@me/billing"); 34 + const [nowInSeconds] = useState(() => Date.now() / 1_000); 34 35 35 36 if ((isLoading && !user?.premium) || (!isLoading && !data) || (data && !isActive(data.status))) { 36 37 return (<> ··· 56 57 </>); 57 58 } 58 59 59 - const periodEndsInDays = Math.floor((((data?.currentPeriodEnd || 0) - Date.now() / 1_000) / (60 * 60 * 24))); 60 + const periodEndsInDays = Math.floor(((data?.currentPeriodEnd || 0) - nowInSeconds) / (60 * 60 * 24)); 60 61 const periodEndsInStr = `${periodEndsInDays > 1 ? "in " : ""}${periodEndsInDays === 0 ? "Today" : periodEndsInDays === 1 ? "Tomorrow" : periodEndsInDays} ${periodEndsInDays > 1 ? "days" : ""}`; 61 62 62 63 return ( ··· 126 127 <h2 className="font-semibold text-xl text-neutral-300 mb-2 lg:mb-0 lg:relative lg:bottom-2">Payment Method</h2> 127 128 {isLoading 128 129 ? <Skeleton className="h-12 w-full" /> 129 - : 130 - <div className="flex gap-2 items-center bg-wamellow-100 px-4 py-1 rounded-lg"> 130 + : <div className="flex gap-2 items-center bg-wamellow-100 px-4 py-1 rounded-lg"> 131 131 <PaymentMethodIcon method={data!.paymentMethod} /> 132 - {typeof data?.paymentMethod === "string" ? data?.paymentMethod : "**** **** **** " + data?.paymentMethod?.last4} 132 + {getPaymentMethodInfo(data!.paymentMethod)} 133 + 133 134 <Button 134 135 asChild 135 136 className="ml-auto" ··· 184 185 return "subscriptions/" + data.subscriptionId + "/cancel"; 185 186 } 186 187 187 - function PaymentMethodIcon({ method }: { method: ApiV1UsersMeBillingGetResponse["paymentMethod"]; }) { 188 - if (typeof method === "string") { 189 - return <HiCreditCard className="size-6" />; 190 - } 188 + function PaymentMethodIcon({ method }: { method?: ApiV1UsersMeBillingGetResponse["paymentMethod"]; }) { 189 + if (!method) return <HiCreditCard className="size-6" />; 191 190 192 - switch (method?.brand) { 191 + switch (method.brand) { 192 + case "paypal": return <SiPaypal className="size-6" />; 193 193 case "amex": return <GrAmex className="size-6" />; 194 194 case "diners": return <SiDinersclub className="size-6" />; 195 195 case "discover": return <SiDiscover className="size-6" />; 196 196 case "jcb": return <SiJcb className="size-6" />; 197 197 case "link": return <SiStripe className="size-6" />; 198 198 case "mastercard": return <SiMastercard className="size-6" />; 199 - case "visa": return <SiVisa className="size-6"/>; 199 + case "visa": return <SiVisa className="size-6" />; 200 + default: return <HiCreditCard className="size-6" />; 200 201 } 202 + } 201 203 202 - return <HiCreditCard className="size-6" />; 204 + function getPaymentMethodInfo(method?: ApiV1UsersMeBillingGetResponse["paymentMethod"]) { 205 + if (!method) return "Unknown"; 206 + 207 + if ("email" in method) return method.email ?? "PayPal"; 208 + if ("last4" in method) return method.last4 ? `•••• •••• •••• ${method.last4}` : "Card"; 209 + 210 + return "Unknown"; 203 211 } 204 212 205 213 function PremiumGuildSelect({
+13 -7
typings.ts
··· 330 330 type: ConnectionType; 331 331 } 332 332 333 + interface PaymentMethodCard { 334 + brand: string | null; 335 + last4: string | null; 336 + } 337 + 338 + interface PaymentMethodPaypal { 339 + brand: "paypal"; 340 + email: string | null; 341 + } 342 + 343 + type PaymentMethod = PaymentMethodCard | PaymentMethodPaypal; 344 + 333 345 export interface ApiV1UsersMeBillingGetResponse { 334 346 subscriptionId: string; 335 347 status: ··· 347 359 currentPeriodStart: number; 348 360 cancelAtPeriodEnd: boolean; 349 361 donationQuantity: number; 350 - paymentMethod: 351 - | { 352 - brand: string | null; 353 - last4: string | null; 354 - } 355 - | string 356 - | null; 362 + paymentMethod: PaymentMethod | null; 357 363 portalUrl: string; 358 364 guildIds: string[]; 359 365 }