ホームページ ウェブフロントエンド jsチュートリアル Next.js で自動通貨スイッチャーを構築する

Next.js で自動通貨スイッチャーを構築する

Jan 03, 2025 am 08:33 AM

前提条件
始める前に、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 ファイルに保存できます。これをリクエストの認証に使用します。
Building an Automatic Currency Switcher in Next.js

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 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

JavaScriptの文字列文字を交換します JavaScriptの文字列文字を交換します Mar 11, 2025 am 12:07 AM

JavaScriptの文字列文字を交換します

jQuery日付が有効かどうかを確認します jQuery日付が有効かどうかを確認します Mar 01, 2025 am 08:51 AM

jQuery日付が有効かどうかを確認します

jQueryは要素のパディング/マージンを取得します jQueryは要素のパディング/マージンを取得します Mar 01, 2025 am 08:53 AM

jQueryは要素のパディング/マージンを取得します

10 jQuery Accordionsタブ 10 jQuery Accordionsタブ Mar 01, 2025 am 01:34 AM

10 jQuery Accordionsタブ

10 jqueryプラグインをチェックする価値があります 10 jqueryプラグインをチェックする価値があります Mar 01, 2025 am 01:29 AM

10 jqueryプラグインをチェックする価値があります

ノードとHTTPコンソールを使用したHTTPデバッグ ノードとHTTPコンソールを使用したHTTPデバッグ Mar 01, 2025 am 01:37 AM

ノードとHTTPコンソールを使用したHTTPデバッグ

カスタムGoogle検索APIセットアップチュートリアル カスタムGoogle検索APIセットアップチュートリアル Mar 04, 2025 am 01:06 AM

カスタムGoogle検索APIセットアップチュートリアル

jQueryはscrollbarをdivに追加します jQueryはscrollbarをdivに追加します Mar 01, 2025 am 01:30 AM

jQueryはscrollbarをdivに追加します

See all articles