How do I know which user has made the payment in stripe - node.js

I am integrating stripe with my application. I want to receive a one time payment from a logged in user and once the payment is done maybe save the payment status in the database.
I have set up the stripe-checkout and stripe-webhook. But how would i know which logged in user from the client side has made the payment so that i can set the payment status for that user in the database.
Here is how my checkout and webhook look like.
app.post("/checkout-session", async (req, res) => {
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
mode: "payment",
line_items: req.body.items.map((item) => {
const storeItem = storeItems.get(item.id);
return {
price_data: {
currency: "usd",
product_data: {
name: storeItem.name,
},
unit_amount: storeItem.priceInCents,
},
quantity: item.quantity,
};
}),
success_url: `${process.env.CLIENT_URL}/success`,
cancel_url: `${process.env.CLIENT_URL}/failure`,
});
res.json({ url: session.url });
} catch (e) {
res.status(500).json({ error: e.message });
}
});
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
let event;
// Only verify the event if you have an endpoint secret defined.
// Otherwise use the basic event deserialized with JSON.parse
if (process.env.STRIPE_WEBHOOK_SECRET) {
// Get the signature sent by Stripe
const signature = req.headers["stripe-signature"];
try {
event = stripe.webhooks.constructEvent(
req.body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
console.log(`⚠️ Webhook signature verification failed.`, err.message);
return res.sendStatus(400);
}
}
// Handle the event
switch (event.type) {
case "payment_intent.succeeded":
const paymentIntent = event.data.object;
console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`);
console.log(paymentIntent);
// Then define and call a methstripe loginod to handle the successful payment intent.
// handlePaymentIntentSucceeded(paymentIntent);
break;
case "payment_method.attached":
const paymentMethod = event.data.object;
// Then define and call a method to handle the successful attachment of a PaymentMethod.
// handlePaymentMethodAttached(paymentMethod);
break;
case "payment_intent.payment_failed":
const failedpaymentIntent = event.data.object;
console.log(`PaymentIntent for ${paymentIntent.amount} failed!`);
// Then define and call a methstripe loginod to handle the successful payment intent.
// handlePaymentIntentSucceeded(paymentIntent);
break;
case "checkout.session.completed":
console.log(event.data.object);
// console.log(`PaymentIntent for ${paymentIntent.amount} failed!`);
// Then define and call a methstripe loginod to handle the successful payment intent.
// handlePaymentIntentSucceeded(paymentIntent);
break;
default:
// Unexpected event type
console.log(`Unhandled event type ${event.type}.`);
}
// Return a 200 response to acknowledge receipt of the event
res.send();
});```

In checkout.session.completed event, you will be able to find customer_details.email in the Checkout Session object to identify the customer.
Alternatively, your internal customer ID might be set under metadata parameter in Checkout Session Creation API. Metadata will be present in checkout.session.completed after successful payment which you can use to update the payment status in your database.

At the login time, you can use json web token (jwt) to send logged in user object in encrypted format and use local storage to store that token. and with request, send this token to backend and decode it. You will get user object from that and then you can able to find which user is sending request.
https://www.npmjs.com/package/jsonwebtoken

Related

Integration of razorpay with node.js

I am building a website in which users have to subscribe to one of the given plans, and I want to collect money from them using RazorPay.
Now I haven't used any payment services like Stripe, Razorpay, or anything like that. This is the first time I am working on that, so I need some guidance regarding that.
How can I setup razorpay on the backend side?(The front end is in flutter).
Additionally, I want to save the payments data in the database as well.
The route to subscribe the package:
exports.subscribe_package = catchAsync(async (req, res) => {
try {
const razorPay = new RazorPay({
key_id: keys.razorPayKey, //Basic razorpay set-up
key_secret: keys.razorPaySecret,
});
const payment = await paymentService.getPayment(
req.data.id,
req.body.razorpay_id
);
const subscription = await subscribeService.addSubscriberPackage(
req.data.id,
req.params.id
);
const user_data = await userService.addPackage(
req.data.id,
req.params.id,
subscription._id
);
return res.succeed(user_data);
} catch (error) {
return res.failed(500, "Internal Server Error", error);
}
});
It is nothing but the updating of some data in the database and the set-up of basic razorpay things.
The currency will be "INR".
The front-end side will send the paymentID to the backend. I just want to make use of it to capture the payment if it is successful or not.
How can I do that?
const Razorpay = require('razorpay');
const instance = new Razorpay({ key_id: 'id', key_secret: 'secret' });
let response = await instance.payments.capture('PaymentID', '2121', 'INR')
if(response){
// save the payments data
return res.success({ data: response });
}else{
return res.failed({ data: {},message:"payment captured failed." });
}

Checkout flow: how should I process a new order?

I'm trying to build an App which allows the customer to download a custom document after his order (so, the product I'm selling it's a digital custom document in pdf).
I'm trying to build that using React for the frontend, Node and Express for the backend, and PayPal Express Checkout (full-stack implementation) as payment.
What is not clear to me is what steps I should take to process an order.
I'm a this point:
Once customer clicks on PayPal button on client side it starts a call on http://localhost/api/paypal/orders endpoint which create the order on the Paypal side and return the PayPal order ID (e.g. id12345)
After the customer approves the payment on the PayPal popup, the client starts a call on http://localhost/api/paypal/orders/id12345/capture endpoint
then? What other endpoints should I crete on the server and what they should return? Maybe... 1 for save the actual store order on my MongoDB and the transaction details, 1 for create the invoice for the order, 1 for allowing the product download ???
Could you please clarify what steps I need to take, what are the endpoints I should create, and what each endpoints should return?
Maybe something like this?
You're trying to do too many things in too many routes. just create a route called something like process-order then you could have an async controller of that route which would contain separate functions for
Storing the order details in mongodb.
Create an invoice
Send a token that allows access to download page.
first , just await the function to complete then call the next.
exports.processOrder = async (req, res, next) => {
try {
const newOrder = await Order.create({
order: req.params.id,
details: req.body.details
})
const createInvoice = () => {
return newOrder._id;
}
const invoice = createInvoice();
const token = jwt.sign(
{
invoiceId = newOrder._id,
},
process.env.JWT_SECRET,
{
expiresIn: '24h'
}
);
return res.status(200).json({status: 'success', message: 'you have 24 hours to download your digital goods', token})
} catch (error) {
res.status(500).send(error)
}
}
This is a very basic idea, but basically store all of your info in one controller, then send a token with the id of the invoice, then when they go to download the book you would have a route where you verify the token. If it succeeds then the book is sent. like this:
app.use(
'/book-download',
expressJwt({ secret: process.env.JWT_SECRET, algorithms: ['HS256'] })
);
app.get('/book-download/success', async (req, res) => {
try{
const invoiceId = req.user.invoiceId;
const invoice = await Invoice.find({_id: invoiceId})
if (invoice) {
return res.status(200).json({status: 'success', message: 'congratulations on your new download', data: {E-book})
} else {
return.res.status(404).json({status: 'fail', message: 'could not find an invoice with that ID'})
} catch (error) {
return res.send(error)
}
});
You can choose to send the pdf via express, or you can allow them to enter a certain part of the website if the return is valid. There you go, that's an idea.
In Express check out, you need to define success / cancel handler
Ref here : https://www.section.io/engineering-education/nodejs-paypal-checkout/
In the success handler, based on the returned URI => you will get a Payer ID and payment ID. Based on that retrieve the transactio information (that internally contains yours order ID). Update your Order id as scucess / cancelled based on the paypal response.
app.get('/success', (req, res) => {
const payerId = req.query.PayerID;
const paymentId = req.query.paymentId;
const execute_payment_json = {
"payer_id": payerId,
"transactions": [{
"amount": {
"currency": "USD",
"total": "25.00"
}
}]
};
// Obtains the transaction details from paypal
paypal.payment.execute(paymentId, execute_payment_json, function (error, payment) {
//When error occurs when due to non-existent transaction, throw an error else log the transaction details in the console then send a Success string reposponse to the user.
if (error) {
console.log(error.response);
throw error;
} else {
console.log(JSON.stringify(payment));
res.send('Success');
}
});
});

Stripe Webhook Returning Error 401 and Success

I have a checkout session:
app.post("/create-checkout-session", async (req, res) => {
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
line_items: converted_items,
mode: "payment",
success_url: process.env.URL + "/order-success.html",
cancel_url: process.env.URL + `/order-page.html`,
billing_address_collection: 'required',
});
res.json({ id: session.id, order: req.body });
});
And I would like to set up a webhook so that after a payment is successfully made, it collects the data from the customer (name, address, products purchased)
I copied this right from the webhook docs:
const fulfillOrder = (session) => {
// TODO: fill me in
console.log("Fulfilling order", session);
}
app.post('/webhook', bodyParser.raw({ type: 'application/json' }), (request, response) => {
const payload = request.body;
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(payload, sig, endpointSecret);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
console.log(event.type)
// Handle the checkout.session.completed event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
console.log("did order")
// Fulfill the purchase...
fulfillOrder(session);
}
response.status(200);
});
How I am setting it up is I am first running my server on localhost and then I am running the testing webhook command from the terminal
stripe listen --forward-to localhost:3000/webhook --events=checkout.session.completed
When I make a payment, it shows me these messages in the terminal:
It looks like you've made some modifications to that webhook handler code from the docs. If you're using Checkout and following the docs, what made you switch the code to look for charge.succeeded?
You should add some logging to figure out why your server is returning 400, but most likely the verification is failing in constructEvent. The snippet you've shared doesn't show you setting endpointSecret, but you need to update the value using the secret shown to you when you run stripe listen.
You should review the Checkout fulfillment docs and focus on checkout.session.completed events. You can modify your CLI command to listen to online these events:
stripe listen --forward-to localhost:3000/webhook --events=checkout.session.completed
then your handler would look like the docs and you can implement fulfillOrder to meet your needs. If you want the details you listed, then you will need to retrieve the session with expansion to include the line items and payment details:
const session = await stripe.checkout.sessions.retrieve(
'cs_test_123',
{
expand: ['line_items', 'payment_intent.payment_method']
},
);
Then session.line_items will be what you created and the name, address and email will be on session.payment_intent.payment_method.billing_details.
Updated to suggest removing signature verification temporarily to diagnose:
The 400 errors suggest your server is returning an error, most likely as a result of signature verification. I recommend testing a basic endpoint without verification following this example, to ensure everything else is working as intended. When that is settled, re-adding signature verification is strongly recommended.

Botframework v4 Directline integration: Is there a way to get the conversation id generated from directline transfer to the Chatbot (nodejs code)

This might be very simple, but I cannot find any reference regarding this.
I integrated the chat bot in the web app using direct line API; I’m using this API to generate conversation id:
POST: https://directline.botframework.com/v3/directline/conversations
I’m trying to get the generated conversation id from the above API (from web app code) to the chatbot code (NodeJS). Is there a way or any reference to do this?
one way as per this issue comment is to send data to bot before starting the conversation using:
var params = BotChat.queryParams(location.search);
var my_token = params['my_token'];
var botConnection = new BotChat.DirectLine({
secret: 'DIRECTLINE_SECRET'
});
BotChat.App({
botConnection: botConnection
,user: { id: 'USER_ID', name: 'User' } // user.id auto updates after first user message
}, document.getElementById("bot"));
botConnection.connectionStatus$.subscribe(function (status) {
if (status == 2) { // wait for connection is 'OnLine' to send data to bot
var convID = botConnection.conversationId;
botConnection.postActivity({
from: { id: convID } // because first time user ID == conversation ID
,type: 'event'
,name: 'registerUserData' // event name as we need
,value: my_token // data attached to event
}).subscribe(function (activityId) {
// This subscription is a MUST
// If I remove this handler the postActivity not reaches the bot
});
}
});
Here you subscribe to the botConnection.connectionStatus$ and when the status is equal to 2, you get the conversation ID from the botConnection object.
Then, you can add this middleware code in the bot code to get the data:
bot.use({ receive: function(event, next) {
if (!!event && !!event.address && event.name == 'registerUserData') {
var message = new builder.Message().address(event.address).text('my_token:' + event.value);
bot.send(message, function (err) {}); // simulate proactive message to user
}
next();
} });
Hope this helps.
I resolve it using Botframework Web Chat back channel, here's the link for reference:
https://github.com/Microsoft/BotFramework-WebChat
After I generated the conversation id using directline API:
POST: https://directline.botframework.com/v3/directline/conversations
I send data from the web app to the chatbot via backchannel.
<div id="webchat"></div>
<script>
(async function () {
// We are using a customized store to add hooks to connect event
const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
// When we receive DIRECT_LINE/CONNECT_FULFILLED action, we will send an event activity using WEB_CHAT/SEND_EVENT
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/join',
value: { conversation_id: conversationID }
}
});
}
return next(action);
});
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({ token }),
store
}, document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
})().catch(err => console.error(err));
//Note: conversationID and token is generated in the backend code of the web app.
</script>

How to correctly create 'charge' in Stripe nodejs library?

Client
I'm using Stripe Checkout custom integration - https://stripe.com/docs/checkout#integration-custom - in a following way:
var handler = StripeCheckout.configure({
key: 'YOUR_KEY_HERE',
image: 'images/logo-48px.png',
token: function(token, args) {
$.post("http://localhost:3000/charge", {token: token}, function(res) {
console.log("response from charge: " + res);
})
}
})
Using custom contrary to simple - How can I modify Stripe Checkout to instead send an AJAX request? - because simple does not allow me to make an AJAX call.
Server
https://stripe.com/docs/tutorials/charges
You've got the token for your user's credit card details, now what? Now you charge them money.
app.post('/charge', function(req, res) {
console.log(JSON.stringify(req.body, null, 2));
var stripeToken = req.body.token;
var charge = stripe.charges.create({
amount: 0005, // amount in cents, again
currency: "usd",
card: stripeToken,
description: "payinguser#example.com"
}, function(err, charge) {
if (err && err.type === 'StripeCardError') {
console.log(JSON.stringify(err, null, 2));
}
res.send("completed payment!")
});
});
Here is the error:
Is seems to me like I have last4, exp_month, exp_year but for some reason I don't have number. Any suggestions / hints / ideas?
Googling for "The card object must have a value for 'number'" - 12 results, not much help.
The "token" you have to give as the card argument should actually just be the token id (e.g.: "tok_425dVa2eZvKYlo2CLCK8DNwq"), not the full object. Using Checkout your app never sees the card number.
You therefeore need to change:
var stripeToken = req.body.token;
to:
var stripeToken = req.body.token.id;
The documentation isn't very clear about this card option, but the Stripe API Reference has an example.
After npm install stripe do this
var stripe = require("stripe")("sk_yourstripeserversecretkey");
var chargeObject = {};
chargeObject.amount = grandTotal * 100;
chargeObject.currency = "usd";
chargeObject.source = token-from-client;
chargeObject.description = "Charge for joe#blow.com";
stripe.charges.create(chargeObject)
.then((charge) => {
// New charge created. record charge object
}).catch((err) => {
// charge failed. Alert user that charge failed somehow
switch (err.type) {
case 'StripeCardError':
// A declined card error
err.message; // => e.g. "Your card's expiration year is invalid."
break;
case 'StripeInvalidRequestError':
// Invalid parameters were supplied to Stripe's API
break;
case 'StripeAPIError':
// An error occurred internally with Stripe's API
break;
case 'StripeConnectionError':
// Some kind of error occurred during the HTTPS communication
break;
case 'StripeAuthenticationError':
// You probably used an incorrect API key
break;
case 'StripeRateLimitError':
// Too many requests hit the API too quickly
break;
}
});

Resources