This is the first time I'm integrating stripe checkout but I keep getting stripe-signature as undefined.
For my backend I am using firebase cloud functions (without express) and for my frontend I am using react-stripe-checkout.
Is there some kind of header I need to send in order to receive that on the backend?
The only header I am sending now is:
'Content-Type': 'application/json',
backend code:
// #ts-ignore
const stripe = new Stripe('sk_test_123');
export const stripeHook = functions.https.onRequest(async (request, response) => {
cors(request, response, async () => {
const sig = request.headers['stripe-signature']; // this is always undefined
// let sig = request.get('stripe-signature'); //also tried streaming
const endpointSecret = 'whsec_123';
let event;
try {
event = stripe.webhooks.constructEvent(request.rawBody.toString('utf8'), sig, endpointSecret);
} catch (err) {
console.log(err)
response.send({status: 'error'});
return;
}
// Handle Type of webhook
const intent:any = event.data.object;
switch (event.type) {
case 'payment_intent.succeeded':
console.log("Succeeded:", intent.id);
break;
case 'payment_intent.payment_failed':
const message = intent.last_payment_error && intent.last_payment_error.message;
console.log('Failed:', intent.id, message);
break;
}
response.send({status: 'success'});
})
})
fronend code:
import React, {useState, useEffect} from 'react';
import StripeCheckout from 'react-stripe-checkout';
import { ToastContainer, toast } from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
import api from '../../services/apiMiddleware';
function Assessment(props: any) {
const [product] = React.useState({
name: "Subscription",
price: 99.99,
description: "Monthly Subscription"
});
const handleToken = async (token: string) => {
const response = await api.post(
"stripeHook",
{ token, product }
);
if (response.status === "success") {
toast("Success! Check email for details", { type: "success" });
} else {
toast("Something went wrong", { type: "error" });
}
}
return (
<div>
<div className="paymentButtonTextWrapper">
<ToastContainer />
<div className="paymentButtonWrapper">
<StripeCheckout
// #ts-ignore
token={handleToken}
stripeKey="pk_test_123"
/>
</div>
</div>
</div>
)
}
You're confusing a webhook and the route on your server that charges the token. They're entirely separate things.
Your frontend code uses (a deprecated Stripe Checkout integration, the StripeCheckout React component there is an old library using an old version of Stripe) to create a Token object representing the customer's card details. The intention then is that you POST that token to your backend and your backend route will call the Stripe API to create a charge : https://stripe.com/docs/payments/accept-a-payment-charges#web-create-charge
The actual code in your backend that you POST to though seems to not be that, it's a webhook endpoint. That's a separate concept, Stripe would send a request to you when a payment succeeds and will include that signature header. But here the request you are processing is coming from your own frontend code, it's not a weboook and it has no Stripe signature.
Related
I want to give expiry time to device token in react using firebase and also want to detect the token is refreshed so that I send the new and refresh token to my node js backend and save in MongoDB, Is there any event or procedure in firebase. I am using latest version of firebase 9.17.1 in react js
Here is firebase file
import { initializeApp } from 'firebase/app';
import { getMessaging, onMessage } from 'firebase/messaging';
import { store } from "../src/store";
const firebaseConfig = {
// configuration
};
export const app = initializeApp(firebaseConfig);
export const messaging = getMessaging(app);
// Message is listen when tab is open
export const onMessageListener = () => new Promise((resolve) => {
onMessage(messaging, (payload) => {
// console.log("mmmmmmmmmmmmmmmmmm => ", JSON.parse(payload.data.newNotification))
store.dispatch({ type: 'SET_FIREBASE_NOTIFICATIONS', payload });
console.log("store => ", store)
resolve(payload);
});
});
and in this dashboard component I call the onMessageListener
async function requestPermission() {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
// Generate Token
const token = await getToken(messaging, {
vapidKey: 'BDKvUQYoXS9kipeDmHngbfIRi-xIakvSX_236OnqFoY-EdjEt4bcuO8MNR1ujfOjk62yfp_j25QC0nPWPh-GCII',
});
dispatch(firebaseToken({ body: { firebaseToken: token }, options: { __microService: 'auth' }, }));
console.log('Token Gen', token);
// Send this token to server ( db)
} else if (permission === 'denied') {
alert('You denied for the notification');
}
}
// This is on message listner which set in firebase.js
onMessageListener().then(payload => {
// setNotification({ title: payload.notification.title, body: payload.notification.body })
console.log("xxxxxxxxxxxxx=>", payload);
// const notifications = useSelector((state) => state.notification);
console.log("kkkkkkkkkkkkkkkkkkkkkk => ", notifications)
}).catch(err => console.log('failed: ', err));
All is working fine, but I want to set the token expiry time and as well as call the on refresh token or any other method, so that I save this new token in the node js backend mongosb database and send notifications using this new token. I am using firebase-admin in node js to send the notificaitons
I need help in sending sms to user using nodejs and Gupshup.io . I'm facing difficulty understanding their documentation.
Khurram Shahzad
I work as a chatbot developer for Gupshup, please let me know what you don't understand.
Either way, here I'll share the snipet code to send an sms in node.js with our API.
const { default: axios } = require('axios');
/*
Variables to replace
YOUR_API_KEY => The unique identifier for a Gupshup account.
YOUR_APP_ID=> The unique identifier for an app. An app's appid can be found in the cURL request generated on the dashboard.
*/
const sendMessage = (msg, phone, callbackFunc) => {
const params = new URLSearchParams()
params.append('destination', phone);
params.append('message', msg);
params.append('source', 'GSDSMS'); //Required only for india
const config = {
headers: {
'apikey': YOUR_API_KEY,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
axios.post('http://api.gupshup.io/sms/v1/message/:YOUR_APP_ID', params, config)
.then((result) => {
console.log('Message sent');
callbackFunc(result.data);
})
.catch((err) => {
console.log(err);
callbackFunc(err);
})
}
module.exports = {
sendMessage
}
Need some quick guidance regarding the newsletter button in react webpage. I wanna make a newsletter button that accepts, emails from users and stores them in a spreadsheet. Basically, I need some code guidance such that when someone enters their email and clicks submit button. They will get the template email like 'Thanks for subscribing to our newsletter and company email and we get their email in the spreadsheet.
In order for this to work you would have your React.js frontend that would get the e-mail address from the user and send a request to your backend service containing that e-mail address.
This could look similar to this.
import { useState } from "react";
import "./styles.css";
function isValidEmailAddress(email) {
// validate here
return true;
}
export default function App() {
const [address, setAddress] = useState("");
async function subscribeNewsletter() {
if (isValidEmailAddress(address)) {
try {
const response = await fetch("https://your-backend/subscription", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email: address
})
});
// check if request was successful (codes 200-299)
// in this implementation we expect code "201 Created"
if (!response.ok) {
const parsed = JSON.parse(response.body);
// show error message received from backend
alert(parsed.msg);
} else {
alert("Subscribed.");
}
} catch (error) {
alert("Subscription failed");
}
}
}
return (
<div className="App">
<input onChange={(e) => setAddress(e.target.value)} value={address} />
<button onClick={() => subscribeNewsletter()}>Subscribe</button>
</div>
);
}
See CodeSandbox for a minimal working demo of the frontend.
Within your backend you have to handle the request to the /subscription endpoint. In a handler you will probably validate the email address again and then write it into a database or spreadsheet (if you really want to use spreadsheets which I would not recommend).
Last but not least you need to send the welcome email. The easiest way to do this is to use a 3rd party API or you use something like SMTP JS to send an email. What you will need in that case is a SMTP server. Have a look on this thread for more details.
The backend service could then look similar to this.
Note: this is not a perfect implementation but a skeleton that should help you getting started.
import express from "express";
// create app
var app = express();
// middleware to parse json
app.use(express.json())
app.get('/', function (req, res) {
// serve your react application
res.sendFile("index.html");
});
app.post("/subscription", function (req, res) {
const emailAddress = req.body.email;
if (!isValidEmailAddress(emailAddress)) {
// malformed email address
res.status(400).send({
msg: "email address is invalid"
})
}
insertNewSubscriber(emailAddress);
sendWelcomeEmail(emailAddress);
// resource subsription has been created
res.status(201).send();
});
// listen for request on port 3000
const port = 3000;
app.listen(port, () => {
console.log(`Listening on port ${port}`);
});
function isValidEmailAddress(email) {
// validate here
return true;
}
function insertNewSubscriber(email) {
// write to database/ file/ spreadsheet etc.
}
function sendWelcomeEmail(email) {
// send email e.g. using 3rd party API
}
I have created login form with angular 8 and node js. I have set the session using node js in back end. i couldnt check session set or not in angular for avoid access dashboard without logged in. Kindly suggest the way to use login system using angular 8 and node js. Thanks.....
A very popular method is to use JWT (JSON Web Tokens) npm package to authenticate.
The process would be:
Send credentials to the server
Server generates and sends back JWT or a Bearer Token
FrontEnd would store it in browser cookies or localStorage
localStorage.setItem('TOKEN', tokenReceivedFromServer);
In subsequent Api Calls the token would be sent to the server in a Header (Authorization).
Authorization: `JWT ${localStorage.getItem('TOKEN')}`
FYI: JWT keyword is removed from string on the server before parsing token
The frontend can check if the token is set in storage to show login page / dashboard
First we need to check the login credentials valid or not in application.
In angular application component typescript file, we have send the data service in argument, the service send the values to backend using httpclient. If credentials valid we set the value in localstorage.
submitLogin(data:any)
{
this.LoginService.loginData(data).subscribe(data =>{
if(data.body.status_code == 404)
{
Swal.fire({
icon: 'warning',
title: 'Invalid E-Mail/Password!',
}).then(function(){
});
}else if(data.body.status_code ==200)
{
localStorage.setItem("user_id",data.body.token);
this.router.navigate(['/Dashboard']);
}else
{
Swal.fire({
icon: 'error',
title: 'Process Failed!',
}).then(function(){
});
}
});
}
In service.ts file make sure about those packages import
import { HttpClient } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import {Login} from './login';
in loginData function
url = "http://localhost:3000/loginCheck";
loginData(Login:Login):Observable<any>
{
return this.http.post(this.url,Login,{observe: 'response'});
}
in backend i have used node.js
in file app.js
first install jsonwebtoken package and include in the file.
npm install jsonwebtoken
then set the jsonwebtoken when where condition satisfies
let payload = {subject:employee_id}
let token = jwt.sign(payload,'secretKey')
var response = {
'token': token,
"status_code":200,
}
res.send(response);
res.end();
Whenever we use the login in angular we must use the authguard routing its helps to access dashboard without logged in.
ng generate guard auth
in auth.guard.ts file we must include the package and service
import { CanActivate, Router } from '#angular/router';
import {LoginService} from './login.service';
export class AuthGuard implements CanActivate {
constructor(private LoginService:LoginService,private router:Router) {}
canActivate(): boolean
{
if(this.LoginService.loggedIn())
{
return true
}else
{
this.router.navigate(['/login']);
return false;
}
}
}
In this file we just checking the localstorage value set or not in boolean datatype.
in service file
add the following code for get and return in boolean type
loggedIn()
{
return !!localStorage.getItem('user_id')
}
getToken()
{
return localStorage.getItem('user_id')
}
if its returns true we can access the dasboard, else its redirected to login page.
We must use this canActive function in routing otherwise it will not working
In app-routing.module.ts file
import { AuthGuard } from './auth.guard';
const routes: Routes = [
{path:'Dashboard',component:DashboardComponent},
{path:'receipt',component:ReciptComponentComponent,canActivate:[AuthGuard]},
];
It will helpus to access dashboard without loggedin but we need to check the token valid or not in backend, we can do that using angular interceptors
we should create the new service with interceptors name
ng g service token-interceptor
In interceptor file we need to import the following
import { Injectable,Injector } from '#angular/core';
import { HttpInterceptor } from '#angular/common/http';
import { LoginService } from './login.service';
In interceptors services inject in different way compared to component.
export class TokenInterceptorService implements HttpInterceptor{
constructor(private Injector:Injector) { }
intercept(req:any,next:any)
{
let loginService = this.Injector.get(LoginService);
let tokenzedReq = req.clone({
setHeaders:
{
Authorization: `Bearer ${loginService.getToken()}`
}
});
return next.handle(tokenzedReq)
}
}
we need to create a function in interceptors with the name intercept, then we need to inject the service as per injector.
In backend we need to create the helper function to verify the jsonwebtoken
if the authorization not set we can send the response 401 not found and can redirected to login page
function verifyToken(req,res,next)
{
if(!req.headers.authorization)
{
return res.status(401).send('Unauthorized request');
}
var token = req.headers.authorization.split(' ')[1];
if(!token)
{
return res.status(401).send('Unauthorized request');
}
if(token === 'null')
{
return res.status(401).send('Unauthorized request');
}
//let payload = jwt.verify(token,'secretKey');
let payload = jwt.decode(token,'secretKey');
if(!payload)
{
return res.status(401).send('Unauthorized request');
}
req.userId = payload.subject;
next();
}
then we can use this middleware function wherever we need
for example
app.get('/dashboard',verifyToken,function(req,res){
let events = [];
res.json(events);
});
In dashboard component ts file
this.dashboardService.getData().subscribe(data=>this.dashboardData=data,
err=>{
if(err instanceof HttpErrorResponse)
{
if(err.status===401)
{
this.router.navigate(['/login']);
}
}
})
in dashboard service ts file
url = "http://localhost:3000/dashboard";
getData()
{
return this.http.get<any>(this.url);
}
in app.module.ts file
import { AuthGuard } from './auth.guard';
import { ReciptComponentComponent } from './recipt-component/recipt-component.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { TokenInterceptorService } from './token-interceptor.service';
import { DashboardServiceService } from './dashboard-service.service';
in providers
providers: [AuthGuard,{provide:HTTP_INTERCEPTORS,useClass:TokenInterceptorService,multi:true},DashboardServiceService],
Current situation
I am developing nodejs backend server and vue frontend application, which is run under different port(localhost:3000 and localhost:8080). With purpose to enable CORS connection, I configured devServer proxy from vue.config.js file.
vue.config.js
module.exports = {
devServer: {
proxy: {
'/users': {
target: 'http://127.0.0.1:3000/users',
changeOrigin: true,
pathRewrite: {
'^/users':''
}
},
'/tasks': {
target: 'http://127.0.0.1:3000/tasks',
changeOrigin: true,
pathRewrite: {
'^/tasks': ''
}
}
}
},
outputDir: '../backend/public'
}
and technically used cors.js to enable request to backend server, which was implemented by expressjs.
I am sending the request with vue component to retrieve data from backend server. It works properly from fetching data from server, and my goal is to make the same behavior when I reload page. However, whenever I reload same page, it keep showing 401 http response status set by the backend code written by myself.
enter image description here
Research and Trial til now
Before I go on the attempts I have tried, I should introduce mandatory codes to be operated at first. Somehow this is at least explanations in which vuex actions using axios, axios using backend routers eventually.
tasks.module.js
import axios from "axios"
import authHeader from '../../services/auth-header'
export const tasks = {
state: {
tasks: []
},
getters: {
allTasks: (state) => state.tasks
},
actions: {
async fetchTasks({ commit }) {
const response = await axios.get('http://127.0.0.1:3000/tasks', {headers: authHeader()})
commit('setTasks', response.data)
axios.defaults.headers.common['Authorization'] = authHeader()
},
async addTask({ commit }, description) {
const response = await axios.post('http://127.0.0.1:3000/tasks', { description, completed: false}, {headers: authHeader()})
commit('newTask', response.data)
},
async updateTask({ commit }, updTask) {
const response = await axios.patch('http://127.0.0.1:3000/tasks/'+updTask.id, updTask, {headers: authHeader()})
commit('updateTask', response.data)
}
},
mutations: {
setTasks: (state, tasks) => (state.tasks = tasks),
newTask: (state, task) => state.tasks.unshift(task),
updateTask: (state, updTask) => {
let updates = Object.keys(updTask)
updates.forEach((update) => {
state.task[update] = updTask[update]
})
}
}
}
TaskManager.vue
<template>
<div class="container">
<div class="jumbotron">
<h3>Task Manager</h3>
<AddTask/>
<Tasks/>
</div>
</div>
</template>
<script>
import Tasks from './components/Tasks'
import AddTask from './components/AddTask'
export default {
name:'TaskManager',
components: {
Tasks,
AddTask
}
}
</script>
Tasks.vue
<template>
<div>
<div>
<div class="legend">
<span>Double click to mark as complete</span>
<span>
<span class="incomplete-box"></span> = Incomplete
</span>
<span>
<span class="complete-box"></span> = Complete
</span>
</div>
</div>
<div class="tasks">
<div
#dblclick="onDblClick(task)"
v-for="task in allTasks"
:key="task.id"
class="task"
v-bind:class="{'is-completed':task.completed}">
{{task.description}}
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
name: "Tasks",
methods:{
...mapActions(['fetchTasks', 'updateTask']),
onDblClick(task) {
const updTask = {
id: task._id,
description: task.description,
completed: !task.completed
}
console.log(updTask)
this.updateTask(updTask)
}
},
computed: {
...mapGetters(['allTasks']),
},
created() {
this.fetchTasks()
}
}
Now I need to introduce what I have tried to solve problems
Configuring CORS options
Since this error page didnt show any authorization header which was supposed to set in request header I figured out the way I enabled cors connection and I believe this enables preflight request. Here is what I configured middleware behavior from backend code.
task.js(express router file)
const router = new express.Router()
const auth = require('../middleware/auth')
const cors = require('cors')
const corsOptions = {
origin: 'http://127.0.0.1:3000',
allowedHeaders: 'content-Type, Authorization',
maxAge:3166950
}
router.options(cors(corsOptions))
router.get('/tasks', auth, async (req, res) => {
const match = {}
const sort = {}
if(req.query.completed) {
match.completed = req.query.completed === 'true'
}
if(req.query.sortBy) {
const parts = req.query.sortBy.split('_')
sort[parts[0]] = parts[1] === 'desc' ? -1:1 // bracket notation
}
try {
await req.user.populate({
path: 'tasks',
match,
options: {
limit: parseInt(req.query.limit),
skip: parseInt(req.query.skip),
sort
}
}).execPopulate()
console.log(req.user.tasks)
res.status(200).send(req.user.tasks)
} catch (e) {
res.status(500).send(e)
}
})
module.exports = router
auth.js(middleware)
const jwt = require('jsonwebtoken')
const User = require('../models/user')
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ','')
const decoded = jwt.verify(token, 'thisisnewcourse')
console.log('decoded token passed')
const user = await User.findOne({ _id: decoded._id, 'tokens.token': token})
console.log('user found')
if(!user) {
throw new Error()
}
req.token = token
req.user = user
next()
} catch (error) {
console.log('error caught')
res.status(401).send({error: 'please authenticate'})
}
}
module.exports = auth
Set Authorization header as axios default header after login
auth.module.js(since login works correctly, I am copying only login action part)
actions: {
async login ({ commit }, user){
try {
const response = await axios.post('http://127.0.0.1:3000/users/login', user)
if(response.data.token){
localStorage.setItem('user', JSON.stringify(response.data))
axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.token}`
}
commit('loginSuccess', response.data)
} catch (e) {
console.log(e)
}
}
Middleware chaining on the express route(cors, auth)
I have tried two different middleware on the same backend code(task.js)
router.get('/tasks', [cors(corsOptions), auth], async (req, res) => {
// same as previously attached code
}
Now I believe referring to another post with similar issue will help me out however it's about having CORS enabled, not the issue that the header is not sent via either preflight request or other type of requests.
You haven't included the code for authHeader but I assume it just returns the value of the Authorization header.
This bit looks suspicious to me:
async fetchTasks({ commit }) {
const response = await axios.get('http://127.0.0.1:3000/tasks', {headers: authHeader()})
commit('setTasks', response.data)
axios.defaults.headers.common['Authorization'] = authHeader()
},
The final line seems to be trying to set the Authorization header globally so that it will be included on all subsequent axios requests. That's fine but it seems strange not to do that sooner. You have a similar line inside the login action, which makes sense, but I assume that isn't being called when the page is refreshed.
Then there's this bit:
{headers: authHeader()}
If authHeader returns the value of the Authorization header then this won't work. Instead you need:
{headers: { Authorization: authHeader() }}
Ideally you wouldn't need to set any headers here and instead you'd just set the global header before attempting this request.
While it isn't the direct cause of your problem, you seem to have got your wires crossed about CORS. You've configured a proxy, which means you aren't using CORS. The request you're making is to the same origin, so CORS doesn't apply. You don't need to include CORS response headers if you aren't making a cross-origin request. If you do want to make a cross-origin request then don't use the proxy. You should try to mimic your production environment during development, so if you intend to use CORS in production you should use it during development. Otherwise, stick with the proxy.