[web] Make payments feel at home (#1231)
Make the imported payments code feel at home in the new monorepo.
This commit is contained in:
commit
9599982f30
40 changed files with 227 additions and 3912 deletions
2
web/.gitignore
vendored
2
web/.gitignore
vendored
|
@ -9,7 +9,7 @@ node_modules/
|
|||
|
||||
# Local env files
|
||||
.env
|
||||
.env.*.local
|
||||
.env*.local
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"presets": ["next/babel"],
|
||||
"plugins": [
|
||||
[
|
||||
"styled-components",
|
||||
{
|
||||
"ssr": true,
|
||||
"displayName": true,
|
||||
"preprocess": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": ["next", "next/core-web-vitals"]
|
||||
}
|
8
web/apps/payments/.eslintrc.js
Normal file
8
web/apps/payments/.eslintrc.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
module.exports = {
|
||||
extends: ["@/build-config/eslintrc-typescript-react"],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
// TODO (MR): Figure out a way to not have to ignored the next config .js
|
||||
ignorePatterns: [".eslintrc.js", "next.config.js"],
|
||||
};
|
|
@ -1,3 +0,0 @@
|
|||
## Description
|
||||
|
||||
## Test Plan
|
34
web/apps/payments/.gitignore
vendored
34
web/apps/payments/.gitignore
vendored
|
@ -1,34 +0,0 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "es5",
|
||||
"singleQuote": true,
|
||||
"jsxBracketSameLine": true
|
||||
}
|
|
@ -1,17 +1,7 @@
|
|||
This is a [Next.js](https://nextjs.org/) project bootstrapped with
|
||||
[`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
Code that runs on `payments.ente.io`. It brokers between our services and
|
||||
Stripe's API for payments.
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## Notes
|
||||
## Development
|
||||
|
||||
If you're running this to test out the payment flows end-to-end, please do a
|
||||
`yarn build`, that will place the output within the `out` folder.
|
||||
|
@ -23,12 +13,12 @@ Aside that, these are the necessary configuration changes.
|
|||
|
||||
### Local configuration
|
||||
|
||||
Update the `.env.local` to point to the local museum instance, and to define the
|
||||
necessary Stripe keys that can be fetched from [Stripe's developer
|
||||
Create an `.env` in this directory to point to the local museum instance, and to
|
||||
define the necessary Stripe keys that can be fetched from [Stripe's developer
|
||||
dashboard](https://dashboard.stripe.com).
|
||||
|
||||
Assuming that your local museum instance is running on `192.168.1.2:8080`, your
|
||||
`.env.local` should look as follows.
|
||||
`.env` should look as follows.
|
||||
|
||||
```
|
||||
NEXT_PUBLIC_ENTE_ENDPOINT = http://192.168.1.2:8080
|
||||
|
@ -42,11 +32,12 @@ NEXT_PUBLIC_STRIPE_US_PUBLISHABLE_KEY = stripe_publishable_key
|
|||
|
||||
2. Define this secret within your `musuem.yaml`
|
||||
|
||||
3. Update the `whitelisted-redirect-urls` so that it supports redirecting to this locally running project
|
||||
3. Update the `whitelisted-redirect-urls` so that it supports redirecting to
|
||||
this locally running project.
|
||||
|
||||
Assuming that your local payments app is running on `192.168.1.2:3001`, your
|
||||
`museum.yaml` should look as follows.
|
||||
|
||||
|
||||
```yaml
|
||||
stripe:
|
||||
us:
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
ente believes that working with security researchers across the globe is crucial to keeping our
|
||||
users safe. If you believe you've found a security issue in our product or service, we encourage you to
|
||||
notify us (security@ente.io). We welcome working with you to resolve the issue promptly. Thanks in advance!
|
||||
|
||||
# Disclosure Policy
|
||||
|
||||
- Let us know as soon as possible upon discovery of a potential security issue, and we'll make every
|
||||
effort to quickly resolve the issue.
|
||||
- Provide us a reasonable amount of time to resolve the issue before any disclosure to the public or a
|
||||
third-party. We may publicly disclose the issue before resolving it, if appropriate.
|
||||
- Make a good faith effort to avoid privacy violations, destruction of data, and interruption or
|
||||
degradation of our service. Only interact with accounts you own or with explicit permission of the
|
||||
account holder.
|
||||
- If you would like to encrypt your report, please use the PGP key with long ID
|
||||
`E273695C0403F34F74171932DF6DDDE98EBD2394` (available in the public keyserver pool).
|
||||
|
||||
# In-scope
|
||||
|
||||
- Security issues in any current release of ente. This includes the web app, desktop app,
|
||||
and mobile apps (iOS and Android). Product downloads are available at https://ente.io. Source
|
||||
code is available at https://github.com/ente-io.
|
||||
|
||||
# Exclusions
|
||||
|
||||
The following bug classes are out-of scope:
|
||||
|
||||
- Bugs that are already reported on any of ente's issue trackers (https://github.com/ente-io),
|
||||
or that we already know of. Note that some of our issue tracking is private.
|
||||
- Issues in an upstream software dependency (ex: Flutter, Next.js etc) which are already reported to the upstream maintainer.
|
||||
- Attacks requiring physical access to a user's device.
|
||||
- Self-XSS
|
||||
- Issues related to software or protocols not under ente's control
|
||||
- Vulnerabilities in outdated versions of ente
|
||||
- Missing security best practices that do not directly lead to a vulnerability
|
||||
- Issues that do not have any impact on the general public
|
||||
|
||||
While researching, we'd like to ask you to refrain from:
|
||||
|
||||
- Denial of service
|
||||
- Spamming
|
||||
- Social engineering (including phishing) of ente staff or contractors
|
||||
- Any physical attempts against ente property or data centers
|
||||
|
||||
Thank you for helping keep ente and our users safe!
|
|
@ -1,44 +1,18 @@
|
|||
// This file sets a custom webpack configuration to use your Next.js app
|
||||
// with Sentry.
|
||||
// https://nextjs.org/docs/api-reference/next.config.js/introduction
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
// @ts-check
|
||||
|
||||
const { withSentryConfig } = require('@sentry/nextjs');
|
||||
|
||||
const cp = require('child_process');
|
||||
const gitSha = cp.execSync('git rev-parse --short HEAD', {
|
||||
cwd: __dirname,
|
||||
encoding: 'utf8',
|
||||
});
|
||||
|
||||
const moduleExports = {
|
||||
// Your existing module.exports
|
||||
output: 'export',
|
||||
/**
|
||||
* Configuration for the Next.js build
|
||||
*
|
||||
* See also:
|
||||
* - packages/next/next.config.base.js
|
||||
* - https://nextjs.org/docs/pages/api-reference/next-config-js
|
||||
*
|
||||
* @type {import("next").NextConfig}
|
||||
*/
|
||||
const nextConfig = {
|
||||
/* generate a static export when we run `next build` */
|
||||
output: "export",
|
||||
reactStrictMode: true,
|
||||
env: {
|
||||
SENTRY_RELEASE: gitSha,
|
||||
},
|
||||
sentry: {
|
||||
hideSourceMaps: false,
|
||||
},
|
||||
};
|
||||
|
||||
const SentryWebpackPluginOptions = {
|
||||
// Additional config options for the Sentry Webpack plugin. Keep in mind that
|
||||
// the following options are set automatically, and overriding them is not
|
||||
// recommended:
|
||||
// release, url, org, project, authToken, configFile, stripPrefix,
|
||||
// urlPrefix, include, ignore
|
||||
release: gitSha,
|
||||
silent: true, // Suppresses all logs
|
||||
// Ignore sentry webpack errors
|
||||
errorHandler: (err, invokeErr, compilation) => {
|
||||
compilation.warnings.push('Sentry CLI Plugin: ' + err.message);
|
||||
},
|
||||
// For all available options, see:
|
||||
// https://github.com/getsentry/sentry-webpack-plugin#options.
|
||||
};
|
||||
|
||||
// Make sure adding Sentry options is the last code to run before exporting, to
|
||||
// ensure that your source maps include changes from all other Webpack plugins
|
||||
module.exports = withSentryConfig(moduleExports, SentryWebpackPluginOptions);
|
||||
module.exports = nextConfig;
|
||||
|
|
|
@ -1,31 +1,11 @@
|
|||
{
|
||||
"name": "payments",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -p 3001",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/nextjs": "^7.54.0",
|
||||
"@stripe/stripe-js": "^1.17.0",
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "4.6.0",
|
||||
"next": "^14.1.4",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^1.6.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"styled-components": "^5.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.11.30",
|
||||
"@types/react": "17.0.15",
|
||||
"@types/styled-components": "^5.1.12",
|
||||
"babel-plugin-styled-components": "^1.13.2",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-next": "^14.1.4",
|
||||
"typescript": "^5.4.2"
|
||||
}
|
||||
"name": "payments",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@/next": "*",
|
||||
"@stripe/stripe-js": "^1.17.0",
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "4.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
// This file configures the initialization of Sentry on the browser.
|
||||
// The config you add here will be used whenever a page is visited.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
|
||||
const SENTRY_DSN =
|
||||
(process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN) ??
|
||||
'https://67447bc36684b1f7a18d79683b788f25@sentry.ente.io/6';
|
||||
|
||||
const TUNNEL_URL = 'https://sentry-reporter.ente.io';
|
||||
const SENTRY_ENV = process.env.NEXT_PUBLIC_SENTRY_ENV ?? 'development';
|
||||
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN,
|
||||
enabled: false,
|
||||
environment: SENTRY_ENV,
|
||||
// Adjust this value in production, or use tracesSampler for greater control
|
||||
tracesSampleRate: 1.0,
|
||||
attachStacktrace: true,
|
||||
autoSessionTracking: false,
|
||||
tunnel: TUNNEL_URL,
|
||||
// ...
|
||||
// Note: if you want to override the automatic release value, do not set a
|
||||
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
|
||||
// that it will also get attached to your source maps
|
||||
});
|
|
@ -1,4 +0,0 @@
|
|||
defaults.url=https://sentry.ente.io/
|
||||
defaults.org=ente
|
||||
defaults.project=web-payments
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// This file configures the initialization of Sentry on the server.
|
||||
// The config you add here will be used whenever the server handles a request.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
|
||||
const SENTRY_DSN =
|
||||
(process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN) ??
|
||||
'https://208e398c28cd4c069c83d7c6e63adef6@sentry.ente.io/6';
|
||||
|
||||
const SENTRY_ENV = process.env.NEXT_PUBLIC_SENTRY_ENV ?? 'development';
|
||||
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN,
|
||||
enabled: SENTRY_ENV !== 'development',
|
||||
environment: SENTRY_ENV,
|
||||
// Adjust this value in production, or use tracesSampler for greater control
|
||||
tracesSampleRate: 1.0,
|
||||
release: process.env.SENTRY_RELEASE,
|
||||
autoSessionTracking: false,
|
||||
|
||||
// ...
|
||||
// Note: if you want to override the automatic release value, do not set a
|
||||
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
|
||||
// that it will also get attached to your source maps
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import styled from 'styled-components';
|
||||
import styled from "@emotion/styled";
|
||||
|
||||
export const Container = styled.div`
|
||||
display: flex;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Spinner } from 'react-bootstrap';
|
||||
import { Spinner } from "react-bootstrap";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export default function EnteSpinner(props: any) {
|
||||
return (
|
||||
<Spinner {...props} animation="border" variant="success" role="status">
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
export const DESKTOP_REDIRECT_URL = 'ente://app/gallery';
|
||||
export const ENTE_WEBSITE_URL = 'https://ente.io';
|
|
@ -1,6 +1,5 @@
|
|||
import { Container } from 'components/Container';
|
||||
import React from 'react';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { Container } from "components/Container";
|
||||
import constants from "utils/strings/constants";
|
||||
|
||||
export default function Home() {
|
||||
return <Container>{constants.NOT_FOUND}</Container>;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import '../styles/globals.css';
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import type { AppProps } from 'next/app';
|
||||
import React from 'react';
|
||||
import constants from 'utils/strings/constants';
|
||||
import Head from 'next/head';
|
||||
import "bootstrap/dist/css/bootstrap.min.css";
|
||||
import type { AppProps } from "next/app";
|
||||
import Head from "next/head";
|
||||
import constants from "utils/strings/constants";
|
||||
import "../styles/globals.css";
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { Container } from 'components/Container';
|
||||
import EnteSpinner from 'components/EnteSpinner';
|
||||
import { DESKTOP_REDIRECT_URL } from 'constants/common';
|
||||
import { useRouter } from 'next/dist/client/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Container } from "components/Container";
|
||||
import EnteSpinner from "components/EnteSpinner";
|
||||
import * as React from "react";
|
||||
|
||||
export default function DesktopRedirect() {
|
||||
useEffect(() => {
|
||||
React.useEffect(() => {
|
||||
const currentURL = new URL(window.location.href);
|
||||
const desktopRedirectURL = new URL(DESKTOP_REDIRECT_URL);
|
||||
const desktopRedirectURL = new URL("ente://app/gallery");
|
||||
desktopRedirectURL.search = currentURL.search;
|
||||
window.location.href = desktopRedirectURL.href;
|
||||
}, []);
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
import { Container } from 'components/Container';
|
||||
import EnteSpinner from 'components/EnteSpinner';
|
||||
import { ENTE_WEBSITE_URL } from 'constants/common';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { parseAndHandleRequest } from 'services/billingService';
|
||||
import { CUSTOM_ERROR } from 'utils/error';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { Container } from "components/Container";
|
||||
import EnteSpinner from "components/EnteSpinner";
|
||||
import * as React from "react";
|
||||
import { parseAndHandleRequest } from "services/billingService";
|
||||
import { CUSTOM_ERROR } from "utils/error";
|
||||
import constants from "utils/strings/constants";
|
||||
|
||||
export default function Home() {
|
||||
const [errorMessageView, setErrorMessageView] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
useEffect(() => {
|
||||
const [errorMessageView, setErrorMessageView] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
React.useEffect(() => {
|
||||
async function main() {
|
||||
try {
|
||||
setLoading(true);
|
||||
await parseAndHandleRequest();
|
||||
} catch (e: any) {
|
||||
} catch (e: unknown) {
|
||||
if (
|
||||
e instanceof Error &&
|
||||
e.message === CUSTOM_ERROR.DIRECT_OPEN_WITH_NO_QUERY_PARAMS
|
||||
) {
|
||||
window.location.href = ENTE_WEBSITE_URL;
|
||||
window.location.href = "https://ente.io";
|
||||
} else {
|
||||
setErrorMessageView(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: audit
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
main();
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
// TODO: Audit
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
/* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/consistent-indexed-object-style */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import axios, { AxiosRequestConfig } from "axios";
|
||||
|
||||
interface IHTTPHeaders {
|
||||
[headerKey: string]: any;
|
||||
|
@ -29,7 +37,7 @@ class HTTPService {
|
|||
* header object to be append to all api calls.
|
||||
*/
|
||||
private headers: IHTTPHeaders = {
|
||||
'content-type': 'application/json',
|
||||
"content-type": "application/json",
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -77,7 +85,9 @@ class HTTPService {
|
|||
...config.headers,
|
||||
};
|
||||
if (customConfig?.cancel) {
|
||||
config.cancelToken=new axios.CancelToken((c)=> (customConfig.cancel.exec=c));
|
||||
config.cancelToken = new axios.CancelToken(
|
||||
(c) => (customConfig.cancel.exec = c),
|
||||
);
|
||||
}
|
||||
return await axios({ ...config, ...customConfig });
|
||||
}
|
||||
|
@ -94,7 +104,7 @@ class HTTPService {
|
|||
return this.request(
|
||||
{
|
||||
headers,
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
params,
|
||||
url,
|
||||
},
|
||||
|
@ -116,7 +126,7 @@ class HTTPService {
|
|||
{
|
||||
data,
|
||||
headers,
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
params,
|
||||
url,
|
||||
},
|
||||
|
@ -138,7 +148,7 @@ class HTTPService {
|
|||
{
|
||||
data,
|
||||
headers,
|
||||
method: 'PUT',
|
||||
method: "PUT",
|
||||
params,
|
||||
url,
|
||||
},
|
||||
|
@ -160,7 +170,7 @@ class HTTPService {
|
|||
{
|
||||
data,
|
||||
headers,
|
||||
method: 'DELETE',
|
||||
method: "DELETE",
|
||||
params,
|
||||
url,
|
||||
},
|
||||
|
|
|
@ -1,68 +1,76 @@
|
|||
import { loadStripe, Stripe } from '@stripe/stripe-js';
|
||||
import { CUSTOM_ERROR } from 'utils/error';
|
||||
import { logError } from 'utils/sentry';
|
||||
import HTTPService from './HTTPService';
|
||||
// TODO: Audit this and other eslints
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-confusing-void-expression */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-enum-comparison */
|
||||
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
|
||||
|
||||
import { loadStripe } from "@stripe/stripe-js";
|
||||
import { CUSTOM_ERROR } from "utils/error";
|
||||
import { logError } from "utils/sentry";
|
||||
import HTTPService from "./HTTPService";
|
||||
|
||||
const getStripePublishableKey = (stripeAccount: StripeAccountCountry) => {
|
||||
if (stripeAccount === StripeAccountCountry.STRIPE_IN) {
|
||||
return (
|
||||
process.env.NEXT_PUBLIC_STRIPE_IN_PUBLISHABLE_KEY ??
|
||||
'pk_live_51HAhqDK59oeucIMOiTI6MDDM2UWUbCAJXJCGsvjJhiO8nYJz38rQq5T4iyQLDMKxqEDUfU5Hopuj4U5U4dff23oT00fHvZeodC'
|
||||
"pk_live_51HAhqDK59oeucIMOiTI6MDDM2UWUbCAJXJCGsvjJhiO8nYJz38rQq5T4iyQLDMKxqEDUfU5Hopuj4U5U4dff23oT00fHvZeodC"
|
||||
);
|
||||
} else if (stripeAccount === StripeAccountCountry.STRIPE_US) {
|
||||
return (
|
||||
process.env.NEXT_PUBLIC_STRIPE_US_PUBLISHABLE_KEY ??
|
||||
'pk_live_51LZ9P4G1ITnQlpAnrP6pcS7NiuJo3SnJ7gibjJlMRatkrd2EY1zlMVTVQG5RkSpLPbsHQzFfnEtgHnk1PiylIFkk00tC0LWXwi'
|
||||
"pk_live_51LZ9P4G1ITnQlpAnrP6pcS7NiuJo3SnJ7gibjJlMRatkrd2EY1zlMVTVQG5RkSpLPbsHQzFfnEtgHnk1PiylIFkk00tC0LWXwi"
|
||||
);
|
||||
} else {
|
||||
throw Error('stripe account not found');
|
||||
throw Error("stripe account not found");
|
||||
}
|
||||
};
|
||||
|
||||
const getEndpoint = () => {
|
||||
const endPoint =
|
||||
process.env.NEXT_PUBLIC_ENTE_ENDPOINT ?? 'https://api.ente.io';
|
||||
process.env.NEXT_PUBLIC_ENTE_ENDPOINT ?? "https://api.ente.io";
|
||||
return endPoint;
|
||||
};
|
||||
enum PAYMENT_INTENT_STATUS {
|
||||
SUCCESS = 'success',
|
||||
REQUIRE_ACTION = 'requires_action',
|
||||
REQUIRE_PAYMENT_METHOD = 'requires_payment_method',
|
||||
SUCCESS = "success",
|
||||
REQUIRE_ACTION = "requires_action",
|
||||
REQUIRE_PAYMENT_METHOD = "requires_payment_method",
|
||||
}
|
||||
|
||||
enum FAILURE_REASON {
|
||||
// Unable to authenticate card or 3DS
|
||||
// User should be showing button for fixing card via customer portal
|
||||
AUTHENTICATION_FAILED = 'authentication_failed',
|
||||
AUTHENTICATION_FAILED = "authentication_failed",
|
||||
// Card declined result in this error. Show button to the customer portal.
|
||||
REQUIRE_PAYMENT_METHOD = 'requires_payment_method',
|
||||
STRIPE_ERROR = 'stripe_error',
|
||||
CANCELED = 'canceled',
|
||||
SERVER_ERROR = 'server_error',
|
||||
REQUIRE_PAYMENT_METHOD = "requires_payment_method",
|
||||
STRIPE_ERROR = "stripe_error",
|
||||
CANCELED = "canceled",
|
||||
SERVER_ERROR = "server_error",
|
||||
}
|
||||
|
||||
enum STRIPE_ERROR_TYPE {
|
||||
CARD_ERROR = 'card_error',
|
||||
AUTHENTICATION_ERROR = 'authentication_error',
|
||||
CARD_ERROR = "card_error",
|
||||
AUTHENTICATION_ERROR = "authentication_error",
|
||||
}
|
||||
|
||||
enum STRIPE_ERROR_CODE {
|
||||
AUTHENTICATION_ERROR = 'payment_intent_authentication_failure',
|
||||
AUTHENTICATION_ERROR = "payment_intent_authentication_failure",
|
||||
}
|
||||
|
||||
enum RESPONSE_STATUS {
|
||||
success = 'success',
|
||||
fail = 'fail',
|
||||
success = "success",
|
||||
fail = "fail",
|
||||
}
|
||||
|
||||
enum PaymentActionType {
|
||||
Buy = 'buy',
|
||||
Update = 'update',
|
||||
Buy = "buy",
|
||||
Update = "update",
|
||||
}
|
||||
|
||||
enum StripeAccountCountry {
|
||||
STRIPE_IN = 'IN',
|
||||
STRIPE_US = 'US',
|
||||
STRIPE_IN = "IN",
|
||||
STRIPE_US = "US",
|
||||
}
|
||||
|
||||
interface SubscriptionUpdateResponse {
|
||||
|
@ -75,10 +83,10 @@ interface SubscriptionUpdateResponse {
|
|||
export async function parseAndHandleRequest() {
|
||||
try {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const productID = urlParams.get('productID');
|
||||
const paymentToken = urlParams.get('paymentToken');
|
||||
const action = urlParams.get('action');
|
||||
const redirectURL = urlParams.get('redirectURL');
|
||||
const productID = urlParams.get("productID");
|
||||
const paymentToken = urlParams.get("paymentToken");
|
||||
const action = urlParams.get("action");
|
||||
const redirectURL = urlParams.get("redirectURL");
|
||||
if (!action && !paymentToken && !productID && !redirectURL) {
|
||||
throw Error(CUSTOM_ERROR.DIRECT_OPEN_WITH_NO_QUERY_PARAMS);
|
||||
} else if (!action || !paymentToken || !productID || !redirectURL) {
|
||||
|
@ -95,7 +103,7 @@ export async function parseAndHandleRequest() {
|
|||
throw Error(CUSTOM_ERROR.INVALID_ACTION);
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error("Error: ", JSON.stringify(e)) ;
|
||||
console.error("Error: ", JSON.stringify(e));
|
||||
if (e.message !== CUSTOM_ERROR.DIRECT_OPEN_WITH_NO_QUERY_PARAMS) {
|
||||
logError(e);
|
||||
}
|
||||
|
@ -110,7 +118,7 @@ async function getUserStripeAccountCountry(
|
|||
`${getEndpoint()}/billing/stripe-account-country`,
|
||||
undefined,
|
||||
{
|
||||
'X-Auth-Token': paymentToken,
|
||||
"X-Auth-Token": paymentToken,
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
|
@ -125,11 +133,11 @@ async function getStripe(
|
|||
const stripe = await loadStripe(publishableKey);
|
||||
|
||||
if (!stripe) {
|
||||
throw Error('stripe load failed');
|
||||
throw Error("stripe load failed");
|
||||
}
|
||||
return stripe;
|
||||
} catch (e) {
|
||||
logError(e, 'stripe load failed');
|
||||
logError(e, "stripe load failed");
|
||||
redirectToApp(
|
||||
redirectURL,
|
||||
RESPONSE_STATUS.fail,
|
||||
|
@ -145,9 +153,8 @@ export async function buyPaidSubscription(
|
|||
redirectURL: string,
|
||||
) {
|
||||
try {
|
||||
const { stripeAccountCountry } = await getUserStripeAccountCountry(
|
||||
paymentToken,
|
||||
);
|
||||
const { stripeAccountCountry } =
|
||||
await getUserStripeAccountCountry(paymentToken);
|
||||
const stripe = await getStripe(redirectURL, stripeAccountCountry);
|
||||
const { sessionID } = await createCheckoutSession(
|
||||
productID,
|
||||
|
@ -158,7 +165,7 @@ export async function buyPaidSubscription(
|
|||
sessionId: sessionID,
|
||||
});
|
||||
} catch (e) {
|
||||
logError(e, 'subscription purchase failed');
|
||||
logError(e, "subscription purchase failed");
|
||||
redirectToApp(
|
||||
redirectURL,
|
||||
RESPONSE_STATUS.fail,
|
||||
|
@ -180,7 +187,7 @@ async function createCheckoutSession(
|
|||
redirectURL,
|
||||
},
|
||||
{
|
||||
'X-Auth-Token': paymentToken,
|
||||
"X-Auth-Token": paymentToken,
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
|
@ -192,9 +199,8 @@ export async function updateSubscription(
|
|||
redirectURL: string,
|
||||
) {
|
||||
try {
|
||||
const { stripeAccountCountry } = await getUserStripeAccountCountry(
|
||||
paymentToken,
|
||||
);
|
||||
const { stripeAccountCountry } =
|
||||
await getUserStripeAccountCountry(paymentToken);
|
||||
const stripe = await getStripe(redirectURL, stripeAccountCountry);
|
||||
const { result } = await subscriptionUpdateRequest(
|
||||
paymentToken,
|
||||
|
@ -245,7 +251,7 @@ export async function updateSubscription(
|
|||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logError(e, 'subscription update failed');
|
||||
logError(e, "subscription update failed");
|
||||
redirectToApp(
|
||||
redirectURL,
|
||||
RESPONSE_STATUS.fail,
|
||||
|
@ -266,7 +272,7 @@ async function subscriptionUpdateRequest(
|
|||
},
|
||||
undefined,
|
||||
{
|
||||
'X-Auth-Token': paymentToken,
|
||||
"X-Auth-Token": paymentToken,
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
/* ubuntu-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-family: "Ubuntu";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(''), url('/fonts/ubuntu-v15-latin-regular.woff2') format('woff2'),
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/ubuntu-v15-latin-regular.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/fonts/ubuntu-v15-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url("/fonts/ubuntu-v15-latin-regular.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
|
||||
/* ubuntu-700 - latin */
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-family: "Ubuntu";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local(''), url('/fonts/ubuntu-v15-latin-700.woff2') format('woff2'),
|
||||
src:
|
||||
local(""),
|
||||
url("/fonts/ubuntu-v15-latin-700.woff2") format("woff2"),
|
||||
/* Chrome 26+, Opera 23+, Firefox 39+ */
|
||||
url('/fonts/ubuntu-v15-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
url("/fonts/ubuntu-v15-latin-700.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
|
||||
}
|
||||
html,
|
||||
body {
|
||||
|
@ -39,3 +43,11 @@ body {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ente-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export function runningInBrowser() {
|
||||
return typeof window !== 'undefined';
|
||||
return typeof window !== "undefined";
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export const CUSTOM_ERROR = {
|
||||
DIRECT_OPEN_WITH_NO_QUERY_PARAMS: 'direct open with no query params',
|
||||
MISSING_REQUIRED_QUERY_PARAM: 'missing required query param',
|
||||
INVALID_ACTION: 'invalid action',
|
||||
DIRECT_OPEN_WITH_NO_QUERY_PARAMS: "direct open with no query params",
|
||||
MISSING_REQUIRED_QUERY_PARAM: "missing required query param",
|
||||
INVALID_ACTION: "invalid action",
|
||||
};
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import { logError } from 'utils/sentry';
|
||||
|
||||
export enum LS_KEYS {
|
||||
AnonymizeUserID = 'anonymizedUserID',
|
||||
}
|
||||
|
||||
export const getData = (key: LS_KEYS) => {
|
||||
try {
|
||||
if (
|
||||
typeof localStorage === 'undefined' ||
|
||||
typeof key === 'undefined' ||
|
||||
typeof localStorage.getItem(key) === 'undefined'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const data = localStorage.getItem(key);
|
||||
return data && JSON.parse(data);
|
||||
} catch (e) {
|
||||
logError(e, 'Failed to Parse JSON');
|
||||
}
|
||||
};
|
||||
|
||||
export const setData = (key: LS_KEYS, value: object) => {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
};
|
|
@ -1,14 +1,3 @@
|
|||
import * as Sentry from '@sentry/nextjs';
|
||||
import { getUserAnonymizedID } from 'utils/user';
|
||||
|
||||
export const logError = (e: any, msg?: string) => {
|
||||
Sentry.captureException(e, {
|
||||
level: "info",
|
||||
user: { id: getUserAnonymizedID() },
|
||||
contexts: {
|
||||
context: {
|
||||
message: msg,
|
||||
},
|
||||
},
|
||||
});
|
||||
export const logError = (e: unknown, msg?: string) => {
|
||||
console.error(msg, e);
|
||||
};
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
import { getConstantValue } from './vernacularStrings';
|
||||
|
||||
const constants = getConstantValue();
|
||||
export default constants;
|
6
web/apps/payments/src/utils/strings/constants.tsx
Normal file
6
web/apps/payments/src/utils/strings/constants.tsx
Normal file
|
@ -0,0 +1,6 @@
|
|||
const englishConstants = {
|
||||
TITLE: "Payments | ente.io",
|
||||
SOMETHING_WENT_WRONG: "Oops, something went wrong.",
|
||||
NOT_FOUND: "404 | This page could not be found.",
|
||||
};
|
||||
export default englishConstants;
|
|
@ -1,6 +0,0 @@
|
|||
const englishConstants = {
|
||||
TITLE: 'Payments | ente.io',
|
||||
SOMETHING_WENT_WRONG: 'Oops, something went wrong.',
|
||||
NOT_FOUND: '404 | This page could not be found.',
|
||||
};
|
||||
export default englishConstants;
|
|
@ -1,87 +0,0 @@
|
|||
import { runningInBrowser } from 'utils/common';
|
||||
import englishConstants from './englishConstants';
|
||||
|
||||
/** Enums of supported locale */
|
||||
export enum locale {
|
||||
en = 'en',
|
||||
hi = 'hi',
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a template with placeholders which can then be
|
||||
* substituted at run time. Enabling the developer to create
|
||||
* different template for different locale and populate them
|
||||
* at run time.
|
||||
*
|
||||
* @param strings
|
||||
* @param keys
|
||||
* @returns string
|
||||
*/
|
||||
export function template(
|
||||
strings: TemplateStringsArray,
|
||||
...keys: string[] | number[]
|
||||
) {
|
||||
return (...values: any[]) => {
|
||||
const dict = values[values.length - 1] || {};
|
||||
const result = [strings[0]];
|
||||
keys.forEach((key, i) => {
|
||||
const value = typeof key === 'number' ? values[key] : dict[key];
|
||||
result.push(value, strings[i + 1]);
|
||||
});
|
||||
return result.join('');
|
||||
};
|
||||
}
|
||||
|
||||
/** Type for vernacular string constants */
|
||||
export type VernacularConstants<T> = {
|
||||
[locale.en]: T;
|
||||
[locale.hi]?: {
|
||||
[x in keyof T]?: string;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a valid locale from string and defaults
|
||||
* to English.
|
||||
*
|
||||
* @param lang
|
||||
*/
|
||||
export const getLocale = (lang: string) => {
|
||||
switch (lang) {
|
||||
case locale.hi:
|
||||
return locale.hi;
|
||||
default:
|
||||
return locale.en;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Global constants
|
||||
*/
|
||||
const globalConstants: VernacularConstants<typeof englishConstants> = {
|
||||
en: englishConstants,
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to extend global constants with local constants
|
||||
* @param localConstants
|
||||
*/
|
||||
export function getConstantValue<T>(localConstants?: VernacularConstants<T>) {
|
||||
const searchParam = runningInBrowser() ? window.location.search : '';
|
||||
const query = new URLSearchParams(searchParam);
|
||||
const currLocale = getLocale(query.get('lang') ?? 'en');
|
||||
|
||||
if (currLocale !== 'en') {
|
||||
return {
|
||||
...globalConstants.en,
|
||||
...localConstants?.en,
|
||||
...globalConstants[currLocale],
|
||||
...localConstants?.[currLocale],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...globalConstants[currLocale],
|
||||
...localConstants?.[currLocale],
|
||||
};
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
import { getData, LS_KEYS, setData } from 'utils/localStorage';
|
||||
|
||||
export function makeID(length: number) {
|
||||
let result = '';
|
||||
const characters =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const charactersLength = characters.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(
|
||||
Math.floor(Math.random() * charactersLength),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getUserAnonymizedID() {
|
||||
let anonymizeUserID = getData(LS_KEYS.AnonymizeUserID)?.id;
|
||||
if (!anonymizeUserID) {
|
||||
anonymizeUserID = makeID(6);
|
||||
setData(LS_KEYS.AnonymizeUserID, { id: anonymizeUserID });
|
||||
}
|
||||
return anonymizeUserID;
|
||||
}
|
|
@ -1,31 +1,21 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": "./src",
|
||||
"incremental": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"baseUrl": "./src",
|
||||
"incremental": true,
|
||||
"allowJs": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": ["node_modules", "next.config.js"]
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
# Sample configuration file
|
||||
#
|
||||
# All variables are commented out by default. Copy paste this into a new file
|
||||
# called `.env.local` (or create a new file with that name) and add the
|
||||
# called `.env.local` (or create a new empty file with that name) and add the
|
||||
# environment variables you want to apply during development. `.env.local` is
|
||||
# gitignored, so you can freely customize it for your local setup.
|
||||
#
|
||||
|
@ -39,7 +39,7 @@
|
|||
|
||||
# The Ente API endpoint for payments related functionality
|
||||
#
|
||||
# NEXT_PUBLIC_ENTE_PAYMENT_ENDPOINT = http://localhost:3001
|
||||
# NEXT_PUBLIC_ENTE_PAYMENTS_ENDPOINT = http://localhost:3001
|
||||
|
||||
# The URL for the shared albums deployment
|
||||
#
|
||||
|
@ -66,7 +66,7 @@
|
|||
#
|
||||
# Enhancement: Consider moving that into the app/ folder in this repository.
|
||||
#
|
||||
# NEXT_PUBLIC_ENTE_FAMILY_PORTAL_ENDPOINT = http://localhost:3003
|
||||
# NEXT_PUBLIC_ENTE_FAMILY_ENDPOINT = http://localhost:3001
|
||||
|
||||
# The JSON which describes the expected results of our integration tests. See
|
||||
# `upload.test.ts` for more details of the expected format.
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
# Develop against a server running on localhost
|
||||
#
|
||||
# Copy this file to `.env`. Then if you run a local instance of the web client
|
||||
# with `yarn dev:photos`, it will connect to a locally running instance of the
|
||||
# server. Not everything will work, you might need to set other env vars (see
|
||||
# Copy this file to `.env.local`. Then if you run a local instance of the web
|
||||
# client with `yarn dev:photos`, it will connect to a locally running instance
|
||||
# of the server.
|
||||
#
|
||||
# Not everything will work, you might need to set other env vars (see
|
||||
# `.env.development`), but it should give you a usable baseline setup.
|
||||
#
|
||||
# Equivalent CLI command using environment variables would be
|
||||
|
|
|
@ -11,16 +11,19 @@
|
|||
"build:accounts": "yarn workspace accounts next build",
|
||||
"build:auth": "yarn workspace auth next build",
|
||||
"build:cast": "yarn workspace cast next build",
|
||||
"build:payments": "yarn workspace payments next build",
|
||||
"build:photos": "yarn workspace photos next build",
|
||||
"deploy:accounts": "open 'https://github.com/ente-io/ente/compare/deploy/accounts...main?quick_pull=1&title=[web]+Deploy+accounts&body=Deploy+accounts.ente.io'",
|
||||
"deploy:auth": "open 'https://github.com/ente-io/ente/compare/deploy/auth...main?quick_pull=1&title=[web]+Deploy+auth&body=Deploy+auth.ente.io'",
|
||||
"deploy:cast": "open 'https://github.com/ente-io/ente/compare/deploy/cast...main?quick_pull=1&title=[web]+Deploy+cast&body=Deploy+cast.ente.io'",
|
||||
"deploy:payments": "open 'https://github.com/ente-io/ente/compare/deploy/payments...main?quick_pull=1&title=[web]+Deploy+payments&body=Deploy+payments.ente.io'",
|
||||
"deploy:photos": "open 'https://github.com/ente-io/ente/compare/deploy/photos...main?quick_pull=1&title=[web]+Deploy+photos&body=Deploy+web.ente.io'",
|
||||
"dev": "yarn dev:photos",
|
||||
"dev:accounts": "yarn workspace accounts next dev",
|
||||
"dev:accounts": "yarn workspace accounts next dev -p 3001",
|
||||
"dev:albums": "yarn workspace photos next dev -p 3002",
|
||||
"dev:auth": "yarn workspace auth next dev",
|
||||
"dev:cast": "yarn workspace cast next dev",
|
||||
"dev:payments": "yarn workspace payments next dev -p 3001",
|
||||
"dev:photos": "yarn workspace photos next dev",
|
||||
"lint": "yarn prettier --check . && yarn workspaces run eslint .",
|
||||
"lint-fix": "yarn prettier --write . && yarn workspaces run eslint --fix ."
|
||||
|
|
|
@ -70,14 +70,6 @@ export const getAccountsURL = () => {
|
|||
return `https://accounts.ente.io`;
|
||||
};
|
||||
|
||||
export const getPaymentsURL = () => {
|
||||
const paymentsURL = process.env.NEXT_PUBLIC_ENTE_PAYMENT_ENDPOINT;
|
||||
if (paymentsURL) {
|
||||
return paymentsURL;
|
||||
}
|
||||
return `https://payments.ente.io`;
|
||||
};
|
||||
|
||||
export const getAlbumsURL = () => {
|
||||
const albumsURL = process.env.NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT;
|
||||
if (albumsURL) {
|
||||
|
@ -91,9 +83,20 @@ export const getAlbumsURL = () => {
|
|||
* family plans.
|
||||
*/
|
||||
export const getFamilyPortalURL = () => {
|
||||
const familyURL = process.env.NEXT_PUBLIC_ENTE_FAMILY_PORTAL_ENDPOINT;
|
||||
const familyURL = process.env.NEXT_PUBLIC_ENTE_FAMILY_ENDPOINT;
|
||||
if (familyURL) {
|
||||
return familyURL;
|
||||
}
|
||||
return `https://family.ente.io`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the URL for the host that handles payment related functionality.
|
||||
*/
|
||||
export const getPaymentsURL = () => {
|
||||
const paymentsURL = process.env.NEXT_PUBLIC_ENTE_PAYMENTS_ENDPOINT;
|
||||
if (paymentsURL) {
|
||||
return paymentsURL;
|
||||
}
|
||||
return `https://payments.ente.io`;
|
||||
};
|
||||
|
|
|
@ -538,7 +538,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.7.2.tgz#2d4260033e199b3032a08b41348ac10de21c47e9"
|
||||
integrity sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==
|
||||
|
||||
"@stripe/stripe-js@^1.13.2":
|
||||
"@stripe/stripe-js@^1.13.2", "@stripe/stripe-js@^1.17.0":
|
||||
version "1.54.2"
|
||||
resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.54.2.tgz#0665848e22cbda936cfd05256facdfbba121438d"
|
||||
integrity sha512-R1PwtDvUfs99cAjfuQ/WpwJ3c92+DAMy9xGApjqlWQMj0FKQabUAys2swfTRNzuYAYJh7NqK2dzcYVNkKLEKUg==
|
||||
|
@ -1151,6 +1151,13 @@ axe-core@=4.7.0:
|
|||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
|
||||
integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==
|
||||
|
||||
axios@^0.21.1:
|
||||
version "0.21.4"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
|
||||
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.0"
|
||||
|
||||
axios@^1.6.7:
|
||||
version "1.6.7"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7"
|
||||
|
@ -1210,6 +1217,11 @@ blazeface-back@^0.0.9:
|
|||
resolved "https://registry.yarnpkg.com/blazeface-back/-/blazeface-back-0.0.9.tgz#a8a26a0022950eb21136693f2fca3c52315ad2a4"
|
||||
integrity sha512-t0i5V117j074d7d7mlLaRq9n/bYchXcSEgpWVbGGloV68A6Jn22t4SNoEC3t+MOsU8H+eXoDv2/6+JsqActM1g==
|
||||
|
||||
bootstrap@4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.0.tgz#97b9f29ac98f98dfa43bf7468262d84392552fd7"
|
||||
integrity sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==
|
||||
|
||||
bootstrap@^4.5.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.2.tgz#8e0cd61611728a5bf65a3a2b8d6ff6c77d5d7479"
|
||||
|
@ -2042,6 +2054,11 @@ flatted@^3.2.9:
|
|||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
|
||||
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
|
||||
|
||||
follow-redirects@^1.14.0:
|
||||
version "1.15.6"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
|
||||
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
|
||||
|
||||
follow-redirects@^1.15.4:
|
||||
version "1.15.5"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020"
|
||||
|
|
Loading…
Add table
Reference in a new issue