{"version":3,"file":"paypal.mjs","names":[],"sources":["../../src/social-providers/paypal.ts"],"sourcesContent":["import { base64 } from \"@better-auth/utils/base64\";\nimport { betterFetch } from \"@better-fetch/fetch\";\nimport { decodeJwt } from \"jose\";\nimport { logger } from \"../env\";\nimport { BetterAuthError } from \"../error\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport { createAuthorizationURL } from \"../oauth2\";\n\nexport interface PayPalProfile {\n\tuser_id: string;\n\tname: string;\n\tgiven_name: string;\n\tfamily_name: string;\n\tmiddle_name?: string | undefined;\n\tpicture?: string | undefined;\n\temail: string;\n\temail_verified: boolean;\n\tgender?: string | undefined;\n\tbirthdate?: string | undefined;\n\tzoneinfo?: string | undefined;\n\tlocale?: string | undefined;\n\tphone_number?: string | undefined;\n\taddress?:\n\t\t| {\n\t\t\t\tstreet_address?: string;\n\t\t\t\tlocality?: string;\n\t\t\t\tregion?: string;\n\t\t\t\tpostal_code?: string;\n\t\t\t\tcountry?: string;\n\t\t }\n\t\t| undefined;\n\tverified_account?: boolean | undefined;\n\taccount_type?: string | undefined;\n\tage_range?: string | undefined;\n\tpayer_id?: string | undefined;\n}\n\nexport interface PayPalTokenResponse {\n\tscope?: string | undefined;\n\taccess_token: string;\n\trefresh_token?: string | undefined;\n\ttoken_type: \"Bearer\";\n\tid_token?: string | undefined;\n\texpires_in: number;\n\tnonce?: string | undefined;\n}\n\nexport interface PayPalOptions extends ProviderOptions {\n\tclientId: string;\n\t/**\n\t * PayPal environment - 'sandbox' for testing, 'live' for production\n\t * @default 'sandbox'\n\t */\n\tenvironment?: (\"sandbox\" | \"live\") | undefined;\n\t/**\n\t * Whether to request shipping address information\n\t * @default false\n\t */\n\trequestShippingAddress?: boolean | undefined;\n}\n\nexport const paypal = (options: PayPalOptions) => {\n\tconst environment = options.environment || \"sandbox\";\n\tconst isSandbox = environment === \"sandbox\";\n\n\tconst authorizationEndpoint = isSandbox\n\t\t? \"https://www.sandbox.paypal.com/signin/authorize\"\n\t\t: \"https://www.paypal.com/signin/authorize\";\n\n\tconst tokenEndpoint = isSandbox\n\t\t? \"https://api-m.sandbox.paypal.com/v1/oauth2/token\"\n\t\t: \"https://api-m.paypal.com/v1/oauth2/token\";\n\n\tconst userInfoEndpoint = isSandbox\n\t\t? \"https://api-m.sandbox.paypal.com/v1/identity/oauth2/userinfo\"\n\t\t: \"https://api-m.paypal.com/v1/identity/oauth2/userinfo\";\n\n\treturn {\n\t\tid: \"paypal\",\n\t\tname: \"PayPal\",\n\t\tasync createAuthorizationURL({ state, codeVerifier, redirectURI }) {\n\t\t\tif (!options.clientId || !options.clientSecret) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t\"Client Id and Client Secret is required for PayPal. Make sure to provide them in the options.\",\n\t\t\t\t);\n\t\t\t\tthrow new BetterAuthError(\"CLIENT_ID_AND_SECRET_REQUIRED\");\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Log in with PayPal doesn't use traditional OAuth2 scopes\n\t\t\t * Instead, permissions are configured in the PayPal Developer Dashboard\n\t\t\t * We don't pass any scopes to avoid \"invalid scope\" errors\n\t\t\t **/\n\n\t\t\tconst _scopes: string[] = [];\n\n\t\t\tconst url = await createAuthorizationURL({\n\t\t\t\tid: \"paypal\",\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint,\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate,\n\t\t\t\tcodeVerifier,\n\t\t\t\tredirectURI,\n\t\t\t\tprompt: options.prompt,\n\t\t\t});\n\t\t\treturn url;\n\t\t},\n\n\t\tvalidateAuthorizationCode: async ({ code, redirectURI }) => {\n\t\t\t/**\n\t\t\t * PayPal requires Basic Auth for token exchange\n\t\t\t **/\n\n\t\t\tconst credentials = base64.encode(\n\t\t\t\t`${options.clientId}:${options.clientSecret}`,\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tconst response = await betterFetch(tokenEndpoint, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Basic ${credentials}`,\n\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\"Accept-Language\": \"en_US\",\n\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: new URLSearchParams({\n\t\t\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t\t\t\tcode: code,\n\t\t\t\t\t\tredirect_uri: redirectURI,\n\t\t\t\t\t}).toString(),\n\t\t\t\t});\n\n\t\t\t\tif (!response.data) {\n\t\t\t\t\tthrow new BetterAuthError(\"FAILED_TO_GET_ACCESS_TOKEN\");\n\t\t\t\t}\n\n\t\t\t\tconst data = response.data as PayPalTokenResponse;\n\n\t\t\t\tconst result = {\n\t\t\t\t\taccessToken: data.access_token,\n\t\t\t\t\trefreshToken: data.refresh_token,\n\t\t\t\t\taccessTokenExpiresAt: data.expires_in\n\t\t\t\t\t\t? new Date(Date.now() + data.expires_in * 1000)\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tidToken: data.id_token,\n\t\t\t\t};\n\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\"PayPal token exchange failed:\", error);\n\t\t\t\tthrow new BetterAuthError(\"FAILED_TO_GET_ACCESS_TOKEN\");\n\t\t\t}\n\t\t},\n\n\t\trefreshAccessToken: options.refreshAccessToken\n\t\t\t? options.refreshAccessToken\n\t\t\t: async (refreshToken) => {\n\t\t\t\t\tconst credentials = base64.encode(\n\t\t\t\t\t\t`${options.clientId}:${options.clientSecret}`,\n\t\t\t\t\t);\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst response = await betterFetch(tokenEndpoint, {\n\t\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\tAuthorization: `Basic ${credentials}`,\n\t\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\t\t\"Accept-Language\": \"en_US\",\n\t\t\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tbody: new URLSearchParams({\n\t\t\t\t\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\t\t\t\t\trefresh_token: refreshToken,\n\t\t\t\t\t\t\t}).toString(),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (!response.data) {\n\t\t\t\t\t\t\tthrow new BetterAuthError(\"FAILED_TO_REFRESH_ACCESS_TOKEN\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst data = response.data as any;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\taccessToken: data.access_token,\n\t\t\t\t\t\t\trefreshToken: data.refresh_token,\n\t\t\t\t\t\t\taccessTokenExpiresAt: data.expires_in\n\t\t\t\t\t\t\t\t? new Date(Date.now() + data.expires_in * 1000)\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tlogger.error(\"PayPal token refresh failed:\", error);\n\t\t\t\t\t\tthrow new BetterAuthError(\"FAILED_TO_REFRESH_ACCESS_TOKEN\");\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\tasync verifyIdToken(token, nonce) {\n\t\t\tif (options.disableIdTokenSignIn) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (options.verifyIdToken) {\n\t\t\t\treturn options.verifyIdToken(token, nonce);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst payload = decodeJwt(token);\n\t\t\t\treturn !!payload.sub;\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\"Failed to verify PayPal ID token:\", error);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\n\t\tasync getUserInfo(token) {\n\t\t\tif (options.getUserInfo) {\n\t\t\t\treturn options.getUserInfo(token);\n\t\t\t}\n\n\t\t\tif (!token.accessToken) {\n\t\t\t\tlogger.error(\"Access token is required to fetch PayPal user info\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst response = await betterFetch(\n\t\t\t\t\t`${userInfoEndpoint}?schema=paypalv1.1`,\n\t\t\t\t\t{\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\tAuthorization: `Bearer ${token.accessToken}`,\n\t\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tif (!response.data) {\n\t\t\t\t\tlogger.error(\"Failed to fetch user info from PayPal\");\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tconst userInfo = response.data;\n\t\t\t\tconst userMap = await options.mapProfileToUser?.(userInfo);\n\n\t\t\t\tconst result = {\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: userInfo.user_id,\n\t\t\t\t\t\tname: userInfo.name,\n\t\t\t\t\t\temail: userInfo.email,\n\t\t\t\t\t\timage: userInfo.picture,\n\t\t\t\t\t\temailVerified: userInfo.email_verified,\n\t\t\t\t\t\t...userMap,\n\t\t\t\t\t},\n\t\t\t\t\tdata: userInfo,\n\t\t\t\t};\n\n\t\t\t\treturn result;\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\"Failed to fetch user info from PayPal:\", error);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\n\t\toptions,\n\t} satisfies OAuthProvider;\n};\n"],"mappings":"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"}