Added elements for mobile payments. BOD-90

This commit is contained in:
Patrick Fic
2020-07-24 16:30:21 -07:00
parent 1e00b376ea
commit 8750894c56
8 changed files with 278 additions and 140 deletions

View File

@@ -24,6 +24,9 @@ const Unauthorized = lazy(() =>
import("../pages/unauthorized/unauthorized.component")
);
const CsiPage = lazy(() => import("../pages/csi/csi.container.page"));
const MobilePaymentContainer = lazy(() =>
import("../pages/mobile-payment/mobile-payment.container")
);
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
@@ -50,20 +53,25 @@ export function App({ checkUserSession, currentUser }) {
<div>
<Switch>
<ErrorBoundary>
<Suspense fallback={<LoadingSpinner message='App.Js Suspense' />}>
<Route exact path='/' component={LandingPage} />
<Route exact path='/unauthorized' component={Unauthorized} />
<Route exact path='/signin' component={SignInPage} />
<Route exact path='/resetpassword' component={ResetPassword} />
<Route exact path='/csi/:surveyId' component={CsiPage} />
<Suspense fallback={<LoadingSpinner message="App.Js Suspense" />}>
<Route exact path="/" component={LandingPage} />
<Route exact path="/unauthorized" component={Unauthorized} />
<Route exact path="/signin" component={SignInPage} />
<Route exact path="/resetpassword" component={ResetPassword} />
<Route exact path="/csi/:surveyId" component={CsiPage} />
<Route
exact
path="/mp/:paymentIs"
component={MobilePaymentContainer}
/>
<PrivateRoute
isAuthorized={currentUser.authorized}
path='/manage'
path="/manage"
component={ManagePage}
/>
<PrivateRoute
isAuthorized={currentUser.authorized}
path='/tech'
path="/tech"
component={TechPageContainer}
/>
</Suspense>

View File

@@ -1,11 +1,17 @@
import axios from "axios";
import React from "react";
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { setEmailOptions } from "../../redux/email/email.actions";
import { TemplateList } from "../../utils/TemplateConstants";
import {
PaymentRequestButtonElement,
useStripe,
Elements,
useElements,
} from "@stripe/react-stripe-js";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -15,86 +21,53 @@ const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(function Test({ bodyshop, setEmailOptions }) {
const handle = async () => {
const response = await axios.post(
"/accounting/iif/receivables",
{ jobId: "661dd1d5-bf06-426f-8bd2-bd9e41de8eb1" },
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
function Test({ bodyshop, setEmailOptions }) {
const stripe = useStripe();
const [paymentRequest, setPaymentRequest] = useState(null);
useEffect(() => {
if (stripe) {
console.log("in useeff");
const pr = stripe.paymentRequest({
country: "CA",
displayItems: [{ label: "Deductible", amount: 1099 }],
currency: "cad",
total: {
label: "Demo total",
amount: 1099,
},
}
);
console.log("handle -> result", response);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute(
"download",
response.headers.filename || "receivables.iif"
); //or any other extension
document.body.appendChild(link);
link.click();
};
requestPayerName: true,
requestPayerEmail: true,
});
const handleLocal = async () => {
try {
const response = await axios.post(
"http://localhost:1337/qb/receivables",
{ jobId: "661dd1d5-bf06-426f-8bd2-bd9e41de8eb1" },
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
},
console.log("pr", pr);
// Check the availability of the Payment Request API.
pr.canMakePayment().then((result) => {
console.log("result", result);
if (result) {
setPaymentRequest(pr);
} else {
var details = {
total: { label: "", amount: { currency: "CAD", value: "0.00" } },
};
// new PaymentRequest(
// [{ supportedMethods: ["basic-card"] }],
// {}
// // details
// ).show();
}
);
console.log("handle -> result", response);
} catch (error) {
console.log("error", JSON.stringify(error));
});
}
};
}, [stripe]);
const handleQbxml = async () => {
const response = await axios.post(
"/accounting/qbxml/receivables",
{ jobId: "661dd1d5-bf06-426f-8bd2-bd9e41de8eb1" },
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
},
}
if (paymentRequest) {
console.log("****************render");
return (
<div style={{ height: "300px" }}>
<PaymentRequestButtonElement options={{ paymentRequest }} />
</div>
);
console.log("handle -> XML", response);
try {
const response2 = await axios.post(
"http://localhost:1337/qb/receivables",
response.data,
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
},
}
);
console.log("handle -> result", response2);
} catch (error) {
console.log("error", error, JSON.stringify(error));
}
// const url = window.URL.createObjectURL(new Blob([response.data]));
// const link = document.createElement("a");
// link.href = url;
// link.setAttribute(
// "download",
// response.headers.filename || "receivables.iif"
// ); //or any other extension
// document.body.appendChild(link);
// link.click();
};
}
return (
<div>
@@ -116,9 +89,6 @@ export default connect(
>
send email
</button>
<button onClick={handle}>Hit with Header.</button>
<button onClick={handleLocal}>Hit Localhost with Header.</button>
<button onClick={handleQbxml}>Qbxml</button>
<button
onClick={() => {
logImEXEvent("IMEXEVENT", { somethignArThare: 5 });
@@ -128,4 +98,6 @@ export default connect(
</button>
</div>
);
});
}
export default connect(mapStateToProps, mapDispatchToProps)(Test);

View File

@@ -61,54 +61,56 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
analytics.logEvent(eventName, eventParams);
};
messaging.onMessage(async (payload) => {
console.log("**********UTILS Message received. ", payload);
navigator.serviceWorker.getRegistration().then((registration) => {
return registration.showNotification(
"[UTIL]" + payload.notification.title,
payload.notification
);
if (messaging) {
messaging.onMessage(async (payload) => {
console.log("**********UTILS Message received. ", payload);
navigator.serviceWorker.getRegistration().then((registration) => {
return registration.showNotification(
"[UTIL]" + payload.notification.title,
payload.notification
);
});
// if (!payload.clientId) return;
// // Get the client.
// const client = await clients.get(payload.clientId);
// // Exit early if we don't get the client.
// // Eg, if it closed.
// if (!client) return;
// // Send a message to the client.
// console.log("Posting to client.");
// client.postMessage({
// msg: "Hey I just got a fetch from you!",
// url: payload.request.url,
// });
// [START_EXCLUDE]
// Update the UI to include the received message.
//appendMessage(payload);
// [END_EXCLUDE]
});
// if (!payload.clientId) return;
// // Get the client.
// const client = await clients.get(payload.clientId);
// // Exit early if we don't get the client.
// // Eg, if it closed.
// if (!client) return;
// // Send a message to the client.
// console.log("Posting to client.");
// client.postMessage({
// msg: "Hey I just got a fetch from you!",
// url: payload.request.url,
// });
// [START_EXCLUDE]
// Update the UI to include the received message.
//appendMessage(payload);
// [END_EXCLUDE]
});
messaging.onTokenRefresh(() => {
messaging
.getToken()
.then((refreshedToken) => {
console.log("**********Token refreshed.");
// Indicate that the new Instance ID token has not yet been sent to the
// app server.
// setTokenSentToServer(false);
// // Send Instance ID token to app server.
// sendTokenToServer(refreshedToken);
// // [START_EXCLUDE]
// // Display new Instance ID token and clear UI of all previous messages.
// resetUI();
// [END_EXCLUDE]
})
.catch((err) => {
console.log("**********Unable to retrieve refreshed token ", err);
// showToken("Unable to retrieve refreshed token ", err);
});
});
messaging.onTokenRefresh(() => {
messaging
.getToken()
.then((refreshedToken) => {
console.log("**********Token refreshed.");
// Indicate that the new Instance ID token has not yet been sent to the
// app server.
// setTokenSentToServer(false);
// // Send Instance ID token to app server.
// sendTokenToServer(refreshedToken);
// // [START_EXCLUDE]
// // Display new Instance ID token and clear UI of all previous messages.
// resetUI();
// [END_EXCLUDE]
})
.catch((err) => {
console.log("**********Unable to retrieve refreshed token ", err);
// showToken("Unable to retrieve refreshed token ", err);
});
});
}

View File

@@ -165,15 +165,20 @@ export function Manage({ match, conflict }) {
<LoadingSpinner message={t("general.labels.loadingapp")} />
}
>
<Elements stripe={stripePromise}>
<PaymentModalContainer />
</Elements>
<BreadCrumbs />
<EnterInvoiceModalContainer />
<JobCostingModal />
<EmailOverlayContainer />
<TimeTicketModalContainer />
<PrintCenterModalContainer />
<Elements stripe={stripePromise}>
<PaymentModalContainer />
</Elements>
<Route
exact
path={`${match.path}/_test`}
component={TestComponent}
/>
<Route exact path={`${match.path}`} component={ManageRootPage} />
<Route
exact
@@ -338,7 +343,6 @@ export function Manage({ match, conflict }) {
path={`${match.path}/scoreboard`}
component={Scoreboard}
/>
<Route
exact
path={`${match.path}/timetickets`}

View File

@@ -0,0 +1,93 @@
import axios from "axios";
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { setEmailOptions } from "../../redux/email/email.actions";
import { TemplateList } from "../../utils/TemplateConstants";
import {
PaymentRequestButtonElement,
useStripe,
Elements,
useElements,
} from "@stripe/react-stripe-js";
export default function MobilePaymentComponent() {
const stripe = useStripe();
const [paymentRequest, setPaymentRequest] = useState(null);
useEffect(() => {
if (stripe) {
console.log("in useeff");
const pr = stripe.paymentRequest({
country: "CA",
displayItems: [{ label: "Deductible", amount: 1 }],
currency: "cad",
total: {
label: "Demo total",
amount: 1,
},
requestPayerName: true,
requestPayerEmail: true,
});
console.log("pr", pr);
// Check the availability of the Payment Request API.
pr.canMakePayment().then((result) => {
console.log("result", result);
if (result) {
setPaymentRequest(pr);
} else {
var details = {
total: { label: "", amount: { currency: "CAD", value: "0.00" } },
};
// new PaymentRequest(
// [{ supportedMethods: ["basic-card"] }],
// {}
// // details
// ).show();
}
});
}
}, [stripe]);
if (paymentRequest) {
// paymentRequest.on("paymentmethod", async (ev) => {
// //Call server side to get the client secret
// // Confirm the PaymentIntent without handling potential next actions (yet).
// const { error: confirmError } = await stripe.confirmCardPayment(
// clientSecret,
// { payment_method: ev.paymentMethod.id },
// { handleActions: false }
// );
// if (confirmError) {
// // Report to the browser that the payment failed, prompting it to
// // re-show the payment interface, or show an error message and close
// // the payment interface.
// ev.complete("fail");
// } else {
// // Report to the browser that the confirmation was successful, prompting
// // it to close the browser payment method collection interface.
// ev.complete("success");
// // Let Stripe.js handle the rest of the payment flow.
// const { error, paymentIntent } = await stripe.confirmCardPayment(
// clientSecret
// );
// if (error) {
// // The payment failed -- ask your customer for a new payment method.
// } else {
// // The payment has succeeded.
// }
// }
// });
return (
<div style={{ height: "300px" }}>
<PaymentRequestButtonElement options={{ paymentRequest }} />
</div>
);
}
return <div></div>;
}

View File

@@ -0,0 +1,23 @@
import React from "react";
import MobilePaymentComponent from "./mobile-payment.component";
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
const stripePromise = new Promise((resolve, reject) => {
resolve(
loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
stripeAccount: "acct_1Fa7lFIEahEZW8b4",
})
);
});
export default function MobilePaymentContainer() {
return (
<div>
The mobile payment container.
<Elements stripe={stripePromise}>
<MobilePaymentComponent />
</Elements>
</div>
);
}

View File

@@ -83,6 +83,7 @@ app.post("/notifications/send", fb.sendNotification);
//Stripe Processing
var stripe = require("./server/stripe/payment");
app.post("/stripe/payment", stripe.payment);
app.post("/stripe/mobilepayment", stripe.mobile_payment);
//Tech Console
var tech = require("./server/tech/tech");

View File

@@ -44,3 +44,38 @@ exports.payment = async (req, res) => {
res.status(400).send(error);
}
};
exports.mobile_payment = async (req, res) => {
const { amount, stripe_acct_id } = req.body;
console.log("exports.payment -> amount", amount);
console.log("exports.payment -> stripe_acct_id", stripe_acct_id);
try {
await stripe.paymentIntents
.create(
{
//Pull the amounts from the payment request.
payment_method_types: ["card"],
amount: amount,
currency: "cad",
application_fee_amount: 50,
},
{
stripeAccount: stripe_acct_id,
}
)
.then(function (paymentIntent) {
try {
return res.send({
clientSecret: paymentIntent.client_secret,
});
} catch (err) {
return res.status(500).send({
error: err.message,
});
}
});
} catch (error) {
console.log("error", error);
res.status(400).send(error);
}
};