use payment method for stripe paymentintent - javascript

I have this:
const stripe = require('stripe')('sk_test', {
stripeAccount: 'acct_...'
});
const paymentIntent = await stripe.paymentIntents.create({
amount: 1900,
currency: 'cad',
customer: 'cus_...',
// confirm: true,
}, {
stripeAccount: 'acct_...',
});
console.log(paymentIntent)
so then I go to run this paymentIntent, and it works, but doesn't actaully charge the customer because it says that it has no payment method on file. So then I take this customers id, and look at my stripe dashboard, and it shows the payment method there, and the method matches with the id. so now I believe I am doing something wrong creating the paymentIntent, but the payment is going through, just not confirmed because it says no payment method attached! So why is this not working?
error: UnhandledPromiseRejectionWarning: Error: You cannot confirm this PaymentIntent because it's missing a payment method. You can either update the PaymentIntent with a payment method and then confirm it again, or confirm it again directly with a payment method.

PaymentIntent requires a Payment Method Object such as;
payment_method_types: [card],
PaymentIntent object
const {
error: backendError,
clientSecret,
paymentIntentId,
transfer_group,
} = await fetch('/create-payment-intent', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
paymentMethodType: 'card',
currency: 'cad',
customerEmail: billingDetails.email,
description: 'Basket_Order_Id',
}),
}).then((r) => r.json());
and when you created the paymentintent on your backend you should return
app.post('/create-payment-intent', async (req, res) => {
const {paymentMethodType, currency, customerEmail, description,
suppliers} =
req.body;
console.log('paymentIntent Create requ body', req.body);
req.session.suppliers = suppliers;
suppliersArray = suppliers;
const idEmpotency = nanoid();
// Each payment method type has support for different currencies. In order
to
// support many payment method types and several currencies, this server
// endpoint accepts both the payment method type and the currency as
// parameters
//
// Some example payment method types include `card`, `ideal`, and
`alipay`.
const params = {
payment_method_types: [paymentMethodType], 'CARD'
amount: 20000,
currency: currency,
description: description,
receipt_email: customerEmail,
statement_descriptor_suffix: 'Your Bank Descriptor on Customer Account',
transfer_group: idEmpotency,
// confirm: true,
// capture_method: 'manual',
};
try {
const paymentIntent = await stripe.paymentIntents.create(params);
// Send publishable key and PaymentIntent details to client
console.log('paymentIntent', paymentIntent);
res.send({
clientSecret: paymentIntent.client_secret, - SENDING YOUR CLIENTSECRET
paymentIntentId: paymentIntent.id,
transfer_group: paymentIntent.transfer_group,
});
} catch (e) {
return res.status(400).send({
error: {
message: e.message,
},
});
}
});
client_secret and use it on your front-end
const {error: stripeError, paymentIntent} = await stripe.confirmCardPayment(
clientSecret, USE YOUR CLIENT SECRET THAT RETURNED FROM YOUR BACKEND FROM PAYMENT INTENT OBJECT
{
payment_method: {
card: elements.getElement(CardElement),
billing_details: {
name: 'Michael',
},
},
}
);
Before confirming the client_secret that returned from payment_intent you can not succesfully confirm the payment.
You can use stripe elements to start with their own card payment component.
I recommend you to check here https://stripe.com/docs/payments/quickstart, you will get more idea...

Related

Issue with Stripe Payment Sheet using firebase, cloud functions and stripe. "Unexpected Character (at line 2, character 1) <html><head>

As the title suggests, I am trying to implement Stripe into my flutter app using the stripe extension for Firebase and using Javascript Firebase Cloud Functions for the server side. I believe the issue is on the server side when I try to create a customer and create a payment intent.
The server side code is here:
const functions = require("firebase-functions");
const stripe = require("stripe")("my test secret key"); // this works fine for the other stripe functions I am calling
exports.stripePaymentIntentRequest = functions.https.onRequest(
async (req, res) => {
const {email, amount} = req.body;
try {
let customerId;
// Gets the customer who's email id matches the one sent by the client
const customerList = await stripe.customers.list({
email: email,
limit: 1,
});
// Checks the if the customer exists, if not creates a new customer
if (customerList.data.length !== 0) {
customerId = customerList.data[0].id;
} else {
const customer = await stripe.customers.create({
email: email,
});
customerId = customer.data.id;
}
// Creates a temporary secret key linked with the customer
const ephemeralKey = await stripe.ephemeralKeys.create(
{customer: customerId},
{apiVersion: "2022-11-15"},
);
// Creates a new payment intent with amount passed in from the client
const paymentIntent = await stripe.paymentIntents.create({
amount: parseInt(amount),
currency: "gbp",
customer: customerId,
});
res.status(200).send({
paymentIntent: paymentIntent.client_secret,
ephemeralKey: ephemeralKey.secret,
customer: customerId,
success: true,
});
} catch (error) {
res.status(404).send({success: false, error: error.message});
}
},
);
Then my client-side code is:
try {
// 1. create payment intent on the server
final response = await http.post(
Uri.parse(
'https://us-central1-clublink-1.cloudfunctions.net/stripePaymentIntentRequest'),
headers: {"Content-Type": "application/json"},
body: json.encode({
'email': email,
'amount': amount.toString(),
}),
);
final jsonResponse = json.decode(response.body);
if (jsonResponse['error'] != null) {
throw Exception(jsonResponse['error']);
}
log(jsonResponse.toString());
//2. initialize the payment sheet
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: jsonResponse['paymentIntent'],
merchantDisplayName: 'Clublink UK',
customerId: jsonResponse['customer'],
customerEphemeralKeySecret: jsonResponse['ephemeralKey'],
style: ThemeMode.dark,
),
);
await Stripe.instance.presentPaymentSheet();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Payment completed!')),
);
} catch (e) {
if (e is StripeException) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error from Stripe: ${e.error.localizedMessage}'),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
}
}
I basically copied the flutter_stripe documentation to create the payment sheet with the necessary changes. Any help would be greatly appreciated!
Ok so I found what worked! I was being given a 403 status error with reason "forbidden". This meant I had to go to the google cloud console and update the permissions in the cloud functions tab.

Sending a response after a finalized transaction - Stripe

After a long discussion with ChatGPT, I managed to write code that redirects the user to the Stripe payment page and then captures an event when the transaction is successfully completed.
The problem is that my fetch request has already received a response from the /checkout endpoint and is not waiting for a response from /webhook. And I would like my API to return a properly generated response after successfully finalizing the transaction. What am I doing wrong?
First, I send a request to the /checkout endpoint, which takes care of generating the payment link and sending it back:
fetch('http://localhost:3001/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
items: [
{
id: 0,
},
],
}),
})
.then((res) => {
if (res.ok) return res.json();
return res.json().then((e) => console.error(e));
})
.then(({url}) => {
console.log(url);
window.location = url;
})
.catch((e) => {
console.log(e);
});
This code when I press the button redirects me to the Stripe payment page.
Endpoint /checkout:
app.post('/checkout', async (req, res) => {
try {
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: req.body.items.map(({id}) => {
const storeItem = storeItems.get(id);
return {
price_data: {
currency: 'pln',
product_data: {
name: storeItem.name,
},
unit_amount: storeItem.priceInCents,
},
quantity: 1,
};
}),
mode: 'payment',
success_url: `${process.env.CLIENT_URL}/success.html`,
cancel_url: `${process.env.CLIENT_URL}/cancel.html`,
});
console.log(session.url);
res.json({url: session.url});
} catch (e) {
// If there is an error send it to the client
console.log(e.message);
res.status(500).json({error: e.message});
}
});
I connected StripeCLI to my server using stripe listen --forward-to localhost:3001/webhook. Now I can capture the successful transaction event using the /webhook endpoint, but I have no way to return The transaction was successful to the client:
app.post('/webhook', (req, res) => {
const event = req.body;
if (event.type === 'checkout.session.completed') {
res.send('The transaction was successful');
}
});
After the suceesful payment the customer should be redirected back to your website. Where you can create success page.
success_url: `${process.env.CLIENT_URL}/success.html`,
If you want to get some data back from the Strapi after the paymant is successful page you can add this
success_url: `${process.env.CLIENT_URL}/success.html?&session_id={CHECKOUT_SESSION_ID}`
At the succes page you just deconstruct the data. And do whatever you want with them :)
If you deconstruct the object for example like this: (Next.js)
const stripe = require("stripe")(`${process.env.STRIPE_SECRET_KEY}`);
export async function getServerSideProps(params) {
const order = await stripe.checkout.sessions.retrieve(
params.query.session_id,
{
expand: ["line_items"],
},
);
const shippingRate = await stripe.shippingRates.retrieve(
"shr_1MJv",
);
return { props: { order, shippingRate } };
}
export default function Success({ order, shippingRate }) {
const route = useRouter();
Yo can log out the whole object to see whats inside
console.log(order);
If the payment was sucessfull you should get in prop variable: payment_status: "paid"
Stripe will automatically redirect the client to the success_url that you specified when you created a Stripe session.
You can use the webhook for saving the order in the database for example, but not to redirect the client.

On payment success remove all cart items

I have integrated stripe api into my ecommerce website. When you checkout you are sent to the stripe api payment link where you type in your information. Of course two things could happen here, either the payment goes through and succeed or the order gets canceled. Everything works except I am trying to remove all cart items only if the payment succeeds and goes through.
Here is my code:
app.post('/api/createCheckoutSession', 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.SERVER_URL}`,
cancel_url: `${process.env.SERVER_URL}cart`,
})
res.json({ url: session.url })
} catch (e) {
console.log(e)
res.status(500).json({ error: e.message })
}
});
So if I do the following code I can remove all cartItems from the user, however this happens regardless of if the payment was successful or not:
app.post('/api/createCheckoutSession', 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.SERVER_URL}`,
cancel_url: `${process.env.SERVER_URL}cart`,
})
cartItem.remove({ user: req.body.user }, function (err) {
if (err) {
console.log(err)
}
})
res.json({ url: session.url })
} catch (e) {
console.log(e)
res.status(500).json({ error: e.message })
}
});
So looking through Stripe api documentation and googling the only thing that consistently comes up is success_url, which wasn't what I am looking for (at least I do not think it will fix what I am trying to do). So for the original code I tried to console.log(session) and found a payment_status: 'unpaid' and figured I could use this to do what I am trying to by the following:
app.post('/api/createCheckoutSession', 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.SERVER_URL}`,
cancel_url: `${process.env.SERVER_URL}cart`,
})
console.log(session)
if (session.payment_status == 'paid') {
cartItem.remove({ user: req.body.user }, function (err) {
if (err) {
console.log(err)
}
})
}
res.json({ url: session.url })
} catch (e) {
console.log(e)
res.status(500).json({ error: e.message })
}
});
With the above code, the if statement does not work as the only time something gets logged to the console is when the session first gets created, so the if statement does not execute after the payment succeeds or gets canceled.
Another solution is for me to create a different success_url where it pops up and I have a react useEffect call the remove cart items function. And then it redirects again somewhere else, but as I alluded to earlier this just seems like a terrible solution when it feels like I am just missing something very simple to get this to work the way I have attempted to.
I'm going to restate what I think your goal is here so I am clear on what the answer is.
After user is sent to checkout you have 2 potential outcomes you want your app to handle:
Successful payment: Get money, get user their goods
Payment canceled: Empty user cart
The problem you are running into is that your code can only await the creation of the user session. At that point you should just redirect to the Checkout URL. At this point your code does not know whether the payment will go through or not because the user is just being redirected to the Checkout UI where they can make their payment.
The state of the user's payment is reported back to your app in 2 different ways.
Customized Success/Cancel URLS - While this doc focuses on capturing the Session ID in the Success URL, you can do the same thing with the Cancel URL. In this case which URL the user is sent to tells your system a Checkout was either successful or was canceled. The Session ID injected into the URL identifies which Checkout session it was.
Checkout Webhook events - This approach provides confirmation of success but requires waiting until the session is expired to confirm cancelation. Still, it is recommended that most integrations make use of Webhooks to monitor account activities.
So to circle back to your code, I would move the code that clears the cart to a function that responds to either the user being redirected to the cancel_url in your app or responds to the checkout.session.expired webhook event

Sending a token via bep20 shows the wrong token on Metamask

When it goes to transfer, it shows the wrong token in the metamask
(async ()=>{
const contract = new web3.eth.Contract(ABI, contractAddress);
const transfer = await contract.methods.transfer(reciever, 1);
const data = await transfer.encodeABI();
if(window.ethereum.chainId == '0x61'){
ethereum
.request({
method: 'eth_sendTransaction',
params: [
{
from: ethereum.selectedAddress,
to: reciever,
gasPrice: '1000000',
gas: '',
data: data,
},
],
})
.then((txHash) => console.log(txHash))
.catch((error) => console.error);
} else {
ethereum.request({ method: 'wallet_switchEthereumChain', params:[{chainId: '0x61'}]})
}
})()
It should show the token, but it shows differently,
When transferring tokens, the transaction needs to be processed by the contract address (not by the token receiver). Note that the contract receiver is passed as the first argument of the transfer() function.
Solution: Replace to: reciever with to: contractAddress in the params section of your snippet.

PayPal Server Side integration not passing payment details to the client

Why am I not getting the response with the capture details on the client side?
I am trying to implement a server side integration for PayPal's smart buttons. I have tried a few different methods, and this is the method I have had the most success with.
However, it still doesn't appear to be working 100%. Atm, clicking a button opens the payment window, I can login with the sandbox personal account, go through the checkout flow, and then I get the standard alert, but for some reason I am not getting the desired response from the server.
When I sign into sandbox paypal, on the personal account, I can see the transactions being sent successfully (they are pending, awaiting confirmation from the merchant). When I sign into the sandbox merchant account, there are no transactions available. When I take the order ID from the smart button, and send it to PayPal's api route to get the order details, it comes back as captured and completed.
Has anyone else experienced something similar with the payments not showing up on the merchant sandbox account? If I sign into the developer account, and look at the API log, I can see the orders being created and captured successfully, but they still don't show up on the merchant account.
Here's my server side code:
const express = require("express");
const router = express.Router();
// 1. Set up your server to make calls to PayPal
// 1a. Import the SDK package
const paypal = require("#paypal/checkout-server-sdk");
// 1b. Import the PayPal SDK client that was created in `Set up Server-Side SDK`.
/**
*
* PayPal HTTP client dependency
*/
const payPalClient = require("./PayPalConfig");
// route to set up a transaction
router.post("/orders/create", async (req, res) => {
// 3. Call PayPal to set up a transaction
const request = new paypal.orders.OrdersCreateRequest();
request.prefer("return=representation");
request.requestBody({
intent: "CAPTURE",
purchase_units: [
{
amount: {
currency_code: "USD",
value: "4.20",
},
},
],
});
let order;
try {
order = await payPalClient.client().execute(request);
} catch (err) {
// 4. Handle any errors from the call
console.error(err);
return res.sendStatus(500);
}
// 5. Return a successful response to the client with the order ID
res.json({
orderID: order.result.id,
});
console.log(order.result.id);
});
// route to handle capturing of orders
router.post("/orders/capture", async (req, res) => {
// const captureDetails
let captureDetails = "";
// 2a. Get the order ID from the request body
const orderID = req.body.orderID;
// 3. Call PayPal to capture the order
const request = new paypal.orders.OrdersCaptureRequest(orderID);
request.requestBody({});
try {
const capture = await payPalClient.client().execute(request);
// 4. Save the capture ID to your database. Implement logic to save capture to your database for future reference.
const captureID = capture.result.purchase_units[0].payments.captures[0].id;
captureDetails = capture.result;
// await database.saveCaptureID(captureID);
res.json(captureDetails);
} catch (err) {
// 5. Handle any errors from the call
console.error(err);
return res.sendStatus(500);
}
// 6. Return a successful response to the client
// res.sendStatus(200).json({ details: captureDetails });
res.json({ details: captureDetails });
});
module.exports = router;
Here's my client side code:
// Render the PayPal button into #paypal-button-container
paypal
.Buttons({
// Call your server to set up the transaction
createOrder: function (data, actions) {
return fetch("http://localhost:3000/payment/paypal/orders/create", {
method: "post",
})
.then(function (res) {
return res.json();
})
.then(function (orderData) {
return orderData.orderID;
console.log(orderData.orderID);
});
},
// Call your server to finalize the transaction
onApprove: function (data) {
return fetch("http://localhost:3000/payment/paypal/orders/capture", {
method: "post",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
orderID: data.orderID,
}),
})
.then(function (res) {
return res;
})
.then(function (details) {
console.log(details);
alert("Transaction funds captured from " + details.payer_given_name);
});
},
})
.render("#paypal-button-container");
Here's the details being logged from the client
Response {type: "cors", url: "http://localhost:3000/payment/paypal/orders/capture", redirected: false, status: 200, ok: true, …}
body: (...)
bodyUsed: false
headers: Headers {}
ok: true
redirected: false
status: 200
statusText: "OK"
type: "cors"
url: "http://localhost:3000/payment/paypal/orders/capture"
__proto__: Response
On the server side, don't specify 'details' as a key.
res.json(captureDetails);
You need to return res.json() on the client side. It hasn't parsed the json object.
When I sign into the sandbox merchant account, there are no
transactions available. When I take the order ID from the smart
button, and send it to PayPal's api route to get the order details, it
comes back as captured and completed.
You are signing in to the wrong sandbox merchant account. The correct one will depend on the sandbox clientId you are using.

Categories