Heim >Web-Frontend >js-Tutorial >Erstellen eines automatischen Währungsumschalters in Next.js

Erstellen eines automatischen Währungsumschalters in Next.js

Mary-Kate Olsen
Mary-Kate OlsenOriginal
2025-01-03 08:33:43444Durchsuche

Voraussetzungen
Bevor Sie beginnen, stellen Sie sicher, dass Sie über grundlegende Kenntnisse von Next.js und React verfügen.

1. Erstellen der Backend-API-Route

Wir erstellen eine Next.js-API-Route, die mit unserer Geolocation-API interagiert.
Erstellen Sie eine neue Datei unter: 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. Erhalten Ihres API-Schlüssels

Wir werden einen kostenlosen Geolokalisierungsdienst namens IP Flare verwenden. Besuchen Sie die Seite „API-Schlüssel“: Navigieren Sie zur Seite „API-Schlüssel“.

Besuchen Sie: www.ipflare.io

Auf der Seite „API-Schlüssel“ können wir unseren API-Schlüssel abrufen und die Schnellkopie verwenden, um ihn als Umgebungsvariable in unserer .env-Datei zu speichern. Wir werden dies zur Authentifizierung unserer Anfragen verwenden.
Building an Automatic Currency Switcher in Next.js

3. Erstellen der Frontend-Komponente

Ich habe diese All-in-One-Komponente erstellt, die den Anbieter und die Währungsauswahl umfasst. Ich verwende shadcn/ui und einige Flaggen-SVGs, die ich online gefunden habe.

Sie müssen die Anwendung in den einschließen. damit wir auf den Kontext zugreifen können.

Jetzt können wir überall in der Anwendung, wo wir auf die Währung zugreifen möchten, den Hook const { payment } = useCurrency(); verwenden.

Um dies mit Stripe zu integrieren, müssen Sie beim Erstellen des Bezahlvorgangs lediglich die Währung senden und sicherstellen, dass Sie Ihren Stripe-Produkten Preise in mehreren Währungen hinzugefügt haben.

"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;
};

Das obige ist der detaillierte Inhalt vonErstellen eines automatischen Währungsumschalters in Next.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn