Stripe Connect firebase functions for creating seller account - flutter

I'm using Firebase functions for creating seller account but I don't know how to create seller account and what to put in the redirect_url
I followed some tutorials and wrote the below code
Let me know what changes should I do to open seller account registration with url_launcher
Thanks
const stripeAccount = functions.https.onRequest(async (req, res) => {
const { method } = req
if (method === "GET") {
// CREATE CONNECTED ACCOUNT
const { mobile } = req.query
const account = await stripe.accounts.create({
type: "express",
})
const accountLinks = await stripe.accountLinks.create({
account: account.id,
refresh_url:, <-- What to put here
return_url:, <-- What to put here
type: "account_onboarding",
})
if (mobile) {
// In case of request generated from the flutter app, return a json response
res.status(200).json({ success: true, url: accountLinks.url })
} else {
// In case of request generated from the web app, redirect
res.redirect(accountLinks.url)
}
} else if (method === "DELETE") {
// Delete the Connected Account having provided ID
const {
query: { id },
} = req
console.log(id)
const deleted = await stripe.accounts.del(id)
res.status(200).json({ message: "account deleted successfully", deleted })
} else if (method === "POST") {
// Retrieve the Connected Account for the provided ID
// I know it shouldn't be a POST call. Don't judge :D I had a lot on my plate
const account = await stripe.accounts.retrieve(req.query.id)
res.status(200).json({ account })
}
const stripeReAuth = async (req, res) => {
const { account_id: accountId } = req.query
const accountLinks = await stripe.accountLinks.create({
account: accountId,
refresh_url: <-- Here
return_url: , <-- Here
type: "account_onboarding",
})
res.redirect(accountLinks.url)
}
})
This is my flutter code, I'm retrieving the return_url and launching it with url_launcher
class StripeBackendService {
static String apiBase = '{function address}/stripeAccount';
static String createAccountUrl =
'$apiBase/account?mobile=true';
static String checkoutSessionUrl =
'${StripeBackendService.apiBase}/checkout-session?mobile=true';
static Map<String, String> headers = {'Content-Type': 'application/json'};
void createSellerAccount() async {
var url = Uri.parse(StripeBackendService.createAccountUrl);
var response = await http.get(url, headers: StripeBackendService.headers);
Map<String, dynamic> body = jsonDecode(response.body.toString());
await canLaunch(body['url']) ? await launch(body['url']) : throw 'Error'
}
}

The refresh url should point to an address that retries the creation of the stripe connect account, in case your current http function returns an expired link. The return url is the address that the potential stripe connect user gets sent to after the stripe onboarding is complete. In knowing that address you can use the webview controller to jump back to the app when reaching that return-url endpoint.

Related

How to get current user ID with Next JS and next-auth within an API route?

I am trying to get the current user ID to push it in the creation of a document with mongodb.
I have created a specific APi route which get the data from a form.
However, I cannot use useSession to get session.user.id in an API route as I can in a basic react component. so how should I proceed to retrieve the current user ID?
This is the current code of the api/companies/create.js:
import { MongoClient } from "mongodb";
// import clientPromise from "../../../lib/mongodb";
async function handler(req, res) {
if (req.method === "POST") {
const { name, bio, size, location, logo, website, industry } = req.body;
// | (bio.trim() === "")
// BACKEND VALIDATION
if (!name || name.trim() === "") {
res.status(422).json({ message: "Invalid input." });
return;
}
// Storing it in the database
const newCompany = {
name,
size,
bio,
location,
logo,
website,
industry,
};
let client;
try {
// const client = await clientPromise;
client = await MongoClient.connect(process.env.MONGODB_URI);
} catch (error) {
res.status(500).json({ message: "Could not connect to database." });
return;
}
const db = client.db("main");
try {
const result = await db.collection("companies").insertOne(newCompany);
// Not sure about that line:
// newCompany.id = result.insertedId;
} catch (error) {
client.close();
res.status(500).json({ message: "Storing message failed!" });
return;
}
client.close();
res.status(201).json({ message: "Sucessfuly stored company" });
}
}
export default handler;
This is from: https://next-auth.js.org/configuration/nextjs
This is how I get a session on the server side in API routes
import type { NextApiRequest, NextApiResponse } from 'next'
import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "../auth/[...nextauth]"
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const session = await unstable_getServerSession(req, res, authOptions)
// you can now use session.data
I think NextAuth ultimately wants you to use middleware to control certain API routes and pass session related data to API functionality.

Error [ERR_HTTP_HEADERS_SENT] : Can't figure out the multipe requests

I have this error : Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client. From my understanding, the problem is that I am trying to send more than one response to the same http request. My instinct tell me that it’s this part that messes up :
catch (err) {
res.status(400).json(err);
}
Because if no user/password found in the DB, we already send status(400). Am I right ? More importantly (and that’s what drives me crazy), I am following a YT tuto and his code is exactly like mine, yet his seems to be working without any problem.
My code :
const router = require("express").Router();
const User = require("../models/Users");
const bcrypt = require("bcrypt");
//LOGIN
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ username: req.body.username });
!user && res.status(400).json("Wrong credentials!");
const validated = await bcrypt.compare(req.body.password, user.password);
!validated && res.status(400).json("Wrong credentiaaaals!");
const { password, ...others } = user._doc;
res.status(200).json(others);
} catch (err) {
res.status(500).json(err);
}
});
module.exports = router;
His code :
//LOGIN
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ username: req.body.username });
!user && res.status(400).json("Wrong credentials!");
const validated = await bcrypt.compare(req.body.password, user.password);
!validated && res.status(400).json("Wrong credentials!");
const { password, ...others } = user._doc;
res.status(200).json(others);
} catch (err) {
res.status(500).json(err);
}
});
module.exports = router;
Am I doing something wrong ? Is my reflexion bad ? Thanks !
You are right, your code is trying to send data to the client multiple times. The issue is that after the call .json("Wrong credentials!") completed, the write stream to the client will be closed, and you will not be able to send any other data to the client. The framework knows to detect it and show you the bug.
In your code, after the method .json("Wrong credentials!") finishes own execution, your program will continue and will try to execute the next lines...
You just need to add return, so the program will exit the current flow after it sends the response to the client.
const router = require("express").Router();
const User = require("../models/Users");
const bcrypt = require("bcrypt");
//LOGIN
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ username: req.body.username });
if (!user) {
return res.status(400).json("Wrong credentials!"); // without return the code will continue to execute next lines
}
const validated = await bcrypt.compare(req.body.password, user.password);
if (!validated) {
return res.status(400).json("Wrong credentiaaaals!"); // without return the code will continue to execute next lines
}
const { password, ...others } = user._doc;
res.status(200).json(others); // return is not necessary, because there is no cod which will be executed after we back from the json method
} catch (err) {
res.status(500).json(err); // return is not necessary, because there is no cod which will be executed after we back from the json method
}
});
module.exports = router;

Paypal Order API capture payment in Angular and NestJS

My stack is NestJS and Angular12, I am using the OrderAPI v2.
I succesfully implemented the order flow using an SDK button, but, since I have several payment systems that are activated by a single button "Pay now", I need to avoid SDK in my front end. Follows the methods I use to create and capture payments, and they works with the SDK button.
async createOrder(value: number): Promise<any> {
const accessToken = await this.generateAccessToken();
const url = this.baseUrl+`/v2/checkout/orders`;
const body = {
intent: "CAPTURE",
return_url: process.env.CLIENT+"/success",
purchase_units: [
{
amount: {
currency_code: "EUR",
value: value.toFixed(2)
}
}
]
}
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`
}
const obs = this.httpService.post(url, JSON.stringify(body),{headers: headers});
const response = await firstValueFrom(obs);
return response.data;
}
async capturePayment(order: CreateOrderDto, orderId: string): Promise<any> {
const accessToken = await this.generateAccessToken();
const url = this.baseUrl+`/v2/checkout/orders/${orderId}/capture`;
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`
}
const obs = this.httpService.post(
url,
{},
{
headers: headers
}
)
const response = await firstValueFrom(obs);
if (response.data.success) await this.orderService.createOrder(order)
return response.data;
}
When calling the createOrder() function I return the url of the approvation, and in my Front-end I do the redirect to the approve page of Paypal. The problem is that when approving the transaction on the approve url the user is shown a infinite loading page.
Is there something I am missing?

Integration Testing Nodejs/Express/Mongoose with Jest/Supertest One Mongoose Model Saves, One Mongoose Model Doesn't

I use Postman and the ReactJS UI to call this registration execution and it works as I expect. Ironically, the Jest and Supertest integration tests do not produce expected results. When integration testing, the Profile is created and the User is not.
The architecture is pretty simple. MongoDB in a Docker container, and Node using nodemon in VSCode.
I have to be doing something wrong, I just can't spot what it is.
// The Integration Test __test__/users/../user.test.js
const app = require('../../app');
const uuidv4 = require('uuid/v4');
const User = require('../../src/models/User');
const Profile = require('../../src/models/Profile');
const bcrypt = require('bcryptjs');
const mongoose = require('mongoose');
const request = require("supertest");
const {
MONGO_URI,
TEST_DB_NAME
} = process.env;
let DB_URI = MONGO_URI + TEST_DB_NAME;
let NAME = TEST_DB_NAME;
mongoose.connect(DB_URI, {
useNewUrlParser: true,
useCreateIndex: true,
dbName: NAME
});
describe('User Integration Test', () => {
// make sure app is imported without issues
it('Has App Defined', () => {
expect(app).toBeDefined();
});
let server;
beforeAll(async () => {
// Clear Test Data
await User.deleteMany({});
await Profile.deleteMany({});
server = await app.listen(3001);
});
afterAll(async (done) => {
// Clear Test Data
await User.deleteMany({});
await Profile.deleteMany({});
// Close server
await server.close(done);
});
describe('User route tests', () => {
it('Can Register a User', async () => {
const body = {
"username": "User21",
"email": "user21#user.com",
"password": "123456",
"avatar": "image.jpg"
}
await request(server)
.post('/api/v1/users')
.send(body)
.set('Accept', 'application/json')
.set('Content-Type', 'application/json')
.expect(200)
});
});
// THE EXPRESS ROUTE in api/v1/users.js
const express = require('express');
const auth = require('../../middleware/auth');
const router = express.Router();
const { UserService } = require('../../services');
const {
check,
validationResult
} = require('express-validator/check');
// #route POST api/users
// #desc Register User
// #access Public
// #return status message
router.post('/', [
check('email', 'Please provide a valid email address').isEmail(),
check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 }),
check('username', 'Username is Required.').not().isEmpty()
], async (req, res, next) => {
try {
//--Validate
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
errors: errors.array()
});
}
const message = await UserService.register(req.body);
return res.status(200).json(message)
} catch (err) {
next(err);
}
});
// THE register METHOD found in ../../services/UserService.js
const register = async (data) => {
try {
// Destructure the data
const {
username,
email,
password,
avatar
} = data;
// remove spaces from username and lcase it
let user_name = username.replace(/\s/g, '').toLowerCase();
// Check if the username or email already exists
await doesUserExist(user_name, email);
// Create a new user
const token = uuidv4();
user = new User({
email: email.toLowerCase(),
username: user_name,
avatar: avatar,
verifyEmailToken: token
});
// encrypt the password
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
// Save the user
// (Works Unless Running Jest Integration Tests)
await user.save();
// Create and save an empty Profile for the new user
profile = new Profile();
profile.user = user;
// (Always Works)
await profile.save();
// Send verification email
await send(user, 'Verify Your Email', token, 'verify-email.html');
return { message: 'User was registered successfully.' };
} catch (err) {
throw err;
}
}
// Does user exist method found in ./UserService.js
const doesUserExist = async (username, email) => {
// Check if user exists by email
let message = await checkEmail(email);
if (!message.email_available) {
throw new Error('Email already exists');
}
// Check if user exists by username
message = await checkUserName(username.toLowerCase())
if (!message.username_available) {
throw new Error('Username already exists');
}
return false;
}
When I call this code via the UI, Postman, or curl both the User and Profile are created, as expected.
When I run the Integration Test, npm run test:integration or npm test,
Only the Profile is created.
my package.json scripts:
"test": "jest",
"test:integration": "jest --testPathPattern integration.test",
Finally, no errors are reported anywhere. User simply isn't created.
After a few hours of work and testing this issue I found that the afterAll() event was raise randomly. Sometimes after all the tests in the inner describe() ran and sometimes not. Of course, when afterAll() ran all Users where deleted from the data store.
If moved beforeAll() and AfterAll() to the inner describe(). This worked very well until I included other integration tests, like authorization, for example that also used the User table. I noticed that the test suites are not executed synchronously. As one test suit was blowing out the data in another test suite as the other test suite was executing.
I now set up a database per test suite. Clunky, wonky, hacky and wrong I know but I have to have move on. Does anyone know if you can control the synchronous and/or asynchronous behavior of Jest? Please don't suggest Mocha and/or Chai.
I ran into a similar issue, where there appeared to be an intermittent race condition between seeding a document and then retrieving it.
I fixed it by running jest with the --runInBand flag.

How can save data using cloud functions to firestore?

I saw some other answers and I tried some of those unsuccessfully.
In my case, I created an iOS app and integrated Stripe payment method, so it reaches my javascript function at cloud functions. I'm actually able to see the payments I realizes within my stripe account but I couldn't manage to save it into our firestore database.
this is my setup at Google side:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const express = require('express');
const cors = require('cors')({origin: true});
const app = express();
const stripe = require('stripe')(functions.config().stripe.token);
//I've preset this data to firebase functions middleware
function charge(req, res) {
const body = (req.body);
const userId = body.userId;
const token = body.token;
const amount = body.amount;
const currency = body.currency;
// Charge card
stripe.charges.create({
amount,
currency,
description: 'Firebase Example',
source: token,
}).then(charge => {
send(res, 200, {
// I WANNA RECORD DATA INTO MY DATABASE HERE!!
message: 'Success',
charge,
});
}).catch(err => {
console.log(err);
send(res, 500, {
error: err.message,
});
});
}
function send(res, code, body) {
res.send({
statusCode: code,
body: JSON.stringify(body),
});
}
app.use(cors);
app.post('/', (req, res) => {
// Catch any unexpected errors to prevent crashing
try {
charge(req, res);
} catch(e) {
console.log(e);
send(res, 500, {
error: `The server received an unexpected error. Please
try again and contact the site admin if the error persists.`,
});
}
});
exports.charge = functions.https.onRequest(app);
And this is our database setup where I want to save like this:
- into payments > userId... I'll save each transaction this userId does with the fields: "token", "currency" and "amount".
Note: I already have all this values in my function and also have userId.
enter image description here