Added elements for mobile payments. BOD-90
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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`}
|
||||
|
||||
93
client/src/pages/mobile-payment/mobile-payment.component.jsx
Normal file
93
client/src/pages/mobile-payment/mobile-payment.component.jsx
Normal 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>;
|
||||
}
|
||||
23
client/src/pages/mobile-payment/mobile-payment.container.jsx
Normal file
23
client/src/pages/mobile-payment/mobile-payment.container.jsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user