i am using nodeJS with mongoDB.
i am using a get request to load the checkout form and then a post request to create a paymentIntent and load the stripe checkout form. then the form is submitted using a javascript file on the checkout page which then sends the user to a success page if it was successful, where would be the best place to save the purchase to the database and update the user to add it to their purchases, if i do it on the post request, it is too early and saves it before the user has a chance to pay.
routes
router.get('/pay/:id', catchAsync (async(req, res, next) => {
if (!req.isAuthenticated()){
req.flash('sir', 'you must be signed in')
return res.redirect('/account/sign-in')
}
try{
const { id } = req.params
const user = req.user._id
const concert = await Concert.findById(id).populate('artist')
const artistId = concert.artist
const artist = await Artist.findById(artistId)
const foundUser = await User.findById(user)
const user_purchases = foundUser.purchased_content.toString()
const concert_id = concert.id
if(!concert || concert.artist.banned === 'true' || concert.visibility === 'private') {
// return next(new AppError('page not found'))
req.flash('paynf', 'sorry, the concert you are looking for could not be found')
return res.redirect('/posts')
}
console.log(artist.stripe_id)
res.render('pay', { concert, foundUser})
}catch(e){
console.log(e.name)
req.flash('paynf', 'sorry, the concert you are looking for could not be found')
res.redirect('/posts')
}
}))
router.post('/pay/:id', catchAsync (async(req, res, next) => {
if (!req.isAuthenticated()){
req.flash('sir', 'you must be signed in')
return res.redirect('/account/sign-in')
}
try{
const { id } = req.params;
const user = req.user._id
const concert = await Concert.findById(id).populate('artist')
const concert_id = concert.id
const artistId = concert.artist
const artist = await Artist.findById(artistId)
const stripe_id = artist.stripe_id
const foundUser = await User.findById(user)
if(!concert || concert.artist.banned === 'true' || concert.visibility === 'private') {
// return next(new AppError('page not found'))
req.flash('paynf', 'sorry, the concert you are looking for could not be found')
return res.redirect('/posts')
}
const purchase = new Purchase(req.body)
const customer = ({
id: foundUser.cus_id,
name: 'john',
email: foundUser.email
});
// Create a PaymentIntent with the order amount and currency
const paymentIntent = await stripe.paymentIntents.create({
customer: customer.id,
amount: concert.price*100,
description: `${concert.title} by ${concert.artName}`,
currency: "gbp",
receipt_email: customer.email,
automatic_payment_methods: {
enabled: true,
},
application_fee_amount: Math.round(concert.price*100*0.35),
transfer_data: {
destination: artist.stripe_id,
},
});
res.send({
clientSecret: paymentIntent.client_secret,
});
}
catch(e){
console.log(e)
return res.send('/posts')
}}))
javascript for checkout page:
const stripe = Stripe("pk_test_51KJaaVEnftAKbusC8A9kTrtzrLKklZDHQdserQ2ZrYMHRqFRfbMk9SrGVnQlLoSjIfqmOCOEsDmcsTnO0evWY2Pr00nNd02KLv");
let elements;
initialize();
checkStatus();
document
.querySelector("#payment-form")
.addEventListener("submit", handleSubmit);
// Fetches a payment intent and captures the client secret
async function initialize() {
const response = await fetch(window.location.href, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items }),
});
const { clientSecret } = await response.json();
const appearance = {
theme: 'stripe'
}
elements = stripe.elements({ appearance, clientSecret });
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-element");
}
async function handleSubmit(e) {
e.preventDefault();
setLoading(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
// Make sure to change this to your payment completion page
return_url: "http://localhost:3000/success",
receipt_email: 'virtconcerts#gmail.com'
},
});
// This point will only be reached if there is an immediate error when
// confirming the payment. Otherwise, your customer will be redirected to
// your `return_url`. For some payment methods like iDEAL, your customer will
// be redirected to an intermediate site first to authorize the payment, then
// redirected to the `return_url`.
if (error.type === "card_error" || error.type === "validation_error") {
showMessage(error.message);
} else {
showMessage("An unexpected error occured.");
}
setLoading(false);
}
// Fetches the payment intent status after payment submission
async function checkStatus() {
const clientSecret = new URLSearchParams(window.location.search).get(
"payment_intent_client_secret"
);
if (!clientSecret) {
return;
}
const { paymentIntent } = await stripe.retrievePaymentIntent(clientSecret);
switch (paymentIntent.status) {
case "succeeded":
showMessage("Payment succeeded!");
break;
case "processing":
showMessage("Your payment is processing.");
break;
case "requires_payment_method":
showMessage("Your payment was not successful, please try again.");
break;
default:
showMessage("Something went wrong.");
break;
}
}
// ------- UI helpers -------
function showMessage(messageText) {
const messageContainer = document.querySelector("#payment-message");
messageContainer.classList.remove("hidden");
messageContainer.textContent = messageText;
setTimeout(function () {
messageContainer.classList.add("hidden");
messageText.textContent = "";
}, 4000);
}
// Show a spinner on payment submission
function setLoading(isLoading) {
if (isLoading) {
// Disable the button and show a spinner
document.querySelector("#submit").disabled = true;
document.querySelector("#spinner").classList.remove("hidden");
document.querySelector("#button-text").classList.add("hidden");
} else {
document.querySelector("#submit").disabled = false;
document.querySelector("#spinner").classList.add("hidden");
document.querySelector("#button-text").classList.remove("hidden");
}}```
You shouldn't rely on your customers being returned to the return_url following a successful payment to handle any fulfilment or post-payment requirements. Instead you should implement a webhook to handle these actions.
Specifically, listen for the payment_intent.succeeeded event. Details here.
Related
I'm making REST APIS with Express.js
I have the following express route:
/api/customer
I added multiple query params to the route like this:
/api/customer?name=jake
/api/customer?country=america
/api/customer?name=jake&country=america
/api/customer?name=jake&limit=10
In my controllers I handle all of these with If and there are so many cases I feel that this method would not scale, is there a better way of handling this ?
This is the code for my controller, I'm using Sequelize to query the database:
async function getAllCustomer(queryLimit, page) {
const customers = await Customer.findAll({
limit: queryLimit ? parseInt(queryLimit) : null,
offset: page ? parseInt(queryLimit) * parseInt(page) : null
});
return customers;
}
async function getCustomerByFirstName(name, queryLimit, page) {
return await Customer.findAll({
where: {
firstName: name,
}
})
}
async function getCustomerByAddress(address) {
return await Customer.findAll({
where: {
customerAddress: address
}
})
}
async function getCustomerByNameAddress(name, address) {
return await Customer.findAll({
where: {
[Op.and]: [
{firstName: name},
{customerAddress: address}
]
}
})
}
async function getCustomer(req, res) {
const page = req.query.page;
const queryLimit = req.query.limit;
const name = req.query.name;
const address = req.query.address;
let customers;
/* User want to find first names */
if (name && !address) {
const names = name.split(",")
customers = await getCustomerByFirstName(names, queryLimit, page)
res.status(200).send(customers)
return;
}
/* User want to find addresses */
if (!name && address) {
const addresses = address.split(",")
customers = await getCustomerByAddress(addresses, queryLimit, page)
res.status(200).send(customers)
return;
}
/* User want to mix both */
if (name && address) {
const names = name.split(",")
const addresses = address.split(",")
customers = await getCustomerByNameAddress(names, addresses, queryLimit, page)
res.status(200).send(customers)
return;
}
if (!name && !address) {
customers = await getAllCustomer(queryLimit, page)
res.status(200).send(customers)
return;
}
}
You could do something like this:
async function getCustomer(req, res) {
const page = req.query.page;
const queryLimit = req.query.limit;
const name = req.query.name;
const address = req.query.address;
let query = { };
if(name) {
query.firstName = name;
}
if(address) {
query.address = address;
}
let customers = await getCustomers(query, queryLimit, page);
res.status(200).send(customers)
return;
}
async function getCustomers(query, queryLimit, page) {
const customers = await Customer.findAll({
where: query,
limit: queryLimit ? parseInt(queryLimit) : null,
offset: page ? parseInt(queryLimit) * parseInt(page) : null
});
return customers;
}
BTW, in your code, the functions getCustomerByFirstName, getCustomerByAddress and getCustomerByNameAddress are expecting to receive name and address as string parameter, but you are passing names and addresses array. This might lead to errors...
const NetworkToUse = process.env.REACT_APP_NETWORK;
const mnemonicWalletSubprovider = new MnemonicWalletSubprovider({
mnemonic: process.env.REACT_APP_MNEMONIC,
});
const infuraRpcSubprovider = new RPCSubprovider({
rpcUrl: `https://${NetworkToUse}.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`,
});
const providerEngine = new Web3ProviderEngine();
if (window.ethereum) {
providerEngine.addProvider(new SignerSubprovider(window.ethereum));
}
// providerEngine.addProvider(mnemonicWalletSubprovider);
providerEngine.addProvider(infuraRpcSubprovider);
providerEngine.start();
const seaport = new OpenSeaPort(
providerEngine,
{
networkName: NetworkToUse === "mainnet" ? Network.Main : Network.Rinkeby,
apiKey: process.env.REACT_APP_API_KEY,
},
(arg) => {
console.log("From OpenSeaPort CB:");
console.log(arg);
}
);
const placeBidMetaMask = async (order) => {
setIsProcessing(true);
if (typeof window.ethereum === "undefined") {
setError("Please make sure you have MetaMask installed!");
return;
}
if (!bidPrice || bidPrice < asset.price) {
setError("Insufficient Funds!");
return;
}
const { tokenId, tokenAddress } = order.asset;
try {
const [userAccount] = await window.ethereum.request({
method: "eth_requestAccounts",
});
const offer = await seaport.createBuyOrder({
asset: {
tokenId,
tokenAddress,
schemaName: asset.details.assetContract.schemaName,
},
accountAddress: userAccount,
startAmount: bidPrice,
});
console.log(offer);
setMessage("Buy Order Created");
} catch (err) {
setError(err.message);
console.log(err.message);
} finally {
setIsProcessing(false);
}
};
I am using metamask as wellet for bidding
Hi, I am using above code to place bid on opensea It is working but, I am using my personal MNEMONIC
But, in real time i can't get this from users meta mask wallet.
Is there any alternate way to place the bid.
I am using metamask as wellet for bidding
Hi, I am using above code to place bid on opensea It is working but, I am using my personal MNEMONIC
But, in real time i can't get this from users meta mask wallet.
Is there any alternate way to place the bid.
I am referencing this tutorial for Firestore security rules. I have extracted the code from the repository and it matches that of the video.
I changed the setup code to run the firestore.rules instead of firestore-test.rules, and tried running firebase emulators:start and jest ./spec following the same directory structure, I fail the tests of "should allow delete when user is admin" and "should not allow delete for normal user" and the reason it is failing is due to the write rule in the wildcard. Does anyone know what is wrong?
collections.spec.js
const { setup, teardown } = require("./helpers");
describe("General Safety Rules", () => {
afterEach(async () => {
await teardown();
});
test("should deny a read to the posts collection", async () => {
const db = await setup();
const postsRef = db.collection("posts");
await expect(postsRef.get()).toDeny();
});
test("should deny a write to users even when logged in", async () => {
const db = await setup({
uid: "danefilled"
});
const usersRef = db.collection("users");
await expect(usersRef.add({ data: "something" })).toDeny();
});
});
describe("Posts Rules", () => {
afterEach(async () => {
await teardown();
});
test("should allow update when user owns post", async () => {
const mockData = {
"posts/id1": {
userId: "danefilled"
},
"posts/id2": {
userId: "not_filledstacks"
}
};
const mockUser = {
uid: "danefilled"
};
const db = await setup(mockUser, mockData);
const postsRef = db.collection("posts");
await expect(
postsRef.doc("id1").update({ updated: "new_value" })
).toAllow();
await expect(postsRef.doc("id2").update({ updated: "new_value" })).toDeny();
});
test("should allow delete when user owns post", async () => {
const mockData = {
"posts/id1": {
userId: "danefilled"
},
"posts/id2": {
userId: "not_filledstacks"
}
};
const mockUser = {
uid: "danefilled"
};
const db = await setup(mockUser, mockData);
const postsRef = db.collection("posts");
await expect(postsRef.doc("id1").delete()).toAllow();
await expect(postsRef.doc("id2").delete()).toDeny();
});
test("should allow delete when user is admin", async () => {
const mockData = {
"users/filledstacks": {
userRole: "Admin"
},
"posts/id1": {
userId: "not_matching1"
},
"posts/id2": {
userId: "not_matching2"
}
};
const mockUser = {
uid: "filledstacks"
};
const db = await setup(mockUser, mockData);
const postsRef = db.collection("posts");
await expect(postsRef.doc("id1").delete()).toAllow();
});
test("should not allow delete for normal user", async () => {
const mockData = {
"users/filledstacks": {
userRole: "User"
},
"posts/id1": {
userId: "not_matching1"
},
"posts/id2": {
userId: "not_matching2"
}
};
const mockUser = {
uid: "filledstacks"
};
const db = await setup(mockUser, mockData);
const postsRef = db.collection("posts");
await expect(postsRef.doc("id1").delete()).toDeny();
});
test("should allow adding a post when logged in", async () => {
const db = await setup({
uid: "userId"
});
const postsRef = db.collection("posts");
await expect(postsRef.add({ title: "new_post" })).toAllow();
});
test("should deny adding a post when not logged in", async () => {
const db = await setup();
const postsRef = db.collection("posts");
await expect(postsRef.add({ title: "new post" })).toDeny();
});
});
firestore.rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// lock down the db
match /{document=**} {
allow read: if false;
allow write: if false;
}
match /posts/{postId} {
allow update: if userOwnsPost();
allow delete: if userOwnsPost() || userIsAdmin();
allow create: if loggedIn();
}
function loggedIn() {
return request.auth.uid != null;
}
function userIsAdmin() {
return getUserData().userRole == 'Admin';
}
function getUserData() {
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data
}
function userOwnsPost() {
return resource.data.userId == request.auth.uid;
}
}
}
Error trace from terminal
FirebaseError: 7 PERMISSION_DENIED:
false for 'create' # L10
● Posts Rules › should not allow delete for normal user
FirebaseError: 7 PERMISSION_DENIED:
false for 'create' # L10
at new FirestoreError (/Users/../../../../../../../../../Resources/rules/node_modules/#firebase/firestore/src/util/error.ts:166:5)
at ClientDuplexStream.<anonymous> (/Users/../../../../../../../../../Resources/rules/node_modules/#firebase/firestore/src/platform_node/grpc_connection.ts:240:13)
at ClientDuplexStream._emitStatusIfDone (/Users/../../../../../../../../../Resources/rules/node_modules/grpc/src/client.js:234:12)
at ClientDuplexStream._receiveStatus (/Users/../../../../../../../../../Resources/rules/node_modules/grpc/src/client.js:211:8)
at Object.onReceiveStatus (/Users/../../../../../../../../../Resources/rules/node_modules/grpc/src/client_interceptors.js:1311:15)
at InterceptingListener._callNext (/Users/../../../../../../../../../Resources/rules/node_modules/grpc/src/client_interceptors.js:568:42)
at InterceptingListener.onReceiveStatus (/Users/../../../../../../../../../Resources/rules/node_modules/grpc/src/client_interceptors.js:618:8)
at /Users/../../../../../../../../../Resources/rules/node_modules/grpc/src/client_interceptors.js:1127:18
I actually followed the same tutorial to get started with the firebase emulator and got the same kind of error messages. The problem for me was that when you start the simulator it automatically looks for your firestore.rules file and loads the rules. So, when you then add your mockData the rules already apply.
In order to make your test code work either change the setting for your firestore rules file in your firebase.json to a non-existing file (or rules file that allows all read/write) or add the mockData as an admin in your setup function, e.g.:
module.exports.setup = async (auth, data) => {
const projectId = `rules-spec-${Date.now()}`;
const app = firebase.initializeTestApp({
projectId,
auth
});
const db = app.firestore();
// Initialize admin app
const adminApp = firebase.initializeAdminApp({
projectId
});
const adminDB = adminApp.firestore();
// Write mock documents before rules using adminApp
if (data) {
for (const key in data) {
const ref = adminDB.doc(key);
await ref.set(data[key]);
}
}
// Apply rules
await firebase.loadFirestoreRules({
projectId,
rules: fs.readFileSync('firestore.rules', 'utf8')
});
return db;
};
Hope this helps.
Also see this question
For those that are currently having this issue firestore 8.6.1 (or equivalent), there is a bug discussed here:
https://github.com/firebase/firebase-tools/issues/3258#issuecomment-814402977
The fix is to downgrade to firestore 8.3.1, or if you are reading this in the future and firestore >= 9.9.0 has been released, upgrade to that version.
I am new to node.js and javascript. I get confused in my code as It give me empty response. I am trying to implement the promise and async and await feature however getting respones {}.
Could anybody help me to understand where I am wrong.
Please see below code may be it will long however I need help on issue of await where I am not getting empty result
var response = {};
var newSecret ='';
class FabricClientRegister {
constructor() {
console.log("called constructer");
}
async RegisterUser(Username, roleid) {
try {
const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
setTimeout(() => {
cb();
resolve();
}, timeout);
});
let query1 = {}
query1.RoleID = roleid;
// query1.name = '';
var name ='';
console.log('roleid',roleid)
// console.log('Username',Username);
var fs = require('fs');
var obj = JSON.parse(fs.readFileSync('./config/Config.json', 'utf8'));
// var Username = req.body.username;
console.log('Username',Username)
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = new FileSystemWallet(walletPath);
console.log(`Wallet path: ${walletPath}`);
// Check to see if we've already enrolled the user.
const userExists = await wallet.exists(Username);
if (userExists) {
response.data = null;
response.httpstatus = 400;
response.message = `An identity for the ${Username} already exists in the wallet`;
return response;
}
console.log("Username1",Username)
// Check to see if we've already enrolled the admin user.
const adminExists = await wallet.exists(appAdmin);
if (!adminExists) {
response.data = null;
response.httpstatus = 400;
response.message = "Am admin identity is not registered . please register admin first";
return response;
}
// Create a new gateway for connecting to our peer node.
const gateway = new Gateway();
await gateway.connect(ccp, { wallet, identity: appAdmin, discovery: { enabled: false, asLocalhost: true }
/*** Uncomment lines below to disable commit listener on submit ****/
, eventHandlerOptions: {
strategy: null
}
});
// Get the CA client object from the gateway for interacting with the CA.
const ca = gateway.getClient().getCertificateAuthority();
const adminIdentity = gateway.getCurrentIdentity();
console.log("Username4",Username)
MongoClient.connect(config.Database.DFARM.connectString, async function (err, client) {
if (err) {
let connError = new Error(500, "Error connecting to DFARM database", err);
res.status(connError.status).json(connError);
}
else {
client.db(config.Database.DFARM.dbName).collection("Role").find(query1).toArray(function (err, docs) {
if(err) {
console.log('err db',err);
} else{
console.log('Role name DB',docs);
name = docs[0].name;
query1.name = name;
console.log('Role',query1);
}
client.close();
})
}
})
setTimeout(() => console.log('Role 10',query1.name), 5 * 1000);
const doStuffAsync = async () => {
setAsyncTimeout( async () => {
console.log('Role Name',query1.name);
const secret = await ca.register({enrollmentID: Username, role: query1.name}, adminIdentity);
console.log('secret',secret);
response.secret = secret;
newSecret = secret;
console.log('newSecret', newSecret );
response.httpstatus = 200;
response.message = `Successfully registered admin user ${Username} and imported it into the wallet`;
return response;
}, 10000);
};
doStuffAsync();
// .then(function(result) {
// // console.log('promise result',result) // error here undefined
// }).catch(err)
// {
// console.log("eee", err)
// };
console.log('newSecret1', newSecret)
console.log('respones', response)
return newSecret;
} catch (error) {
response.error = error;
response.httpstatus = 500;
response.message = "Failed to enroll admin due to above error";
return response;
}
}
};
Please see below output in CLI
Username Abhinav345
called constructer
roleid 1
Username Abhinav345
Wallet path: /vagrant/Dfarm-app/dFarmUserService/dFarmUserService/wallet
Username1 Abhinav345
Username4 Abhinav345
newSecret1
respones {} //getting empty one however need some data
data result
Role name DB [ { _id: 5d029dec7e8409b489e04cff,
appName: 'Farmer',
RoleID: 1,
name: 'farmer',
routes: [ [Object], [Object], [Object], [Object] ],
tabs: [ [Object], [Object], [Object], [Object] ] } ]
Role { RoleID: 1, name: 'farmer' }
Role 10 farmer
Role Name farmer
secret XXCephExVetS
newSecret XXCephExVetS
Maybe you should consider put await before doStuffAsync().
I am working on solutions using which i can send desktop push notification to subscribed clients.
I have created basic solution in where whenever user click on button i ask user for whether they want to allow notifications for my app or not!
I am getting an error of "Registration failed - permission denied" whenever i click on button for first time.
So that i am not able to get required endpoints to save at backend
Here is my code
index.html
<html>
<head>
<title>PUSH NOT</title>
<script src="index.js"></script>
</head>
<body>
<button onclick="main()">Ask Permission</button>
</body>
</html>
index.js
const check = () => {
if (!("serviceWorker" in navigator)) {
throw new Error("No Service Worker support!");
} else {
console.log("service worker supported")
}
if (!("PushManager" in window)) {
throw new Error("No Push API Support!");
} else {
console.log("PushManager worker supported")
}
};
const registerServiceWorker = async () => {
const swRegistration = await navigator.serviceWorker.register("/service.js?"+Math.random());
return swRegistration;
};
const requestNotificationPermission = async () => {
const permission = await window.Notification.requestPermission();
// value of permission can be 'granted', 'default', 'denied'
// granted: user has accepted the request
// default: user has dismissed the notification permission popup by clicking on x
// denied: user has denied the request.
if (permission !== "granted") {
throw new Error("Permission not granted for Notification");
}
};
const main = async () => {
check();
const swRegistration = await registerServiceWorker();
const permission = await requestNotificationPermission();
};
// main(); we will not call main in the beginning.
service.js
// urlB64ToUint8Array is a magic function that will encode the base64 public key
// to Array buffer which is needed by the subscription option
const urlB64ToUint8Array = base64String => {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, "+")
.replace(/_/g, "/");
const rawData = atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};
const saveSubscription = async subscription => {
console.log("Save Sub")
const SERVER_URL = "http://localhost:4000/save-subscription";
const response = await fetch(SERVER_URL, {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(subscription)
});
return response.json();
};
self.addEventListener("activate", async () => {
try {
const applicationServerKey = urlB64ToUint8Array(
"BFPtpIVOcn2y25il322-bHQIqXXm-OACBtFLdo0EnzGfs-jIGXgAzjY6vNapPb4MM1Z1WuTBUo0wcIpQznLhVGM"
);
const options = { applicationServerKey, userVisibleOnly: true };
const subscription = await self.registration.pushManager.subscribe(options);
console.log(JSON.stringify(subscription))
const response = await saveSubscription(subscription);
} catch (err) {
console.log(err.code)
console.log(err.message)
console.log(err.name)
console.log('Error', err)
}
});
self.addEventListener("push", function(event) {
if (event.data) {
console.log("Push event!! ", event.data.text());
} else {
console.log("Push event but no data");
}
});
Also i have created a bit of backend as well
const express = require("express");
const cors = require("cors");
const bodyParser = require("body-parser");
const webpush = require('web-push')
const app = express();
app.use(cors());
app.use(bodyParser.json());
const port = 4000;
app.get("/", (req, res) => res.send("Hello World!"));
const dummyDb = { subscription: null }; //dummy in memory store
const saveToDatabase = async subscription => {
// Since this is a demo app, I am going to save this in a dummy in memory store. Do not do this in your apps.
// Here you should be writing your db logic to save it.
dummyDb.subscription = subscription;
};
// The new /save-subscription endpoint
app.post("/save-subscription", async (req, res) => {
const subscription = req.body;
await saveToDatabase(subscription); //Method to save the subscription to Database
res.json({ message: "success" });
});
const vapidKeys = {
publicKey:
'BFPtpIVOcn2y25il322-bHQIqXXm-OACBtFLdo0EnzGfs-jIGXgAzjY6vNapPb4MM1Z1WuTBUo0wcIpQznLhVGM',
privateKey: 'mHSKS-uwqAiaiOgt4NMbzYUb7bseXydmKObi4v4bN6U',
}
webpush.setVapidDetails(
'mailto:janakprajapati90#email.com',
vapidKeys.publicKey,
vapidKeys.privateKey
)
const sendNotification = (subscription, dataToSend='') => {
webpush.sendNotification(subscription, dataToSend)
}
app.get('/send-notification', (req, res) => {
const subscription = {endpoint:"https://fcm.googleapis.com/fcm/send/dLjyDYvI8yo:APA91bErM4sn_wRIW6xCievhRZeJcIxTiH4r_oa58JG9PHUaHwX7hQlhMqp32xEKUrMFJpBTi14DeOlECrTsYduvHTTnb8lHVUv3DkS1FOT41hMK6zwMvlRvgWU_QDDS_GBYIMRbzjhg",expirationTime:null,keys:{"p256dh":"BE6kUQ4WTx6v8H-wtChgKAxh3hTiZhpfi4DqACBgNRoJHt44XymOWFkQTvRPnS_S9kmcOoDSgOVD4Wo8qDQzsS0",auth:"CfO4rOsisyA6axdxeFgI_g"}} //get subscription from your databse here.
const message = 'Hello World'
sendNotification(subscription, message)
res.json({ message: 'message sent' })
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Please help me
Try the following code:
index.js
const check = () => {
if (!("serviceWorker" in navigator)) {
throw new Error("No Service Worker support!");
} else {
console.log("service worker supported")
}
if (!("PushManager" in window)) {
throw new Error("No Push API Support!");
} else {
console.log("PushManager worker supported")
}
};
const saveSubscription = async subscription => {
console.log("Save Sub")
const SERVER_URL = "http://localhost:4000/save-subscription";
const response = await fetch(SERVER_URL, {
method: "post",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(subscription)
});
return response.json();
};
const urlB64ToUint8Array = base64String => {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, "+")
.replace(/_/g, "/");
const rawData = atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
};
const registerServiceWorker = async () => {
return navigator.serviceWorker.register("service.js?"+Math.random()).then((swRegistration) => {
console.log(swRegistration);
return swRegistration;
});
};
const requestNotificationPermission = async (swRegistration) => {
return window.Notification.requestPermission().then(() => {
const applicationServerKey = urlB64ToUint8Array(
"BFPtpIVOcn2y25il322-bHQIqXXm-OACBtFLdo0EnzGfs-jIGXgAzjY6vNapPb4MM1Z1WuTBUo0wcIpQznLhVGM"
);
const options = { applicationServerKey, userVisibleOnly: true };
return swRegistration.pushManager.subscribe(options).then((pushSubscription) => {
console.log(pushSubscription);
return pushSubscription;
});
});
};
const main = async () => {
check();
const swRegistration = await registerServiceWorker();
const subscription = await requestNotificationPermission(swRegistration);
// saveSubscription(subscription);
};
service.js
self.addEventListener("push", function(event) {
if (event.data) {
console.log("Push event!! ", event.data.text());
} else {
console.log("Push event but no data");
}
});
I can think of three reasons that the permission is denied
1) your site is not on https (including localhost that is not on https), the default behaviour from chrome as far as i know is to block notifications on http sites. If that's the case, click on the info icon near the url, then click on site settings, then change notifications to ask
2) if you are on Safari, then safari is using the deprecated interface of the Request permission, that is to say the value is not returned through the promise but through a callback so instead of
Notification.requestPermission().then(res => console.log(res))
it is
Notification.requestPermission(res => console.log(res))
3) Your browser settings are blocking the notifications request globally, to ensure that this is not your problem run the following code in the console (on a secured https site)
Notification.requestPermission().then(res => console.log(res))
if you receive the alert box then the problem is something else, if you don't then make sure that the browser is not blocking notifications requests