diff --git a/.gitignore b/.gitignore
index 99048f8..ec8dc6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,7 @@ build/
dist/
*.pyc
*.pyo
-**/env/
+.env/
env/
.env
mCovo.py
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/ax3Client.iml b/.idea/ax3Client.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/ax3Client.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..03d9549
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..be27107
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index c8c7cbc..2ff90c1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "client",
+ "name": "novodraft-client",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "client",
+ "name": "novodraft-client",
"version": "0.1.0",
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
@@ -14,6 +14,7 @@
"bootstrap": "^5.3.2",
"classnames": "^2.3.2",
"docx": "^8.2.4",
+ "dotenv": "^16.3.2",
"firebase": "^10.3.1",
"react": "^18.2.0",
"react-bootstrap": "^2.8.0",
@@ -24,6 +25,7 @@
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"react-spinners": "^0.13.8",
+ "stripe": "^14.13.0",
"uuid": "^9.0.1",
"web-vitals": "^2.1.4"
},
@@ -7849,6 +7851,17 @@
"tslib": "^2.0.3"
}
},
+ "node_modules/dotenv": {
+ "version": "16.3.2",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz",
+ "integrity": "sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/motdotla/dotenv?sponsor=1"
+ }
+ },
"node_modules/dotenv-expand": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
@@ -17275,6 +17288,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/stripe": {
+ "version": "14.13.0",
+ "resolved": "https://registry.npmjs.org/stripe/-/stripe-14.13.0.tgz",
+ "integrity": "sha512-uLOfWtBUL1amJCTpKCXWrHntFOSaO2PWb/2hsxV/OlXLr0bz5MyU8IW1pFlmZqpw6hBqAW5Fad7Ty7xRxDYrzA==",
+ "dependencies": {
+ "@types/node": ">=8.1.0",
+ "qs": "^6.11.0"
+ },
+ "engines": {
+ "node": ">=12.*"
+ }
+ },
"node_modules/style-loader": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz",
diff --git a/package.json b/package.json
index 55f1a87..fcc1946 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
"bootstrap": "^5.3.2",
"classnames": "^2.3.2",
"docx": "^8.2.4",
+ "dotenv": "^16.3.2",
"firebase": "^10.3.1",
"react": "^18.2.0",
"react-bootstrap": "^2.8.0",
@@ -20,6 +21,7 @@
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"react-spinners": "^0.13.8",
+ "stripe": "^14.13.0",
"uuid": "^9.0.1",
"web-vitals": "^2.1.4"
},
diff --git a/src/Components/SignupPage/SignupPage.js b/src/Components/SignupPage/SignupPage.js
index 900991c..7834952 100644
--- a/src/Components/SignupPage/SignupPage.js
+++ b/src/Components/SignupPage/SignupPage.js
@@ -17,25 +17,34 @@ import {
isFormDataHasErrors,
} from "../../Utils/Form";
import { objectMap } from "../../Utils/Object";
-import { signupfields } from "../../Constants/Fields/SignupFields";
+import { signupFields } from "../../Constants/Fields/SignupFields";
import { paymentfields } from "../../Constants/Fields/PaymentFields";
import PaymentModal from "../Modals/PaymentModal";
import "../../styles/signup.scss";
+import Stripe from 'stripe';
const SignupPage = () => {
const navigate = useNavigate();
const [notice, setNotice] = useState("");
const fieldsChunkSize = 2;
const [isBusy, setIsBusy] = useState(false);
- const [data, setData] = useState(getFormDataDefaults(signupfields));
+ const [data, setData] = useState(getFormDataDefaults(signupFields));
const [paymentData, setPaymentData] = useState(
getFormDataDefaults(paymentfields)
);
const [showPaymentModal, setShowPaymentModal] = useState(false);
const modalText = "Description of monthly and annual subscriptions";
+ // this could also go in App.js if needed in the future
+ const stripe = new Stripe('pk_test_51NNoasBi8p7FeGFrI5SfpM6HuNMoxwImx6NRKyKbgbt6OPxMxQDiZ9I1GqvDa9qUwB3D3jlJOng6MyjPppWofxzP00Exvr8dBy')
+
+ const apiUrl =
+ process.env.NODE_ENV === "development"
+ ? process.env.REACT_APP_API_DEV
+ : process.env.REACT_APP_API_PROD;
+
const handleChangeInput = (e, name) => {
- const newData = handleFormDataChange(e, name, data, signupfields);
+ const newData = handleFormDataChange(e, name, data, signupFields);
if (newData !== null) {
setData(newData);
}
@@ -54,7 +63,7 @@ const SignupPage = () => {
};
const validateData = () => {
- const newData = getValidatedFormData(data, signupfields);
+ const newData = getValidatedFormData(data, signupFields);
const hasErrors = isFormDataHasErrors(newData);
setData(newData);
return hasErrors ? null : objectMap(({ value }) => value, newData);
@@ -125,10 +134,45 @@ const SignupPage = () => {
setShowPaymentModal(true);
};
- async function handleStripeAuthorization(paymentDataValues) {
+ async function handleStripeAuthorization(paymentDataValues, customerDataValues) {
// Do: calls to Stripe API
// if success, return some truthy value or
// handle failure in handleSignup
+
+ let error = false;
+
+ const token = await stripe.tokens.create({
+ card: {
+ number: paymentDataValues.cardNumber,
+ exp_month: paymentDataValues.cardExpirationMonth,
+ exp_year: paymentDataValues.cardExpirationYear,
+ cvc: paymentDataValues.cardCvvCode,
+ name: paymentDataValues.cardFirstName + ' ' + paymentDataValues.cardLastName,
+ }
+ })
+
+ try {
+ let response = await fetch(`${apiUrl}/create-subscription`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ type: 'monthly', // TODO: replace this with the user-chosen value later when we have the UI for it
+ customerData: {
+ email: customerDataValues.email,
+ name: customerDataValues.firstName + ' ' + customerDataValues.lastName,
+ },
+ token,
+ }),
+ })
+ }
+ catch (error) {
+ console.log(error)
+ error = true;
+ }
+
+ return !error;
}
const handleSignup = async (e) => {
@@ -144,25 +188,29 @@ const SignupPage = () => {
}
setIsBusy(true);
- const paymentSuccess = handleStripeAuthorization(paymentDataValues);
- if (paymentSuccess) {
- try {
- const userCredential = await createUserWithEmailAndPassword(
- auth,
- dataValues.email,
- dataValues.password
- );
- const user = userCredential.user;
+
+ try {
+ const userCredential = await createUserWithEmailAndPassword(
+ auth,
+ dataValues.email,
+ dataValues.password
+ );
+ const user = userCredential.user;
+
+ const paymentSuccess = await handleStripeAuthorization(paymentDataValues, dataValues);
+
+ if(paymentSuccess) {
await saveUserData(user.uid, dataValues);
navigate("/");
- } catch (error) {
- setIsBusy(false);
- setNotice(
- error.message || "Sorry, something went wrong. Please try again."
- );
}
- } else {
- //handle payment failure
+ else {
+ // handle payment failure
+ }
+ } catch (error) {
+ setIsBusy(false);
+ setNotice(
+ error.message || "Sorry, something went wrong. Please try again."
+ );
}
};
@@ -174,7 +222,7 @@ const SignupPage = () => {