Rumah >hujung hadapan web >tutorial js >Membina Penukar Mata Wang Automatik dalam Next.js
Prasyarat
Sebelum anda bermula, pastikan anda mempunyai pemahaman asas tentang Next.js dan React.
Kami akan mencipta laluan API Next.js yang berinteraksi dengan API Geolokasi kami.
Cipta fail baharu di: 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 } ); } }
Kami akan menggunakan perkhidmatan geolokasi percuma yang dipanggil IP Flare. Lawati Halaman Kunci API: Navigasi ke halaman Kunci API.
Lawati: www.ipflare.io
Dari halaman Kunci API kami boleh mendapatkan kunci API kami dan kami boleh menggunakan salinan pantas untuk menyimpannya sebagai pembolehubah persekitaran dalam fail .env kami. Kami akan menggunakan ini untuk mengesahkan permintaan kami.
Saya telah mencipta komponen semua-dalam-satu ini yang merangkumi pembekal dan pemilih mata wang. Saya menggunakan shadcn/ui dan beberapa SVG bendera yang saya temui dalam talian.
Anda perlu membungkus aplikasi dalam
Sekarang, di mana-mana dalam aplikasi di mana kita ingin mengakses mata wang, kita boleh menggunakan cangkuk const { currency } = useCurrency();.
Untuk menyepadukan ini dengan Stripe, apabila anda membuat pembayaran, anda hanya perlu menghantar mata wang dan memastikan bahawa anda telah menambahkan harga berbilang mata wang pada produk Stripe anda.
"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; };
Atas ialah kandungan terperinci Membina Penukar Mata Wang Automatik dalam Next.js. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!