Skip to content

Commit

Permalink
Merge pull request stripe#7 from mattbasta/e4b
Browse files Browse the repository at this point in the history
Express for Business support
  • Loading branch information
romain-stripe authored Oct 13, 2017
2 parents 3099a94 + f25d961 commit 3ef60a1
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 21 deletions.
38 changes: 30 additions & 8 deletions server/models/pilot.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ mongoose.Promise = global.Promise;
const PilotSchema = new Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true },

type: { type: String, default: 'individual', enum: ['individual', 'company'] },

firstName: String,
lastName: String,
address: String,
Expand All @@ -24,11 +27,21 @@ const PilotSchema = new Schema({
license: String,
color: String
},
businessName: String,

// Stripe account ID to send payments obtained with Stripe Connect.
stripeAccountId: String
stripeAccountId: String,
});

// Return a pilot name for display.
PilotSchema.methods.displayName = function() {
if (this.type === 'company') {
return this.businessName;
} else {
return `${this.firstName} ${this.lastName}`;
}
};

// List rides of the past week for the pilot.
PilotSchema.methods.listRecentRides = function() {
const weekAgo = Date.now() - (7*24*60*60*1000);
Expand Down Expand Up @@ -63,23 +76,32 @@ PilotSchema.statics.getLatestOnboarded = function() {
};

// Make sure the email has not been used.
PilotSchema.path('email').validate(function (email, callback) {
PilotSchema.path('email').validate(function(email, callback) {
const Pilot = mongoose.model('Pilot');

// Check only when it is a new pilot or when the email has been modified.
if (this.isNew || this.isModified('email')) {
Pilot.find({ email: email }).exec(function (err, pilots) {
Pilot.find({ email: email }).exec(function(err, pilots) {
callback(!err && pilots.length === 0);
});
} else callback(true);
}, 'This email already exists. Please try to login instead.');

// Pre-save hook making sure the password is hashed before being stored.
PilotSchema.pre('save', function (next) {
if (!this.isModified('password')) {
return next();
// Pre-save hook to ensure consistency.
PilotSchema.pre('save', function(next) {
// Make sure certain fields are blank depending on the pilot type.
if (this.isModified('type')) {
if (this.type === 'individual') {
this.businessName = null;
} else {
this.firstName = null;
this.lastName = null;
}
}
// Make sure the password is hashed before being stored.
if (this.isModified('password')) {
this.password = this.generateHash(this.password);
}
this.password = this.generateHash(this.password);
next();
});

Expand Down
18 changes: 18 additions & 0 deletions server/public/scripts/onboarding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
document.body.addEventListener('change', function(e) {
if (e.target.name !== 'pilot-type') {
return;
}
// Show the correct header for legal entity information.
document.querySelector('.type-header.is-visible').classList.toggle('is-visible');
document.querySelector(`.type-header.${e.target.value}-info`).classList.add('is-visible');

// Show any fields that apply to the new pilot type.
Array.prototype.slice.call(
document.querySelectorAll('.pilot-info')
).forEach(function(elem) {
elem.classList.toggle(
'is-visible',
elem.classList.contains(`${e.target.value}-type`)
);
});
});
73 changes: 71 additions & 2 deletions server/public/stylesheets/rocketrides.css
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ fieldset .row.date span {

fieldset .row.date input {
flex: 0;
min-width: 35px;
min-width: 35px;
}

fieldset .row.date input:last-child {
Expand Down Expand Up @@ -650,6 +650,75 @@ form a.button {
margin: 0;
}

/* Onboarding */

.pilot-options {
margin-bottom: 20px;
}
.pilot-options .pilot-option {
align-items: center;
cursor: pointer;
display: inline-flex;
padding: 0;
}
.pilot-option input {
appearance: none;
-moz-appearance: none;
-ms-appearance: none;
-webkit-appearance: none;
height: 0;
width: 0;
}
.pilot-option input + span {
align-items: center;
display: flex;
font-size: 15px;
padding-left: 20px;
position: relative;
}
.pilot-option input + span:before {
background: #fff;
border-radius: 30px;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.15);
content: "";
display: inline-block;
flex: 0 0 30px;
height: 30px;
left: -20px;
position: relative;
width: 30px;
}
.pilot-option input + span:after {
align-items: center;
bottom: 0;
content: url(/images/checkmark-green.svg);
display: flex;
height: 13px;
left: 0;
margin: auto;
opacity: 0;
position: absolute;
right: calc(100% - 30px);
top: 0;
transform: scale(0.7);
transition: transform 0.2s, opacity 0.2s;
width: 13px;
}
.pilot-option input:checked + span:after {
opacity: 1;
transform: scale(1);
}

.type-header,
.row.pilot-info {
display: none;
}
.type-header.is-visible,
.pilot-info.is-visible {
display: flex;
}


/* Ride History */

.rides {
Expand Down Expand Up @@ -923,7 +992,7 @@ form a.button {
}
}

@keyframes slide-in-vertical {
@keyframes slide-in-vertical {
0% {
transform: translateY(50px);
opacity: 0;
Expand Down
2 changes: 1 addition & 1 deletion server/routes/api/rides.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ router.post('/', async (req, res, next) => {

// Return the ride info.
res.send({
pilot_name: [pilot.firstName, pilot.lastName].join(' '),
pilot_name: pilot.displayName(),
pilot_vehicle: pilot.rocket.model,
pilot_license: pilot.rocket.license,
});
Expand Down
16 changes: 13 additions & 3 deletions server/routes/pilots/pilots.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ router.get('/signup', (req, res) => {
let step = 'account';
// Naive way to check which step we're on via presence of profile data.
if (req.user) {
if (!req.user.firstName || !req.user.lastName) {
if (
req.user.type === 'individual' ?
!req.user.firstName || !req.user.lastName :
!req.user.businessName
) {
step = 'profile';
} else if (!req.user.stripeAccountId) {
step = 'payments'
Expand All @@ -120,11 +124,17 @@ router.get('/signup', (req, res) => {
* Create a user and update profile information during the pilot onboarding process.
*/
router.post('/signup', (req, res, next) => {
const body = Object.assign({}, req.body, {
// Use `type` instead of `pilot-type` for saving to the DB.
type: req.body['pilot-type'],
'pilot-type': undefined,
});

// Check if we have a logged-in pilot.
let pilot = req.user;
if (!pilot) {
// Try to create and save a new pilot.
pilot = new Pilot(req.body);
pilot = new Pilot(body);
pilot.save((err, pilot) => {
if (err) {
// Show an error message to the user.
Expand All @@ -140,7 +150,7 @@ router.post('/signup', (req, res, next) => {
});
} else {
// Try to update the logged-in pilot with the newly entered profile data.
pilot.set(req.body);
pilot.set(body);
pilot.save((err, pilot) => {
if (err) next(err);
return res.redirect('/pilots/signup');
Expand Down
5 changes: 3 additions & 2 deletions server/routes/pilots/stripe.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ router.get('/authorize', pilotRequired, (req, res) => {
// Optionally, Stripe Connect accepts `first_name`, `last_name`, `email`,
// and `phone` in the query parameters for them to be autofilled.
parameters = Object.assign(parameters, {
first_name: req.user.firstName,
last_name: req.user.lastName,
'stripe_user[business_type]': req.user.type === 'company' ? 'company' : undefined,
first_name: req.user.firstName || undefined,
last_name: req.user.lastName || undefined,
email: req.user.email
});
// Redirect to Stripe to start the Connect onboarding.
Expand Down
2 changes: 1 addition & 1 deletion server/views/dashboard.pug
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ block header
.block.profile
.photo
p Pilot since #{moment(pilot.created).format('MMMM YYYY')}
h1 #{pilot.firstName} #{pilot.lastName}
h1 #{pilot.displayName()}
.rating
.block.week
p This Week
Expand Down
2 changes: 2 additions & 0 deletions server/views/layout.pug
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ html

footer
block footer

script(src='/scripts/onboarding.js')
20 changes: 16 additions & 4 deletions server/views/signup.pug
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,23 @@ block content

when 'profile'
form(method='POST', autocomplete='off')
h4 Personal Information
h4 Type of Pilot
.pilot-options
label.pilot-option
input(type='radio', name='pilot-type', value='individual', id='po-individual', checked=true)
span Individual
label.pilot-option
input(type='radio', name='pilot-type', value='company', id='po-company')
span Corporation / LLC

h4.type-header.individual-info.is-visible Personal Information
h4.type-header.company-info Company Information
fieldset
.row(label='* First Name')
.row.pilot-info.company-type(label='* Biz Name')
input(type='text', name='businessName', placeholder='Infinity and Beyond LLC')
.row.pilot-info.individual-type.is-visible(label='* First Name')
input(type='text', name='firstName', placeholder='Jane')
.row(label='* Last Name')
.row.pilot-info.individual-type.is-visible(label='* Last Name')
input(type='text', name='lastName', placeholder='Diaz')
.row(label='Address')
input(type='text', name='address', placeholder='185 Berry St', value='185 Berry St')
Expand Down Expand Up @@ -62,7 +74,7 @@ block content

when 'payments'
h2 Connect your Rocket Rides account to Stripe
p We use Stripe to make sure you get paid on time and keep your personal and bank details secure.
p We use Stripe to make sure you get paid on time and keep your personal and bank details secure.
a.button(href='/pilots/stripe/authorize') Setup Payments on Stripe

when 'done'
Expand Down

0 comments on commit 3ef60a1

Please sign in to comment.