diff --git a/.env.local b/.env.local
deleted file mode 100644
index 61b4d79..0000000
--- a/.env.local
+++ /dev/null
@@ -1 +0,0 @@
-# GA_MEASUREMENT_ID=UA-169820463-3
diff --git a/.gitignore b/.gitignore
index 1f1d315..bc8c3c3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
.DS_Store
-.next
-node_modules
+/.next
+/node_modules
+/.env
diff --git a/components/Itinerary/Itinerary.js b/components/Itinerary/Itinerary.js
index bf20b6a..ad8bead 100644
--- a/components/Itinerary/Itinerary.js
+++ b/components/Itinerary/Itinerary.js
@@ -13,36 +13,52 @@ import {
lines,
} from './Itinerary.module.css';
-export default function Itinerary() {
+export default function Itinerary({linkToEdit}) {
const { query } = useRouter();
const { t, formatNumber } = useTranslation();
const {
trip: { airports, distance, flightClass, passengers },
} = useTrip();
+
+ const WrappedAirportCard = (airport, key) =>
+
;
+
+ const stats =
+ ;
+
return (
<>
{t('itinerary')}
- {airports.map((airport, index) => (
-
+ {linkToEdit
+ ?
+ <>
+ {airports.map((airport, index) => (
+
+
+ {WrappedAirportCard(airport, index)}
+
+
+ ))}
+
-
+ {stats}
- ))}
-
-
-
-
-
+ >
+ : <>
+ {airports.map(WrappedAirportCard)}
+ {stats}
+ >
+ }
>
);
}
diff --git a/components/Payment/Payment.js b/components/Payment/Payment.js
index eee08d2..dc798ef 100644
--- a/components/Payment/Payment.js
+++ b/components/Payment/Payment.js
@@ -1,6 +1,6 @@
import { useState } from 'react';
-import { useTrip, useTranslation } from '../../hooks';
+import { usePayment, useTrip, useTranslation } from '../../hooks';
import { Select } from '../Select';
@@ -8,61 +8,24 @@ import {
wrapper,
headline,
amount,
- select,
button,
} from './Payment.module.css';
-function usePayment() {
- const { t, formatNumber } = useTranslation();
- const {
- trip: { cost },
- } = useTrip();
- const [recipient, setRecipient] = useState('trees');
- const getUrl = () => {
- const amount = formatNumber(cost, 2);
- const language = t('lang');
- switch (recipient) {
- case 'trees':
- return `https://donorbox.org/trees-for-lure?amount=${amount}&language=${language}`;
- case 'greenpeace':
- return `https://www.greenpeace.de/spenden?betrag=${amount}`;
- default:
- throw new Error('unknown recipient');
- }
- };
- return {
- options: {
- trees: t('donateTrees'),
- greenpeace: t('donateToGreenpeace'),
- },
- value: recipient,
- onChange: setRecipient,
- href: getUrl(),
- };
-}
export default function Payment() {
const { t, formatNumber } = useTranslation();
const {
trip: { cost },
} = useTrip();
- const { options, value, onChange, href } = usePayment();
+ const { process } = usePayment();
+
return (
);
}
diff --git a/components/ThankYou/ThankYou.js b/components/ThankYou/ThankYou.js
new file mode 100644
index 0000000..e557dd5
--- /dev/null
+++ b/components/ThankYou/ThankYou.js
@@ -0,0 +1,12 @@
+import { useTranslation } from '../../hooks';
+
+export default function ThankYou() {
+ const { t } = useTranslation();
+
+ return (
+
+
{t('thankYouHeadline')}
+
{t('thankYouDescription')}
+
+ );
+}
diff --git a/components/ThankYou/index.js b/components/ThankYou/index.js
new file mode 100644
index 0000000..4a84688
--- /dev/null
+++ b/components/ThankYou/index.js
@@ -0,0 +1 @@
+export { default as ThankYou } from './ThankYou';
diff --git a/components/index.js b/components/index.js
index 147dfbc..3306b8a 100644
--- a/components/index.js
+++ b/components/index.js
@@ -6,3 +6,4 @@ export { TripForm } from './TripForm';
export { Itinerary } from './Itinerary';
export { Payment } from './Payment';
export { EmissionData } from './EmissionData';
+export { ThankYou } from './ThankYou';
diff --git a/data/de.json b/data/de.json
index 76e0419..d59fe4d 100644
--- a/data/de.json
+++ b/data/de.json
@@ -6,14 +6,11 @@
"analysis": "Analyse",
"business": "Business",
"calculateEmissions": "Emissionen berechnen",
- "chooseOrganization": "Organisation auswählen",
"cookieConsentButton": "Cookies Akzeptieren",
"ch4": "Methan",
"co2": "Kohlendioxid",
"destination": "Zielflughafen",
"distance": "Entfernung",
- "donateToGreenpeace": "Greenpeace unterstützen",
- "donateTrees": "Bäume pflanzen",
"economy": "Economy",
"emissions": "Emissionen",
"first": "First",
@@ -25,10 +22,14 @@
"origin": "Abflughafen",
"passengers": "Passagiere",
"paymentHeadline": "Offsetting-Kosten",
+ "paymentName": "CO2-Kompensation",
+ "paymentDescription": "Flugreise mit diesen Stationen: {{ airports }}",
"premium": "Economy Premium",
"removeStopover": "Zwischenstop entfernen",
"roundTrip": "Rückflug",
"stopover": "Zwischenstopp",
"trees": "Baum-Äquivalent",
+ "thankYouHeadline": "Danke für's Spenden!",
+ "thankYouDescription": "Die Spendenquittung kommt im nächsten Januar per E-Mail. 💚",
"yes": "Ja"
}
diff --git a/hooks/index.js b/hooks/index.js
index b5ff0bd..79f261b 100644
--- a/hooks/index.js
+++ b/hooks/index.js
@@ -1,3 +1,4 @@
+export { usePayment } from './usePayment';
export { useSuggestions } from './useSuggestions';
export { useTracking, withTracking } from './useTracking';
export { useTranslation, withTranslation } from './useTranslation';
diff --git a/hooks/usePayment.js b/hooks/usePayment.js
new file mode 100644
index 0000000..5ed303a
--- /dev/null
+++ b/hooks/usePayment.js
@@ -0,0 +1,36 @@
+import { useState } from 'react';
+
+function getPaymentURL(amount) {
+ const params = new URLSearchParams({ amount: Number(amount) });
+ return `/api/payment?${params.toString()}`;
+}
+
+async function fetchPaymentSession(amount) {
+ const response = await fetch(getPaymentURL(amount));
+ return response.ok
+ ? response.json()
+ : Promise.reject(new Error(response.statusText));
+}
+
+async function processPayment(amount) {
+ try {
+ const { loadStripe } = await import('@stripe/stripe-js');
+ const [stripe, { sessionId }] = await Promise.all([
+ loadStripe(window.stripeKey),
+ fetchPaymentSession(amount),
+ ]);
+ stripe.redirectToCheckout({ sessionId });
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function usePayment() {
+ const [processing, setProcessing] = useState(false);
+ const process = (amount) => {
+ if (processing) return;
+ setProcessing(true);
+ processPayment(amount);
+ };
+ return { processing, process };
+}
diff --git a/package.json b/package.json
index f019f49..f80172f 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"@mdx-js/loader": "^1.6.16",
"@mdx-js/react": "^1.6.16",
"@next/mdx": "^9.5.2",
+ "@stripe/stripe-js": "^1.11.0",
"@turf/distance": "^6.0.1",
"browserslist": "^4.14.0",
"cookie": "^0.4.1",
@@ -42,7 +43,8 @@
"postcss-preset-env": "^6.7.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
- "snakecase-keys": "^3.2.0"
+ "snakecase-keys": "^3.2.0",
+ "stripe": "^8.130.0"
},
"devDependencies": {
"@commitlint/cli": "^9.1.2",
diff --git a/pages/_document.js b/pages/_document.js
index c45c9f7..b6062e5 100644
--- a/pages/_document.js
+++ b/pages/_document.js
@@ -6,12 +6,13 @@ export default withTranslation(
class FlygoodDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
+ const stripeApiKey = process.env.STRIPE_PUBLIC_KEY;
const trackingId = process.env.GA_MEASUREMENT_ID;
const mapboxToken = process.env.MAPBOX_TOKEN;
- return { ...initialProps, trackingId, mapboxToken };
+ return { ...initialProps, trackingId, stripeApiKey, mapboxToken };
}
render() {
- const { trackingId, mapboxToken, t } = this.props;
+ const { stripeApiKey, trackingId, mapboxToken, t } = this.props;
return (
@@ -49,6 +50,13 @@ export default withTranslation(
+ {stripeApiKey ? (
+
+ ) : null}
{trackingId ? (