User Guide Redacted
User Guide Redacted
Purpose: This guide will include details about the tech stack, user and admin features, a
database diagram, a known bug, and customization instructions.
Description: This website allows customers to subscribe to the cybersecurity services of our
client Sam Abdul. The landing page includes the pricing table from Stripe, services offered, and
testimonials from happy customers.
● Node.js + Expres.js
● Bcrypt: Used to hash passwords before being stored in our database, 10 salt rounds.
● Calendly: Used to book consultation appointments + webhook for the database.
● Express rate-limit: To prevent DOS and brute force attacks
● Express session: Used for our customers/admins to log in with a session
● Google Cloud Storage node package, multer node package, and the bucket
configuration to store uploaded files from the customer/admin portal and download them
from there. See “Google Cloud Storage bucket setup” below for more details.
● MySQL: Database used.
● Nodemailer: Used to send emails for forgotten passwords. We created a sender email
[email protected] password: , but this can be replaced
if you want the verification code to be sent by a different email. See “Updating
Nodemailer Google Account” below for more details.
● OAuth: Gives our users the ability to log in without a password with Google. See “Google
OAuth setup” below for more details.
● Passport: Part of the OAuth integration.
● Stripe: Payment processing, pricing table, customer portal, billing sessions, including
signature verification to prevent malicious actors from spoofing requests.
User Features
A customer can register with us using either an email and password or via Google
OAuth. Once registered they can access the customer portal, which will show the pricing table
again but this time it will be interactable. When they choose to subscribe Stripe will create a
billing session where the customer can enter their payment information securely handled by
Stripe. Their card details will NEVER be stored on our database or even interact with our server.
After a successful checkout, the users table will be updated with the subscription information via
the webhook. The customer will be redirected back to our customer portal where they will have
access to new features.
A customer can click “Manage Subscription” which will take them to the Stripe customer
portal, where they can either change the subscription level, change payment options, view
invoice history, cancel their subscription, or return to our customer portal. If they choose to
change their subscription our database will reflect that change as well as the features that are
available on our customer portal. A canceled subscription will still have access to the features
until the end of the billing cycle.
A customer may click “Schedule a Consultation” which will open the Calendly embedded
widget. This widget is tied to Sam’s email address and Calendly options such as availability, and
event type. After making a selection of meeting type, the user is directed to a calendar where
they can see available dates and times to meet with Sam. After confirming a timeslot, the user’s
email address is automatically filled in and they can enter any useful information about their
appointment. On completion, the database is updated and the appointment is added to both the
customer's and Sam’s calendars. The customer is sent a confirmation email where they can
either cancel or reschedule the appointment, both of these events are tracked in our database.
A cancellation will not count against the customer for the number of appointments booked.
The customer can also choose from a dropdown list of requests, the list is populated with
an array inside the customerPortal.ejs file. These requests vary depending on the level of
subscription. The user may enter details about their request and upload a supporting document
if they choose. After submitting the request the portal will be rendered to show the request on
the list of pending requests. After an admin completes the request it will be listed in a paginated
list of completed requests. As of 12/4, these requests are not constrained by any limits, a user
can place as many requests as they desire. As of 12/8, these requests are now limited by the
subscription level (view customization instructions for more info).
Admin Features
To register as an admin a user will need to get a code from our database in addition to
an email and password. The admin has access to all of the customers' submitted requests and
can filter to specify a particular user. Pending requests show on top of completed requests and
the filter applies to both. The admin can download the file that the user shared as part of their
request and add feedback before pressing the complete button. At this point, the request moves
from Pending to Completed for both the admin and the customer.
Database Diagram
The users table is the primary table containing everything related to the customer. Most columns
are self-explanatory but the subscription_duration_months is set to either 3 or 12 depending on
the subscription duration (quarter vs annual). This is used to determine the number of
consultation appointments that the user is allowed to make. As of 12/4/2023, the user is allowed
to book either 1 or 5 appointments per month depending on their subscription level. This is
currently hard-coded but could be changed to query a table with the respective values.
The consultations table includes a status column, the two expected values are ‘active’ or
‘canceled’. If the user decides to cancel an appointment via their confirmation email it will show
in the database as canceled and not count against them for the number of consultations they
have available.
Known bug: The Calendly widget will automatically populate the user's email address but this
field is not locked and the user may change their email address. If they enter an email address
that does not match, our database will store the appointment and it will show up on Sam's
calendar. However, this appointment will NOT count against the customer and they could
potentially schedule unlimited appointments. The Calendly support team told me there is no way
to lock that field at this time. It could be possible to create a custom widget but I believe that is a
project in itself. ~Kevin
Customization Instructions
Most Stripe related changes should be handled in the Stripe dashboard. Product
descriptions, feature lists, pricing tables, and pricing options. There are also customizable
subscription and email settings for various events at this link:
https://dashboard.stripe.com/settings/billing/automatic. Any changes made to the current pricing
table “prctbl_1OHJDWGuIql9Y6sUPIKC9kRh” will automatically be applied to the project.
Should you ever need to create a new pricing table you can follow the instructions and replace
the old pricing table ID with the new one on both the customer portal and the landing page EJS
files. On the creation of a new pricing table, you will need to update the helper function
getSubscriptionLevelFromPlanId with new values for each product.
The embedded Calendly widget is also customizable through the Calendly account.
Under settings, you can add, remove, or edit appointment event types. As of 12/6/2023, there
are two options on the Calendly widget Discovery Call and a 30-minute consultation. I believe all
options are available to all subscription levels, it may be possible to render a different widget but
that may require a secondary Calendly account.
The request type dropdown menu for customers is currently being populated by an array
near the bottom of the customerPortal.ejs file.
To change the number of consultations and requests allowed per subscription type you
can change the Heroku config vars. MONTHLY_CONSULTATIONS_BUSINESS: is set to 5 and
MONTHLY_CONSULTATIONS_STARTER: is set to 1 MONTHLY_REQUESTS_BUSINESS is
set to 12 and MONTHLY_REQUESTS_STARTER is set to 8.
For the custom payment option on our pricing table, instead of creating a billing session,
the user is prompted to call a placeholder phone number xxx-xxx-xxxx. This can be set up to an
email instead. After sending an email to Sam and working out what “Custom” means in this
case, a payment link will need to be created on the Stripe dashboard.
https://dashboard.stripe.com/payment-links/plink 1OKZBfGuIql9Y6sUWzGIlPua This link is for
a “Custom 1” plan at $500/3months. There is a Copy link button on this page that you can send
to a user manually via email. This is a placeholder Custom plan that is set up in our project,
however, if you want to create a more accurate custom plan will need to follow these
instructions.
1. Create a new product on the Stripe dashboard.
2. Create a new payment link and attach the new product to it.
3. Update the function getSubscriptionLevelFromPlanId in the server.js file.
4. Update the functions calculateAvailableRequests and calculateAvailableConsultations
again in the server.js file, there is an if block that will need the new product “name” given
in step 3.
5. Update the script in customerPortal.ejs file, there is a declared const subscriptionOptions
object that contains the request types that are available to that subscription level.
My recommendation for setting up custom types would be to create 2-5 different “custom” levels
all at the same time. This way instead of having to update code in multiple places and redeploy
you can simply share the payment link with them. I would give them names like “Custom 1-5”,
you must ensure that the string returned by getSubscriptionLevelFromPlanId matches the
calculateAvailable functions and the subscriptionOptions.
The Heroku deployment is currently set up to auto-deploy the main branch whenever a
change is pushed to that branch. It takes 30-90 seconds for Heroku to rebuild the project and
deploy it to the web. If you want to test changes locally I recommend using the GitHub desktop
app and VS Code. Once the app is set with access to the repository you can select the branch
you want to make changes to and press open in Visual Studio Code. In the PowerShell terminal
you should see the file location and to make sure your environment has the correct versions of
various dependencies type in npm i. To run the program type npm run dev, after a moment you
should be able to open http://localhost:8080/ in your web browser. If you want to test anything
related to Stripe locally you have to have the Stripe CLI and change the variable endpointSecret
= process.env.STRIPE_WEBHOOK_SECRET_LIVE to STRIPE_WEBHOOK_SECRET_TEST
. You can access the Stripe CLI with the Windows command prompt. Navigate to the directory
where the project is located ex (cd "Desktop\CSUMBstuff\CST 489 Capstone Project") then run
stripe listen --forward-to localhost:8080/webhook. This will allow the local instance to interact
with Stripe events. Testing the Calendly webhook must be done on a deployed internet version
like Heroku or using ngrok. Any changes you make to the server.js file will cause the site to
reboot, but changes to the routes .ejs files should reflect on a page refresh.
If you want to change the email address that sends the verification codes to users, you
will need to update the environment variables in the Heroku app settings and look for the
“Reveal Config Vars” button: https://dashboard.heroku.com/apps/cybersecurity0/settings
Once you click the button and expand the list of config vars, look for the environment
variable NODEMAILER_USER and update it with your preferred Google account username (the
text before the @), and for the password, update the environment variable
NODEMAILER_PASSWORD with the following password that you will obtain from here:
Go to your Google account settings https://myaccount.google.com/ and in the “search
Google account” search bar look for “App passwords”. Once there, type the name of the app in
the prompt and click Create. You should then see a popup box with a generated password,
make sure to store that password somewhere secure (leave the spaces in the password),
especially when copying and pasting it over to your NODEMAILER_PASSWORD environment
variable.
If you decide to have more control over the OAuth or decide to publish the app, you will
need to create your own OAuth credentials using your preferred Google account or the
company’s account. Follow these steps:
1. Create a Google Developer Project:
However, if you choose to extend the GCS bucket, then creating a bucket involves the
following:
2. Select/Create a Project: Create a new project or select an existing project where you
want to enable Google Cloud Storage.
3. Navigate to the IAM & Admin section: Click on the "IAM & Admin" menu from the
left-hand side.
4. Select Service Accounts: In the IAM & Admin section, select "Service Accounts" from the
menu.
5. Create a New Service Account: Click on the "Create Service Account" button at the top
of the page.