Skip to content

Commit

Permalink
feat: billing features
Browse files Browse the repository at this point in the history
  • Loading branch information
rostyslavvnahornyi authored and rahulkgupta committed Jun 18, 2024
1 parent d007458 commit 44f1195
Show file tree
Hide file tree
Showing 18 changed files with 2,015 additions and 581 deletions.
1,962 changes: 1,552 additions & 410 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,23 @@
"dependencies": {
"@fortawesome/free-regular-svg-icons": "^6.5.2",
"@react-google-maps/api": "^2.19.3",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
"@stripe/stripe-js": "^3.4.1",
"aframe-atlas-uvs-component": "^3.0.0",
"classnames": "^2.3.2",
"clipboard": "^2.0.11",
"date-fns": "^2.30.0",
"firebase": "^9.23.0",
"firebase-admin": "^12.1.1",
"firebase-functions": "^5.0.1",
"lodash-es": "^4.17.21",
"posthog-js": "^1.138.3",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-ga4": "^2.1.0",
"react-select": "^5.4.0",
"stripe": "^15.8.0",
"three": "0.145.0",
"uuid": "^9.0.0"
},
Expand Down
3 changes: 1 addition & 2 deletions public/firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
".git",
"firebase-debug.log",
"firebase-debug.*.log"
],
"predeploy": ["npm --prefix \"$RESOURCE_DIR\" run lint"]
]
}
],
"emulators": {
Expand Down
39 changes: 39 additions & 0 deletions public/functions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,42 @@ exports.getScene = functions.https.onRequest(async (req, res) => {
res.status(500).send({ error: 'Error retrieving scene' });
}
});

exports.createStripeSession = functions.https.onCall(async (data, context) => {
const stripe = require('stripe')(
'sk_test_51PAsDFP2BZd7kkhqseXWoZnLwoKiuTwL4u7LAnkGJeUpTFy2YducfwlSq6YhuBaB5eZUpc9ZNsyhIZZAQFnrIlGb00GAZp2S4h'
);

const session = await stripe.checkout.sessions.create(data);

return {
id: session.id
};
});

exports.stripeWebhook = functions.https.onRequest(async (req, res) => {
const stripe = require('stripe')(
'pk_test_51PAsDFP2BZd7kkhq6uIm5LRHQQCR2qBppnVwMA1vAokzkgjlngXgAgfaz1jexz1IbqoE2WjEQSWxjTpdeDNeJZSP00PqhX34fp'
);
let event;

try {
event = stripe.webhooks.constructEvent(
req.rawBody,
req.headers['stripe-signature'],
'whsec_L7OLhcNHHiQ7dHbQiz0ad0j1cOFCKcQZ'
);
} catch (err) {
console.error('⚠️ Webhook signature verification failed.');
return res.send(err).sendStatus(400);
}

const dataObject = event.data.object;
await admin.firestore().collection('orders').doc().set({
checkoutSessionId: dataObject.id,
paymentStatus: dataObject.payment_status,
userId: dataObject.metadata.userId
});

return res.sendStatus(200);
});
3 changes: 2 additions & 1 deletion public/functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"main": "index.js",
"dependencies": {
"firebase-admin": "^10.0.2",
"firebase-functions": "^3.18.0"
"firebase-functions": "^3.18.0",
"stripe": "^8.4.0"
},
"devDependencies": {
"eslint": "^8.9.0",
Expand Down
29 changes: 24 additions & 5 deletions src/editor/components/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import { LoadScript } from '@react-google-maps/api';
import { GeoModal } from './modals/GeoModal';
import { ActionBar } from './components/ActionBar';
import { ScenesModal } from './modals/ScenesModal';
import { PaymentModal } from './modals/PaymentModal';
import { SceneEditTitle } from './components/SceneEditTitle';
import { AddLayerPanel } from './components/AddLayerPanel';
THREE.ImageUtils.crossOrigin = '';

const isStreetLoaded = window.location.hash.length;
const isPaymentModalOpened = window.location.hash.includes('/modal/payment');

export default class Main extends Component {
constructor(props) {
Expand All @@ -35,6 +37,8 @@ export default class Main extends Component {
isAddLayerPanelOpen: false,
isGeoModalOpened: false,
isScenesModalOpened: !isStreetLoaded,
isPaymentModalOpened: isPaymentModalOpened,
isScenesModalOpened: !isStreetLoaded && !isPaymentModalOpened,
sceneEl: AFRAME.scenes[0],
visible: {
scenegraph: true,
Expand Down Expand Up @@ -115,6 +119,9 @@ export default class Main extends Component {
});
Events.on('opengeomodal', () => {
this.setState({ isGeoModalOpened: true });
})
Events.on('openpaymentmodel', () => {
this.setState({ isPaymentModalOpened: true });
});
}

Expand Down Expand Up @@ -153,6 +160,9 @@ export default class Main extends Component {

onCloseGeoModal = () => {
this.setState({ isGeoModalOpened: false });

onClosePaymentModal = () => {
this.setState({ isPaymentModalOpened: false });
};

toggleEdit = () => {
Expand Down Expand Up @@ -243,6 +253,10 @@ export default class Main extends Component {
isOpen={this.state.isSignInModalOpened}
onClose={this.onCloseSignInModal}
/>
<PaymentModal
isOpen={this.state.isPaymentModalOpened}
onClose={this.onClosePaymentModal}
/>
<ScenesModal
isOpen={this.state.isScenesModalOpened}
onClose={this.onCloseScenesModal}
Expand All @@ -267,11 +281,7 @@ export default class Main extends Component {
selectedTexture={this.state.selectedTexture}
onClose={this.onModalTextureOnClose}
/>
{this.state.inspectorEnabled && (
<div id="help">
<HelpButton />
</div>
)}

{this.state.inspectorEnabled && (
<div id="geo">
<GeoPanel />
Expand Down Expand Up @@ -300,7 +310,16 @@ export default class Main extends Component {
<Compass32Icon />
</Button>
)}
<<<<<<< HEAD
{this.state.inspectorEnabled && this.state.isAddLayerPanelOpen && (
=======
{this.state.inspectorEnabled && (
<div id="layerWithCategory">
<AddLayerButton onClick={this.toggleAddLayerPanel} />
</div>
)}
{this.state.isAddLayerPanelOpen && (
>>>>>>> c80f984 (feat: billing features)
<AddLayerPanel
onClose={this.toggleAddLayerPanel}
isAddLayerPanelOpen={this.state.isAddLayerPanelOpen}
Expand Down
4 changes: 3 additions & 1 deletion src/editor/components/components/Button/Button.component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ const Button = ({
disabled,
id,
leadingicon,
trailingicon
trailingicon,
...props
}) => (
<button
className={classNames(styles.buttonWrapper, variants[variant], className)}
Expand All @@ -47,6 +48,7 @@ const Button = ({
id={id}
data-leadingicon={leadingicon}
data-trailingicon={trailingicon}
{...props}
>
{leadingicon && <div className={styles.icon}>{leadingicon}</div>}
{children}
Expand Down
119 changes: 119 additions & 0 deletions src/editor/components/modals/PaymentModal/PaymentModal.component.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { httpsCallable } from 'firebase/functions';
import React from 'react';
import styles from './PaymentModal.module.scss';

import { loadStripe } from '@stripe/stripe-js';
import PaymentPlaceholderImg from '../../../../../ui_assets/payment-placeholder.png';
import { useAuthContext } from '../../../contexts/index.js';
import { CheckMark32Icon } from '../../../icons/icons.jsx';
import { Button } from '../../components/index.js';
import Modal from '../Modal.jsx';
import { functions } from '../../../services/firebase.js';

let stripePromise;
const getStripe = () => {
if (!stripePromise) {
stripePromise = loadStripe(
'pk_test_51PAsDFP2BZd7kkhq6uIm5LRHQQCR2qBppnVwMA1vAokzkgjlngXgAgfaz1jexz1IbqoE2WjEQSWxjTpdeDNeJZSP00PqhX34fp'
);
}

return stripePromise;
};

const PaymentModal = ({ isOpen, onClose }) => {
const { currentUser } = useAuthContext();

return (
<Modal
className={styles.modalWrapper}
isOpen={isOpen}
onClose={onClose}
extraCloseKeyCode={72}
>
<div className={styles.paymentDetails}>
<h3>Unlock Geospatial Features with 3DStreet Pro</h3>
<h2>
Create with geospatial maps and share your vision in augmented reality
with 3DStreet Pro.
</h2>
<ul>
<li>
<CheckMark32Icon /> All features in Free
</li>
<li>
<CheckMark32Icon />
Google Maps 3D Tiles
</li>
<li>
<CheckMark32Icon />
Augmented Reality (QR Anchor)
</li>
<li>
<CheckMark32Icon />
Custom SVG Paths
</li>
<li>
<CheckMark32Icon />
Mapbox Layers
</li>
</ul>
<Button className={styles.learnMoreBtn} variant="ghost">
Learn more
</Button>
</div>
<div className={styles.rightCol}>
{currentUser ? (
<>
<img
className={styles.paymentPlaceholder}
src={PaymentPlaceholderImg}
/>
{currentUser.isPremium ? (
<CheckMark32Icon />
) : (
<Button
onClick={async () => {
try {
const {
data: { id }
} = await httpsCallable(
functions,
'createStripeSession'
)({
line_items: [
{ price: 'price_1PAsLrP2BZd7kkhqoVPaJOT3', quantity: 1 }
],
mode: 'subscription',
success_url: `${location.origin}/#/modal/payment`,
cancel_url: `${location.origin}/#/modal/payment`,
metadata: {
userId: currentUser.uid
}
});

const stripe = await getStripe();
await stripe.redirectToCheckout({ sessionId: id });
} catch (error) {
console.log(error);
}
}}
className={styles.checkoutWithBtn}
variant="filled"
>
Checkout with Stripe
</Button>
)}
</>
) : (
<div className={styles.unAuth}>
<p>To upgrade you have to sign in:</p>
<Button variant="filled">Sign in to 3DStreet Cloud</Button>
</div>
)}
</div>
</Modal>
);
};

export { PaymentModal };
84 changes: 84 additions & 0 deletions src/editor/components/modals/PaymentModal/PaymentModal.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
@use '../../../style/variables.scss';

.modalWrapper {
& > div {
display: flex;
row-gap: 64px;
}

.paymentDetails {
display: flex;
flex-direction: column;
max-width: 460px;

ul {
list-style-type: none;
margin-top: 32px;
}

li {
display: flex;
align-items: center;
column-gap: 16px;

svg {
width: 24px;
height: 24px;

& {
stroke: #0eaf00;
fill: #0eaf00;
}
}
}

.learnMoreBtn {
padding: 0 !important;
margin-top: 40px;
}
}

.rightCol {
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 380px;
height: 350px;

& > svg {
position: absolute;
width: 100%;
height: 100%;

* {
stroke: #0eaf00;
fill: #0eaf00;
}
}

.checkoutWithBtn {
position: absolute;
}

.paymentPlaceholder {
width: 100%;
height: 100%;
border-radius: 12px;
opacity: 0.3;
}

.unAuth {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
row-gap: 16px;

& > p {
font-size: 24px !important;
font-weight: 500;
}
}
}
}
Loading

0 comments on commit 44f1195

Please sign in to comment.