How to access clientReferenceId on success using Stripe Checkout - stripe-payments

I'm new to Stripe and using what appears to be a very simplistic setup to accept payments using Stripe Checkout. I'm passing a clientReferenceId which I need to access on the success page. Is this possible? Here's the code which is called on the Checkout button:
const stripeCheckout = async () => {
setLoading(true)
const stripe = await stripePromise;
const { error } = await stripe.redirectToCheckout({
lineItems: [
{
price: 'price_xxxxx',
quantity:1,
}
],
mode:"payment",
cancelUrl: window.location.origin,
successUrl: `${window.location.origin}/payment-complete`,
clientReferenceId: 'abc',
});
if (error) {
console.log("Error # Checkout: ",error)
setLoading(false)
}
}
Thank you for any help.

You can access the client_reference_id by retrieving the Checkout Session on the success page: https://stripe.com/docs/payments/checkout/custom-success-page
However a much easier solution would be to just encode your variable directly into the success URL:
successURL: `${window.location.origin}/payment-complete?id=abc`,
Then on your success page you can just access the query string variables.

Related

Can't access the stripe check-out page

I have written my server-side code by nodejs .
I have implemented a route to create a stripe checkout session, which contains a bunch of data about the object that is going to be purchased.
router.get(
'/checkout-session/:productId',
catchAsync(async (req, res, next) => {
// 1) Get the currently ordered product
const product = await Product.findById(req.params.productId);
// 2) Create checkout session
const session = await stripe.checkout.sessions.create({
expand: ['line_items'],
payment_method_types: ['card'],
success_url: `https://nasim67reja.github.io/CareoCIty-ecommerce/`,
cancel_url: `https://nasim67reja.github.io/CareoCIty-ecommerce/#/${product.categories}`,
customer_email: req.user.email,
client_reference_id: req.params.productId,
line_items: [
{
price_data: {
currency: 'usd',
unit_amount: product.price * 100,
product_data: {
name: `${product.name} `,
description: product.summary,
images: [
`https://e-commerceapi.up.railway.app/Products/${product.categories}/${product.images[0]}`,
],
},
},
quantity: 1,
},
],
mode: 'payment',
});
// 3) Create session as response
res.status(200).json({
status: 'success',
session,
});
})
);
Then on the front-end (ReactJs), I created a function to request the checkout session from the server once the user clicks the buy button.
So once I hit that endpoint that I created on the backend, that will make a session and send it back to the client. Then based on that session, the stripe will automatically create a checkout page for us. where the user can then input
here is my client side code:
const buyProduct = async () => {
try {
const session = await axios.get(
`${URL}/api/v1/orders//checkout-session/${product}`
);
window.location.replace(session.data.session.url);
} catch (error) {
console.log(`error: `, error.response);
}
};
All was okay when I tried on the local server.But when I hosted my backend on the Railway, then I got an error
I have also tried to put the stripe public & private key on the authorization header. but still got that error.I searched a lot but didn't find any solution. I will be very happy to get help from you
Looks like your server on Railway doesn't have the correct secret key. It depends on how you initialize stripe in NodeJS, but normally if you read it from an environment variable, you want to make sure Railway also sets that value correctly.
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);

how can I got stripe confirmation after successfully paid a payment?

I am trying to console after getting my payment but it doesn't work.
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
// Make sure to change this to your payment completion page
return_url: "/dashBoard/myorder",
},
},
From the Stripe.js website:
stripe.confirmPayment({
elements,
confirmParams: {
// Return URL where the customer should be redirected after the PaymentIntent is confirmed.
return_url: 'https://example.com',
},
})
.then(function(result) {
if (result.paymentIntent) {
}
});

Stripe "Missing required param: line_items[0][currency]." Node js

I'm creating subscription on node js backend. It had been working good but today I got this error. I didn't made any changes in code - it just starts returning this error.
backend code:
app.post('/api/subscription', async (req, res) => {
const { priceId } = req.body;
try {
const session = await stripe.checkout.sessions.create({
mode: "subscription",
payment_method_types: ["card"],
line_items: [
{
price: priceId,
// For metered billing, do not pass quantity
quantity: 1,
},
],
// {CHECKOUT_SESSION_ID} is a string literal; do not change it!
// the actual Session ID is returned in the query parameter when your customer
// is redirected to the success page.
success_url: 'https://someurl',
cancel_url: 'https://someurl',
});
res.send({
sessionId: session.id,
});
} catch (e) {
console.log(e)
res.status(400);
return res.send({
error: {
message: e.message,
}
});
}
})
from client I'm sending
fetch("http://localhost:8000/api/subscription", {
method: "POST",
body: JSON.stringify({ priceId }),
});
I've taken this code from official stripe example here https://stripe.com/docs/billing/subscriptions/checkout
And as I said it works fine, and now I've tested it on two different stripe accounts and getting the same error. It looks like something changed on stripe but not in their documentation
If priceId is undefined/null it won't be sent in the request. If the Price isn't present the API assumes you're trying to specify information about a line item without using a Price, and one of the first checks it performs is for a valid currency (which you don't have), which results in the Missing required param: line_items[0][currency]. error.
To fix the issue you'll need to figure out why priceId isn't being populated as expected, and you may also want to add a check to make sure priceId is valid before proceeding to the Checkout Session creation step.

Nextjs api and authentication

I'm in the process of building an application for stripe payments. This application generates a form that passes the data to the Stripe api via nextjs api. I just need to build in some basic authentication so only those submitting their payments via my form have access to the api. How would I go about adding some basic auth to my api without requiring users to login? Would I do this via env variable? I'm fairly new to the nextjs/vercel world and come from the python/django/react realm, so maybe my thoughts on this are backwards... I'm planning on hosting the api on vercel and the react form on a php site. So the react form will essentially push data to the vercel app api.
(The reason I'm not building the api in php is because I don't know php and because I'm attempting to build something with as little footprint in the current php site as possible.) Any help or guidance on this would be much appreciated!
My pages/api/customers.js file
import Stripe from 'stripe'
const stripe = new Stripe(process.env.SECRET_KEY)
export default async (req, res) => {
if (req.method === 'POST') {
try {
const { email, name, address, phone, source } = req.body
// Check for customer
const customerExist = await stripe.customers.list(
{
email: email,
limit: 0
})
// console.log('customerExist', customerExist.data[0])
if (customerExist.data.length < 1) {
const customer = await stripe.customers.create({
email,
name,
address,
phone,
source
})
res.status(200).send(customer.id)
} else {
res.status(200).send(customerExist.data[0].id)
}
} catch (err) {
res.status(500).json({ statusCode: 500, message: err.message })
}
} else {
res.setHeader('Allow', 'POST')
res.status(405).end('Method Not Allowed')
}
}
Part of my checkout form
// Function to check/create a user account via api
const checkUserAccount = async (billingDetails, source) => {
try {
const customer = await axios.post('/api/customers', {
email: billingDetails.email,
name: billingDetails.name,
phone: billingDetails.phone,
address: billingDetails.address,
source: source
})
return customer.data
} catch (err) {
console.log(err)
}
}
UPDATE:
Alright, so I added a "TOKEN" to my .env file and now require my api to receive that specific token.
I added this to my checkout form:
...
axios.defaults.headers.common.Authorization = process.env.AUTH_TOKEN
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
...
and then added this to the api:
if (req.method === 'POST' && req.headers.authorization === process.env.AUTH_TOKEN)...
Since I'm not using a login/logout system, I'm hoping this is enough. Thoughts or feedback are more than welcome.

Why are the Stripe Payments Incomplete?

My Stripe payments show up on my dashboard, but they have an 'Incomplete' status, and hovering over shows the tip, "The customer has not entered their payment method." I thought I accounted for payment method in my sessions.create() method.
My Angular component creates a StripeCheckout and sends the session data to my API. API then includes it in the response to the browser (my first attempt was to use this sessionId to redirect to checkout, but I chose this because it was smoother).
Angular/TS StripeCheckout and handler:
let headers = new Headers();
headers.append("Content-Type", "application/json");
headers.append(
"authentication",
`Bearer ${this.authServ.getAuthenticatedUserToken()}`
);
this.handler = StripeCheckout.configure({
key: environment.stripe_public_key,
locale: "auto",
source: async source => {
this.isLoading = true;
this.amount = this.amount * 100;
this.sessionURL = `${this.stringValServ.getCheckoutSessionStripeURL}/${this.activeUser.id}/${this.amount}`;
const session = this.http
.get(this.sessionURL, {
headers: new HttpHeaders({
"Content-Type": "application/json",
authentication: `Bearer ${this.authServ.getAuthenticatedUserToken()}`
})
})
.subscribe(res => {
console.log(res);
});
}
});
//so that the charge is depicted correctly on the front end
this.amount = this.amount / 100;
}
async checkout(e) {
this.stripeAmountEvent.emit(this.amount);
this.handler.open({
name: "[REDACTED]",
amount: this.amount * 100,
description: this.description,
email: this.activeUser.email
});
e.preventDefault();
}
NodeJS API picks it up
exports.getCheckoutSession = catchAsync(async (req, res, next) => {
const currentUserId = req.params.userId;
const paymentAmount = req.params.amount;
const user = await User.findById(currentUserId);
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
success_url: `${process.env.CLIENT_URL}`,
cancel_url: `${process.env.CLIENT_URL}`,
customer_email: user.email,
client_reference_id: user.userId,
line_items: [
{
name: `Donation from ${user.userName}`,
description: '[REDACTED]',
amount: paymentAmount,
currency: 'usd',
quantity: 1,
customer: user.userId
}
]
});
const newPayment = await Payment.create({
amount: paymentAmount,
createdAt: Date.now(),
createdById: user._id
});
res.status(200).send({
status: 'success',
session
});
});
The payment gets created in my db, and the payment shows up on my Stripe dashboard. The payments show as 'Incomplete' when I expected it to charge the card.
Thanks in advance.
The latest version of Checkout lets you accept payments directly on a Stripe-hosted payment page. This includes collecting card details, showing what's in your cart and ensuring the customer pays before being redirected to your website.
At the moment, though, your code is mixing multiple products in one place incorrectly. The code you have client-side uses Legacy Checkout. This is an older version of Stripe's product that you could use to collect card details securely. This is not something you should use anymore.
Then server-side, you are using the newer version of Checkout by creating a Session. This part is correct but you never seem to be using it.
Instead, you need to create the Session server-side, and then client-side you only need to redirect to Stripe using redirectToCheckout as documented here.

Resources