Configuring and testing Stripe Accounts Create for Stripe custom - node.js

I am trying to configure stripe.accounts.create({}) for Stripe custom. My goal here is to create everything in one form so the user fulfills all of the information requirements for their stripe account to transact after the form is compete. When testing the current code using the credit card number Stripe recommended, I am getting the error that is displayed after the following code block. I am wondering if there is a tokenization process that I am missing that isn't referenced in the stripe create account docs. This is my current post method
var knex = require("../models/knex"),
express = require('express'),
middleware = require("../middleware/index"),
stripe = require("stripe")("sk_test_VALUEOFMYTESTKEY"),
router = express.Router({mergeParams:true});
router.post("/formuser",function(req,res){
console.log(req.user[0].user_id);
knex("users.user").select("*").where("user_id",req.user[0].user_id)
.then((user) => {
var today = new Date(Date.now()).toLocaleString();
var accountType = String(req.body.accountType).toLowerCase();
var checkIfCard = accountType=="card";
console.log(req.body.accountType,checkIfCard,String(req.body.cardNumber));
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
console.log(ip);
if(!checkIfCard){
stripe.accounts.create({
email: user.email,
country: "US",
type: "custom",
//Required fields for Custom via... https://stripe.com/docs/connect/required-verification-information
metadata: {
"external_account": {
"object": "bank_account",
"exp_month": req.body.cardExpirationMonth,
"exp_year": req.body.cardExpirationYear,// : null,
"number": req.body.bankNumber,// : null,
}, //external account info... https://stripe.com/docs/api#account_create_bank_account
"city": req.body.city,
"legal_entity.adress.line1": req.body.streetAddress,
"legal_entity.address.postal_code": req.body.zipCode,
"legal_entity.address.state": req.body.state,
"legal_entity.dob.day": req.body.birthDay,
"legal_entity.dob.month": req.body.birthMonth,
"legal_entity.dob.year": req.body.birthYear,
"legal_entity.first_name": req.body.firstName,
"legal_entity.last_name": req.body.lastName,
"legal_entity.ssn_last_4": req.body.ssn_last_4,
"tos_acceptance.date": today,
"tos_acceptance.ip": ip,
}
}).then((acct) => {
res.redirect("/");
})
.catch((e) => {
console.log(e);
});
} else {
stripe.accounts.create({
email: user.email,
country: "US",
type: "custom",
//Required fields for Custom via... https://stripe.com/docs/connect/required-verification-information
metadata: {
"external_account": {
"object": "card", //bank account or cc or dc...
"card": req.body.cardNumber.toString(),
"cvc" : req.body.cvc.toString(),
"currency" : "usd",// : null
}, //external account info... https://stripe.com/docs/api#account_create_bank_account
"city": req.body.city,
"legal_entity.adress.line1": req.body.streetAddress,
"legal_entity.address.postal_code": req.body.zipCode,
"legal_entity.address.state": req.body.state,
"legal_entity.dob.day": req.body.birthDay,
"legal_entity.dob.month": req.body.birthMonth,
"legal_entity.dob.year": req.body.birthYear,
"legal_entity.first_name": req.body.firstName,
"legal_entity.last_name": req.body.lastName,
"legal_entity.ssn_last_4": req.body.ssn_last_4,
"tos_acceptance.date": today,
"tos_acceptance.ip": ip,
}
}).then((acct) => {
res.redirect("/");
})
.catch((e) => {
console.log(e);
});
}});
});
When I enter in the credit card information that Stripe recommends to test, I get the following error
{ [Error: Invalid val: {"object"=>"card", "card"=>"4242 4242 4242 4242", "cvc"=>"111", "currency"=>"usd"} must be a string under 500 characters]
type: 'StripeInvalidRequestError',
stack: 'Error: Invalid val: {"object"=>"card", "card"=>"4242 4242 4242 4242", "cvc"=>"111", "currency"=>"usd"} must be a string under 500 character
when I expected a user to be created.
EDIT: I removed some of the knex database code in this post to shorten it's length as it is not relevant to the current error. The current error is specifically from Stripe's promise.

Your code is trying to pass bank account details in external_account but also passing card data at the same time. This is unlikely to be what you want.
On top of this, you should not be passing this information server-side at all as it's sensitive. Instead, you should be creating a token client-side. For card data, you would use Elements and for bank account data you would build your own form and tokenize with Stripe.js. Once this is done, you get a card token tok_123 or a bank account token btok_123 and can then use this server-side in the external_account parameter.
Then, you should also pass the data as nested hashes. This means that you would not pass "legal_entity.adress.line1" but instead legal_entity[address][line1]. Your code should instead look something like this:
stripe.accounts.create(
{
type: 'custom',
country: 'US',
legal_entity : {
first_name : 'john',
last_name : 'doe',
type : 'individual',
address: {
line1: 'line1',
city: 'city',
state: 'state',
postal_code: '90210',
country: 'US'
}
},
external_account: 'tok_visa_debit',
}).then((acct) => {
console.log('account: ', JSON.stringify(acct));
}).catch((e) => {
console.log(e);
});

Related

How do I add expenses for only specific users?

I have created two models in my app- one for User (_id, email, username, password) and one for Expense (_id, date, detail, amount, category). For the users, I have finished the authentication with jwt.
I want logged-in users to be able to add/remove expenses and not show their expenses to other users but I don't know how I can implement that. I am not asking for code- I would be grateful if you could roughly tell me what I need to do. Thanks!
//expense schema
const expenseSchema = new mongoose.Schema(
{
date: Date,
detail: String,
amount: Number,
category: String
}
)
//controller for adding expenses
const addExpenseController = (req, res) => {
const expense = new Expense({
"date": new Date(),
"amount": req.body.amount,
"detail": req.body.detail,
"category": "expense"
});
expense.save();
res.send('expense added');
};
You should define a ref property in the expense schema pointing at the User model (change the value of the ref attribute to equal the model name given to the users):
const expenseSchema = new mongoose.Schema(
{
...
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
}
)
Then, on creation, specify the user by setting the value of its _id.
You can either store it in the session or pass it in the body, depending on your implementation:
const addExpenseController = async (req, res) => {
try {
const expense = new Expense({
date: new Date(),
amount: req.body.amount,
detail: req.body.detail,
category: 'expense',
user: req.session.user_id, // or req.body.user_id
});
await expense.save();
res.send('expense added');
} catch (err) {
res.send('server error');
}
};

Maching ids doesn't return true when compared

I'm trying to make some comprobations on my API (Node.js + Mongodb)
I want to check if the proposerId is equal to the eventOrganizer. To do so
I'm sending this on the body:
{
"participantId": "6238a608170aff10d16ccd89",
"proposerId": "62385d8caee17d13a1762b39", // this guy id is also an organizer
"gender": "female",
"groupId": "623aea21fcfad83bcf8d5bc4"
}
in my PATCH controller to add a user I have this verification:
exports.addParticipantToEvent = async (req, res, next) => {
// Group Organizer constants
const eventId = req.params.eventId;
const groupId = req.body.groupId;
const proposerId = req.body.proposerId; // it can be an admin adding someone to the participants list
// Participant constants
const participantId = req.body.participantId;
const gender = req.body.gender;
// EVENT
// Does the event even exist?
const eventData = await Event.findById(eventId);
if (!eventData) {
return res.status(406).json({
code: 'EVENT_DOESNT_EXIST',
message: 'The event is not valid.',
});
}
console.log(eventData);
// What kind of users can participate in this event?
const allowedParticipants = eventData.allowedParticipants;
// whos the event organizer?
const eventOrganizer = eventData.organizer._id;
console.log('Organizer: ' + eventOrganizer);
console.log('Proposer: ' + proposerId);
console.log('Result: ' + proposerId === eventOrganizer);
try {
return res.status(200).json({
message: 'The participant can be added',
participantId: participantId,
gender: gender,
allowedParticipants: allowedParticipants,
});
} catch (err) {
return res.status(400).json({ message: err });
}
};
I want to verify is the proposerId is an admin or an organizer of the event, so I console.log the eventData entirely and later I console log all the constants I want to verify and the result, it gives me false all the time.
Maybe I need to specify better something?
{
location: { province: 'Barcelona' },
_id: new ObjectId("634ffee75990124926431e6f"),
title: 'Test open close 14',
sport: new ObjectId("622ce6ca672c3d4447676705"),
group: new ObjectId("623aea21fcfad83bcf8d5bc4"),
organizer: new ObjectId("62385d8caee17d13a1762b39"),
participants: [ new ObjectId("62385d8caee17d13a1762b39") ],
replacements: [],
invitations: [],
when: 2022-10-09T13:43:02.999Z,
open: true,
costPerParticipant: 4.4,
skill: 'novice',
allowedGender: 'female',
minParticipants: 2,
maxParticipants: 5,
visible: false,
externalLink: 'https://www.komoot.es/tour/731122050?ref=wta',
allowInvitations: true,
allowReplacements: false,
allowedParticipants: 'only-members',
createdAt: 2022-10-19T13:43:03.006Z,
updatedAt: 2022-10-19T13:43:03.006Z,
__v: 0
}
Organizer: 62385d8caee17d13a1762b39
Proposer: 62385d8caee17d13a1762b39
false
As you can see, both Organizer and proposer are same id, yet I get false.
After lurking some more, I have found that to validate mongos object ids with strings I need to use equals(). So now I have the solution.

Firebase: how to get getAdditionalUserInfo in onCreate Firebase Cloud function

I'm trying to get some additional data from a SAML login provider. I can see this data in the client but I fail to see how to get this in the back end (firebase functions).
I'm using these in the FE
"firebase": "^9.8.2",
"firebase-functions": "^3.14.1",
And this in the BE
"firebase-admin": "^10.2.0",
"firebase-functions": "^3.21.2",
This is the data and how I get it in the client:
async myproviderSignIn() {
const provider = new SAMLAuthProvider('saml.myprovider');
const auth = getAuth();
const userCredential = await signInWithPopup(auth, provider);
const credential = SAMLAuthProvider.credentialFromResult(userCredential);
if (!environment.production) {
console.log('User:', userCredential, credential);
console.log(
'getAdditionalUserInfo:',
getAdditionalUserInfo(userCredential)
);
}
}
This is what I'm after and logged by getAdditionalUserInfo in the client:
{
"isNewUser": false,
"providerId": "saml.myprovider",
"profile": {
"urn:schac:attribute-def:schacPersonalUniqueCode": "urn:schac:personalUniqueCode:nl:local:diy.myproviderconext.nl:studentid:123456",
"urn:oid:1.3.6.1.4.1.25178.1.2.9": "diy.myproviderconext.nl",
"urn:oid:1.3.6.1.4.1.25178.1.2.14": "urn:schac:personalUniqueCode:nl:local:diy.myproviderconext.nl:studentid:123456",
"urn:oid:0.9.2342.19200300.100.1.3": "student1#diy.myproviderconext.nl",
"urn:oid:1.3.6.1.4.1.5923.1.1.1.1": [
"student",
"employee",
"staff",
"member"
],
"urn:mace:dir:attribute-def:eduPersonAffiliation": [
"student",
"employee",
"staff",
"member"
],
"urn:mace:dir:attribute-def:sn": "One",
"urn:mace:dir:attribute-def:givenName": "Student",
"urn:oid:2.5.4.42": "Student",
"urn:mace:dir:attribute-def:mail": "student1#diy.myproviderconext.nl",
"urn:oid:2.5.4.4": "One",
"urn:mace:terena.org:attribute-def:schacHomeOrganization": "diy.myproviderconext.nl"
}
}
Finally this is my BE on user create trigger. It creates a DB record of the user when a new user is created in Firebase auth. I'd wish to map some of the properties shown above here to the user record in the DB.
export const onCreate = functions.auth
.user()
.onCreate((user: UserRecord, context: EventContext) => {
const timestamp = serverTimestamp();
const dbUser: DbUser = {
uid: user.uid,
name: user.displayName || '',
firstName: user.displayName || '',
lastName: '',
email: user.email,
photoURL: user.photoURL,
emailVerified: user.emailVerified,
createdDate: timestamp,
lastSeen: timestamp,
providerData: JSON.parse(JSON.stringify(user.providerData)),
userDump: JSON.parse(JSON.stringify(user)),
contextDump: JSON.parse(JSON.stringify(context)),
};
// Get additional user data from the UserCredential
// const additionalUserInfo = getAdditionalUserInfo(user); ???
const result = getFirestore()
.collection(constants.dbCollections.users)
.doc(user.uid)
.set(dbUser);
return result;
});
How do I access these additional properties in my cloud function without relying on the client?
the problem you try to solve is something explained in here:
https://firebase.google.com/docs/auth/admin/custom-claims#node.js
some of the data you described below is stored within the auth.user
const dbUser: DbUser = {
uid: user.uid,
name: user.displayName || '',
firstName: user.displayName || '',
lastName: '',
email: user.email,
photoURL: user.photoURL,
emailVerified: user.emailVerified,
createdDate: timestamp,<-----------------------------------------------here
lastSeen: timestamp,<--------------------------------------------------and here
providerData: JSON.parse(JSON.stringify(user.providerData)),
userDump: JSON.parse(JSON.stringify(user)),
contextDump: JSON.parse(JSON.stringify(context)),
};
but the data i marked as "here" needs to be created via "custom user claims" but its under the FireBaseAnalytics tab neither auth. nor firestore if you insist to retrive this data from Firebase.
But for this data :
providerData: JSON.parse(JSON.stringify(user.providerData)),
userDump: JSON.parse(JSON.stringify(user)),
contextDump: JSON.parse(JSON.stringify(context)),
you should use FireStore or FirebaseRealTimeDatabase i repeat if you insist of getting those data from FireBase because of the 1000byte policy of Custom Claims.
i hope i do understand your question well. But im sure if you add those properties with this
// Set admin privilege on the user corresponding to uid.
getAuth()
.setCustomUserClaims(uid, { admin: true })
.then(() => {
// The new custom claims will propagate to the user's ID token the
// next time a new one is issued.
});
you can retrive it with .auth token

Missing Stripe Connect Custom Account Information for Node.js

Currently working on server side info for Stripe connect and I'm having issues figuring out the correct parameters for creating a stripe connect account. I'm stuck on tax_id (ein) at the moment. Can someone please give me the correct abbreviation for this any others I may be missing on Node.js.
For example when it came to the name of the business Stripes API just say "name". Yet I kept getting the error 'Received unknown parameters: name'. But after some help looking online I saw that the correct way was to write out 'business_name' even through this was no where in the Stripe API.
here is my code: Hope this helps guide some people in the right direction as well (:
app.post('/newConnectCompanyAcct', (req, res) => {
// Creating New Connect Account...
//User
var company = req.body.business_name
var ein = req.body.business_tax_id
var first = req.body.first_name
var last = req.body.last_name
var email = req.body.email
var phone = req.body.phone
var birthDay = req.body.birthDay
var birthMonth = req.body.birthMonth
var birthYear = req.body.birthYear
var ssn = req.body.personal_id_number
var time = req.body.Time
var ip = req.body.iP
//Acct Type
var type = req.body.type
//Address
var line1 = req.body.line1
var line2 = req.body.line2
var zipcode = req.body.postal_code
var city = req.body.city
var state = req.body.state
var lastFour = req.body.lastFour
console.log('the email ' + email);
console.log('the phone ' + phone);
stripe.accounts.create({
type: "custom",
country: 'US',
requested_capabilities: ['card_payments','transfers'],
product_description: "example description",
mcc: "7299",
business_name: company,
tax_id: ein,
legal_entity: {
address: {
line1: line1,
line2: line2,
postal_code: zipcode,
city: city,
state: state
},
// relationship: {
// representative: true,
// title: "Manager"
// },
dob: {
day: birthDay,
month: birthMonth,
year: birthYear
},
first_name: first,
last_name: last,
type: type,
personal_phone_number: phone,
personal_email: email,
ssn_last_4: lastFour,
personal_id_number: ssn,
personal_address: {
line1: line1,
line2: line2,
postal_code: zipcode,
city: city,
state: state
}
},
tos_acceptance: {
date: Math.floor(Date.now() / 1000),
ip: ip
}
}).then((accounts) => {
console.log(accounts)
// Send customerId -> Save this for later use
return res.status(200).send(accounts.id)
}).catch((error) => {
console.log('error while creating new account' + error)
return res.status(500).send(JSON.stringify({ success: false, error: error }))
});
});

Node + Mailchimp NPM: How to add a subscriber to a list and include their first and last name?

Mailchimp is almost a perfect company, except their Node API documentation is non-existent. How can I add a subscriber to my new list and include their first name and last name? The code below successfully adds the subscriber, but first and last names are not being added.
var MCapi = require('mailchimp-api');
MC = new MCapi.Mailchimp('***********************-us3');
addUserToMailchimp = function(user, callback) {
var merge_vars = [
{ EMAIL: user.email },
{ LNAME: user.name.substring(user.name.lastIndexOf(" ")+1) },
{ FNAME: user.name.split(' ')[0] }
];
MC.lists.subscribe({id: '1af87a08af', email:{email: user.email}, merge_vars: merge_vars, double_optin: false }, function(data) {
console.log(data);
}, function(error) {
console.log(error);
});
}; // addUserToMailchimp
The supplied merge variables should be passed as a single object, not an array of objects. Please see my example below:
var mcReq = {
id: 'mail-chimp-list-id',
email: { email: 'subscriber-email-address' },
merge_vars: {
EMAIL: 'subscriber-email-address',
FNAME: 'subscriber-first-name',
LNAME: 'subscriber-last-name'
}
};
// submit subscription request to mail chimp
mc.lists.subscribe(mcReq, function(data) {
console.log(data);
}, function(error) {
console.log(error);
});
It looks like you supplied your actual mail chimp API key in your question. If so, you should remove it immediately.

Resources