Что нового
  • Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

Building an Automatic Currency Switcher in Next.js

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
6,535
Баллы
155
Prerequisites
Before you begin, ensure you have abasic understanding of Next.js and React.

1. Creating the Backend API Route


We'll create a Next.js API route that interacts with our Geolocation API.
Create a new file at: 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>(
`

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

{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. Obtaining Your API Key


We are going to use a free geolocation service called

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

. Visit the API Keys Page: Navigate to the

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

page.

Visit:

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.



From the API Keys page we can get our API key and we can use the quick copy to store it as an environment variable in our .env file. We will use this to authenticate our requests.

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.



3. Creating the Frontend Component


I have created this all-in-one component that includes the provider and the currency selector. I am using

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

and some flag SVGs I found online.

You will need to wrap the application in the <CurrencyProvider /> so that we can access the context.

Now, anywhere in the application where we want to access the currency, we can use the hook const { currency } = useCurrency();.

To integrate this with Stripe, when you create the checkout you just need to send the currency and ensure that you have added multi-currency pricing to your Stripe products.


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


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх