import "server-only"; const ACCOUNTS_BASE_URL = process.env.GOOYAL_ACCOUNTS_BASE_URL!; const CLIENT_ID = process.env.GOOYAL_CLIENT_ID!; const CLIENT_SECRET = process.env.GOOYAL_CLIENT_SECRET!; const SCOPE = process.env.GOOYAL_OAUTH_SCOPE!; function basicAuthHeader() { return "Basic " + Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64"); } const ERROR_MESSAGES_FA: Record = { invalid_grant: "کد تایید نامعتبر یا منقضی شده است.", invalid_client: "خطا در پیکربندی سرویس احراز هویت. لطفاً بعداً تلاش کنید.", invalid_request: "درخواست نامعتبر است.", unsupported_grant_type: "خطا در پیکربندی سرویس احراز هویت.", }; export class GooyalAuthError extends Error { constructor( public status: number, public code: string, description?: string, ) { super(ERROR_MESSAGES_FA[code] ?? description ?? code); } } export interface RequestOtpResult { phone_number: string; otp_expire: string; ttl: number; } export async function requestOtp(phoneNumber: string): Promise { const res = await fetch(`${ACCOUNTS_BASE_URL}/users/api/request_otp/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ phone_number: phoneNumber }), cache: "no-store", }); const data = await res.json().catch(() => ({})); if (!res.ok) { throw new GooyalAuthError(res.status, data.error ?? "request_otp_failed", data.error_description ?? data.detail); } return data; } export interface GooyalTokenResponse { access_token: string; refresh_token?: string; token_type: string; expires_in: number; scope: string; } export async function exchangeOtpForToken(phoneNumber: string, otp: string): Promise { const body = new URLSearchParams({ grant_type: "password", username: phoneNumber, password: otp, scope: SCOPE, auth_fields: "phone_number:otp", }); const res = await fetch(`${ACCOUNTS_BASE_URL}/oauth2/token/`, { method: "POST", headers: { Authorization: basicAuthHeader(), "Content-Type": "application/x-www-form-urlencoded", }, body: body.toString(), cache: "no-store", }); const data = await res.json().catch(() => ({})); if (!res.ok) { throw new GooyalAuthError(res.status, data.error ?? "token_exchange_failed", data.error_description); } return data; } export async function refreshAccessToken(refreshToken: string): Promise { const body = new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, }); const res = await fetch(`${ACCOUNTS_BASE_URL}/oauth2/token/`, { method: "POST", headers: { Authorization: basicAuthHeader(), "Content-Type": "application/x-www-form-urlencoded", }, body: body.toString(), cache: "no-store", }); const data = await res.json().catch(() => ({})); if (!res.ok) { throw new GooyalAuthError(res.status, data.error ?? "refresh_failed", data.error_description); } return data; }