在 Next.js 中构建自动货币切换器
先决条件
在开始之前,请确保您对 Next.js 和 React 有基本的了解。
1. 创建后端API路由
我们将创建一个与我们的 Geolocation API 交互的 Next.js API 路由。
在以下位置创建一个新文件:src/app/api/geolocation/route.ts
import { NextResponse } from "next/server"; import axios from "axios"; type IPGeolocation = { ip: string; version?: string; city?: string; region?: string; region_code?: string; country_code?: string; country_code_iso3?: string; country_fifa_code?: string; country_fips_code?: string; country_name?: string; country_capital?: string; country_tld?: string; country_emoji?: string; continent_code?: string; in_eu: boolean; land_locked: boolean; postal?: string; latitude?: number; longitude?: number; timezone?: string; utc_offset?: string; country_calling_code?: string; currency?: string; currency_name?: string; languages?: string; country_area?: number; asn?: string; // Append ?fields=asn to the URL isp?: string; // Append ?fields=isp to the URL } type IPGeolocationError = { code: string; error: string; } export async function GET() { // Retrieve IP address using the getClientIp function // For testing purposes, we'll use a fixed IP address // const clientIp = getClientIp(req.headers); const clientIp = "84.17.50.173"; if (!clientIp) { return NextResponse.json( { error: "Unable to determine IP address" }, { status: 400 } ); } const key = process.env.IPFLARE_API_KEY; if (!key) { return NextResponse.json( { error: "IPFlare API key is not set" }, { status: 500 } ); } try { const response = await axios.get<IPGeolocation | IPGeolocationError>( `https://api.ipflare.io/${clientIp}`, { headers: { "X-API-Key": key, }, } ); if ("error" in response.data) { return NextResponse.json({ error: response.data.error }, { status: 400 }); } return NextResponse.json(response.data); } catch { return NextResponse.json( { error: "Internal Server Error" }, { status: 500 } ); } }
2. 获取您的API密钥
我们将使用名为 IP Flare 的免费地理定位服务。访问 API 密钥页面:导航至 API 密钥页面。
访问:www.ipflare.io
从 API 密钥页面我们可以获得 API 密钥,并且可以使用快速复制将其作为环境变量存储在 .env 文件中。我们将用它来验证我们的请求。
3. 创建前端组件
我创建了这个一体化组件,其中包括提供程序和货币选择器。我正在使用 shadcn/ui 和一些我在网上找到的标志 SVG。
您需要将应用程序包装在
现在,在应用程序中我们想要访问货币的任何地方,我们都可以使用钩子 const {currency } = useCurrency();。
要将其与 Stripe 集成,当您创建结帐时,您只需发送货币并确保您已将多货币定价添加到您的 Stripe 产品中。
"use client"; import { useRouter } from "next/navigation"; import { createContext, type FC, type ReactNode, useContext, useEffect, useMemo, useState, } from "react"; import axios from "axios"; // 1) Import axios import { Flag } from "~/components/flag"; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from "~/components/ui/select"; import { cn } from "~/lib/utils"; import { type Currency } from "~/server/schemas/currency"; // -- [1] Create a local type for the data returned by /api/geolocation. type GeolocationData = { country_code?: string; continent_code?: string; currency?: string; }; type CurrencyContext = { currency: Currency; setCurrency: (currency: Currency) => void; }; const CurrencyContext = createContext<CurrencyContext | null>(null); export function useCurrency() { const context = useContext(CurrencyContext); if (!context) { throw new Error("useCurrency must be used within a CurrencyProvider."); } return context; } export const CurrencyProvider: FC<{ children: ReactNode }> = ({ children }) => { const router = useRouter(); // -- [2] Local state for geolocation data const [location, setLocation] = useState<GeolocationData | null>(null); const [isLoading, setIsLoading] = useState<boolean>(true); // -- [3] Fetch location once when the component mounts useEffect(() => { const fetchLocation = async () => { setIsLoading(true); try { const response = await axios.get("/api/geolocation"); setLocation(response.data); } catch (error) { console.error(error); } finally { setIsLoading(false); } }; void fetchLocation(); }, []); // -- [4] Extract currency from location if present (fallback to "usd") const geoCurrency = location?.currency; const getInitialCurrency = (): Currency => { if (typeof window !== "undefined") { const cookie = document.cookie .split("; ") .find((row) => row.startsWith("currency=")); if (cookie) { const value = cookie.split("=")[1]; if (value === "usd" || value === "eur" || value === "gbp") { return value; } } } return "usd"; }; const [currency, setCurrencyState] = useState<Currency>(getInitialCurrency); useEffect(() => { if (!isLoading && geoCurrency !== undefined) { const validatedCurrency = validateCurrency(geoCurrency, location); if (validatedCurrency) { setCurrency(validatedCurrency); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isLoading, location, geoCurrency]); // -- [5] Update currency & store cookie; no more tRPC invalidation const setCurrency = (newCurrency: Currency) => { setCurrencyState(newCurrency); if (typeof window !== "undefined") { document.cookie = `currency=${newCurrency}; path=/; max-age=${ 60 * 60 * 24 * 365 }`; // Expires in 1 year } // Removed tRPC invalidate since we are no longer using tRPC router.refresh(); }; const contextValue = useMemo<CurrencyContext>( () => ({ currency, setCurrency, }), [currency], ); return ( <CurrencyContext.Provider value={contextValue}> {children} </CurrencyContext.Provider> ); }; export const CurrencySelect = ({ className }: { className?: string }) => { const { currency, setCurrency } = useCurrency(); return ( <Select value={currency} onValueChange={setCurrency}> <SelectTrigger className={cn("w-[250px]", className)}> <SelectValue placeholder="Select a currency" /> </SelectTrigger> <SelectContent> <SelectGroup className="text-sm"> <SelectItem value="usd"> <div className="flex items-center gap-3"> <Flag code="US" className="h-4 w-4 rounded" /> <span>$ USD</span> </div> </SelectItem> <SelectItem value="eur"> <div className="flex items-center gap-3"> <Flag code="EU" className="h-4 w-4 rounded" /> <span>€ EUR</span> </div> </SelectItem> <SelectItem value="gbp"> <div className="flex items-center gap-3"> <Flag code="GB" className="h-4 w-4 rounded" /> <span>£ GBP</span> </div> </SelectItem> </SelectGroup> </SelectContent> </Select> ); }; // -- [6] Use our new GeolocationData type in place of RouterOutputs const validateCurrency = ( currency: string, location?: GeolocationData | null, ): Currency | null => { if (currency === "usd" || currency === "eur" || currency === "gbp") { return currency; } if (!location) { return null; } if (location.country_code === "GB") { return "gbp"; } // Check if they are in the EU if (location.continent_code === "EU") { return "eur"; } // North America if (location.continent_code === "NA") { return "usd"; } return null; };
以上是在 Next.js 中构建自动货币切换器的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

Python和JavaScript开发者的薪资没有绝对的高低,具体取决于技能和行业需求。1.Python在数据科学和机器学习领域可能薪资更高。2.JavaScript在前端和全栈开发中需求大,薪资也可观。3.影响因素包括经验、地理位置、公司规模和特定技能。

学习JavaScript不难,但有挑战。1)理解基础概念如变量、数据类型、函数等。2)掌握异步编程,通过事件循环实现。3)使用DOM操作和Promise处理异步请求。4)避免常见错误,使用调试技巧。5)优化性能,遵循最佳实践。

实现视差滚动和元素动画效果的探讨本文将探讨如何实现类似资生堂官网(https://www.shiseido.co.jp/sb/wonderland/)中�...

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

如何在JavaScript中将具有相同ID的数组元素合并到一个对象中?在处理数据时,我们常常会遇到需要将具有相同ID�...

zustand异步操作中的数据更新问题在使用zustand状态管理库时,经常会遇到异步操作导致数据更新不及时的问题。�...
