Stripe - Creating Charge - node.js

Working on integrating stipe. Everything seems to work on the front end but on the server side code the token is empty and it is not successfully charging to Stripe. Can't seem to figure out where I'm going wrong.
app.post('/apple-pay', function(req, res, next) {
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_XXX");
// Token is created using Checkout or Elements!
// Get the payment token ID submitted by the form:
const token = req.body.stripeToken;
console.log(token)
const charge = stripe.charges.create({
amount: 999,
currency: 'usd',
description: 'Example charge',
source: token,
}, function(err, charge) {
if(err){
req.flash("error", err.message);
res.redirect("back");
} else {
}
});
});

Before Creating charge, You should create Customer. After Charge Works.
Sample Code. (ES6)
let customer = await payStripe.customers.create({
email: req.body.stripeEmail,
source: req.body.stripeToken
});
//After Created Customer...
if(customer){
let charge = await payStripe.charges.create({
amount: req.body.amount,
description: req.body.description,
currency: 'usd',
customer: customer.id
});
}
I hope it's work fine.

In your frontend code from the other question you passed the POST body as
JSON.stringify({token: ev.token.id})
which means that the Stripe token is actually in the token POST parameter, not stripeToken. So you need to do
const token = req.body.token;
instead.

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);

Create Stripe Connect Customer with Payment Method on Platform Account

I am following the docs here about enabling other business to accept payments directly.
I have successfully created a connected account and accepted a payment from a customer on the connected account, but I have not yet been able to save that customers payment information for future payments.
All across the documentation, especially here it assumes you already created a customer on the platform account WITH a payment method, before you should try and clone the payment method to the connected accounts.
I cannot for the life of me figure out how to create a customer with payment information on the platform account before I clone them on the connected account.
As per the documentation, I started here on the client side where the accountID is the ID of the connected account:
const [stripePromise, setstripePromise] = useState(() =>
loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY, {
stripeAccount: uid.accountID,
})
);
My Stripe Elements are created with the stripePromise.
When trying to save the card details I do this on the client side:
This is where I believe my mistake is I am using the connected accounts credentials while trying to create a platform payment method.
const handleRememberMe = async () => {
const { token } = await stripe.createToken(
elements.getElement(CardElement)
);
console.log(token);
const res = await fetchPostJSON("/api/pay_performer", {
accountID: accountID,
amount: value,
cardToken: token,
});
That API call goes to "/api/pay_performer":
//Handle onboarding a new connect user for x
require("dotenv").config();
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: "2020-08-27",
});
import setCookie from "../../../utils/cookies";
export default async function createPayment(req, res) {
if (req.method === "POST") {
// Connected account ID and the token generated on the client to be saved.
const { accountID, amount, cardToken } = req.body;
console.log(`API side cardToken: ${cardToken.used}`);
try {
// Trying to create a customer with that token.
const customer = await stripe.customers.create({
email: "test2#example.com",
source: cardToken.card,
});
customer.sources = cardToken.card;
// Beginning the cloning process for the connected account.
const token = await stripe.tokens.create(
{
customer: customer.id,
},
{
stripeAccount: accountID,
}
);
const clonedCustomer = await stripe.customers.create(
{
source: token.id,
},
{
stripeAccount: accountID,
}
);
console.log(`Default Source: ${clonedCustomer.default_source}`);
console.log(`AccountID: ${accountID}, Amount: ${amount}`);
const paymentIntent = await stripe.paymentIntents.create(
{
payment_method_types: ["card"],
payment_method: clonedCustomer.default_source
? clonedCustomer.default_source
: null,
amount: amount * 100,
currency: "usd",
application_fee_amount: 1,
customer: clonedCustomer.id,
},
{
stripeAccount: accountID,
}
);
const secret = paymentIntent.client_secret;
console.log(secret);
const payment_method = paymentIntent.payment_method;
return res.status(200).send({ secret, payment_method });
} catch (e) {
console.log(e);
return res.status(500).send({ error: e.message });
}
}
}
But I get an error that the token provided is not a valid token. I am assuming this is because I created the token using the credentials for the connected account on the client and then tried to apply it to the platform account.
How do I have a separate UI to create platform customers FIRST, and then come back and clone them to the connected accounts upon purchase.
Should I not pass a token to the server and just pass the card information over HTTPS? Very lost and the documentation has not helped!
Any help is appreciated!
In order to create a Customer, collect card information and attach a PaymentMethod to them, you could for example use a SetupIntent and Elements [1] or create a PaymentMethod [2]. The core point being, if the system should initially store that information on the platform, the connected account isn't coming into play at all yet. Broadly speaking the steps are (not providing the account id of the connected account anywhere as this is only happening on the platform account), using the platform's secret key:
Create a customer
Collect card data using Stripe.js and Elements
Attach the pm_xxx to the customer in the backend
[1] https://stripe.com/docs/payments/save-and-reuse
[2] https://stripe.com/docs/js/payment_methods/create_payment_method

Stripe No such customer (Express Connected Account)

I'm trying to allow users to create one-off invoices after they have onboarded to the platform via an Express account.
I have a Node route set up to create and send an invoice.
I'm getting the error
StripeInvalidRequestError: No such customer: 'cus_I3Xra0juO9x2Iu'
However the customer does exist in the user's connect account.
The route is below
app.post('/api/new_invoice', async (req, res) => {
try {
const { customer, deliverables, amount, payableBy } = req.body
const product = await stripe.products.create({
name: deliverables,
})
const price = await stripe.prices.create({
unit_amount: amount,
currency: 'aud',
product: product.id,
})
const invoiceItem = await stripe.invoiceItems.create({
customer,
price: price.id,
})
const stripeInvoice = await stripe.invoices.create(
{
customer,
collection_method: 'send_invoice',
days_until_due: 30,
invoiceItem,
},
{
stripeAccount: req.user.stripeAcct,
}
)
const invoice = await new Invoice({
customer,
deliverables,
amount,
paid: false,
issueDate: Date.now(),
payableBy,
_user: req.user.id,
}).save()
res.send(invoice.data)
console.log(invoiceItem, stripeInvoice)
} catch (err) {
console.log(err)
res.status(400)
res.send({ error: err })
return
}
})
From what I understand adding a second object to stripe.invoices.create with the connect account's id then it should look into their Stripe account for the customer?
Thanks in advance
When making a call on behalf of a connected account, you need to set the Stripe-Account header as their account id as documented here. This header has to be set on every API request you make on behalf of that connected account.
In your code, you are only setting the header on the Invoice Create API request but not the other one(s) such as the Invoice Item Create. Make sure to pass it everywhere and your code will start working.

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.

Authentication error while implementing Stripe in Node js

I am trying to implement Stripe in my backend which is in node js. While calling it from front end I am getting this error:
You did not provide an API key, though you did set your Authorization header to "null". Using Bearer auth, your Authorization header should look something like 'Authorization: Bearer YOUR_SECRET_KEY'. See https://stripe.com/docs/api#authentication for details, or we can help at https://support.stripe.com/.
Here is my code:
const stripe = require("stripe")(process.env.keySecret);
if(req.body.type == 'stripe') {
const token = req.body.token;
const amount = req.body.amount;
let charge = stripe.charges.create({
amount: amount,
source: token,
currency: 'USD'
},(err, chargeData) => {
if(err) {
console.log('error in buy premium api error... ', err);
return;
}
let payment = [];
payment.push({
amount: chargeData.amount,
address: chargeData.billing_details.address,
email: chargeData.billing_details.email,
payment_method_details: chargeData.payment_method_details.card
})
console.log('charge... ', charge);
if(charge) {
register.findByIdAndUpdate({ _id: therapist._id },
{
$set:
{
payment_details: payment
}
}, {new:true}, (e1, updatedUser) => {
if(e1) {
return;
}
resolve(updatedUser);
})
}
})
}
else {
let err = 'No payment type define..';
return reject(err);
}
My req.body is:
{
"token": "tok_1F2XPBG04BYg8nGtLoWnQwRU", // coming from front end
"amount": "250",
"type":"stripe"
}
The secret key is test secret key which is like sk_test_xxxxxxxxxxxxxxxx.
My front end is in Angular 6 and from front end I am passing my test publishable key i.e. pk_test_xxxxxxxxxxxxxxxxx. I am passing proper data to the both front end as well as backend. Where am I mistaken?
Since you are calling .env file, you have to include the following line before requiring stripe:
require ("dotenv").config();
const stripe = require("stripe")(process.env.keySecret);

Resources