I'm charging payments with Stripe in Next.js 13. I'd like to know the fee after each successful transaction. I could not see the charges/fee of the transaction in the response. This is my first experience with stripe. I don't know how to calculate or find the transaction fee. Note: Payments could be in multiple currencies like euro, usd, chf and aud etc. Here is my code
import React, { useState, useEffect } from "react";
import {
CardExpiryElement,
CardCvcElement,
CardNumberElement,
useElements,
useStripe,
} from "@stripe/react-stripe-js";
import axios from "axios";
import Cookies from "js-cookie";
import PaymentService from "src/services/payment";
import { ArrowLeftIcon } from "@heroicons/react/24/outline";
import AlertMessage from "components/common/AlertMessage";
import { useRouter } from "next/navigation";
export default function PaymentForm({
amount,
currency,
campaign,
setShowPayment,
}) {
const stripe = useStripe();
const router = useRouter();
const elements = useElements();
const [paymentStatus, setPaymentStatus] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const [user, setUser] = useState(null);
const [payingAmount, setPayingAmount] = useState();
const [showAlert, setShowAlert] = useState(false);
const [alertType, setAlertType] = useState("");
const [alertMessage, setAlertMessage] = useState("");
useEffect(() => {
setUser(JSON.parse(Cookies.get("rc_me")));
setPayingAmount(amount);
}, []);
const onSubmit = async (e) => {
e.preventDefault();
const cardNumberElement = elements?.getElement(CardNumberElement);
const cardExpiryElement = elements?.getElement(CardExpiryElement);
const cardCvcElement = elements?.getElement(CardCvcElement);
const customerDetails = {
name: user.firstname + " " + user.lastname,
email: user.email,
};
try {
if (
!stripe ||
!cardNumberElement ||
!cardExpiryElement ||
!cardCvcElement ||
!payingAmount ||
payingAmount === 0
)
return null;
setShowAlert(false);
setIsSubmitting(true);
const { data } = await axios.post("/api/create-payment-intent", {
data: {
amount: payingAmount,
currency: currency,
customer: customerDetails, // Include customer details
},
});
const clientSecret = data;
const result = await stripe?.confirmCardPayment(clientSecret, {
payment_method: {
card: cardNumberElement,
billing_details: customerDetails,
},
});
console.log("result: ", result);
if (result.error) {
setPaymentStatus("Payment failed: " + result.error.message);
setShowAlert(true);
setAlertMessage("Payment failed: " + result.error.message);
setAlertType("error");
setIsSubmitting(false);
} else {
const data = {
campaign,
transactionRecord: result,
};
PaymentService.saveTransactionRecord(data).then((res) => {
console.log("res: ", res);
setIsSubmitting(false);
setPaymentStatus("Payment succeeded!");
setShowAlert(true);
setAlertMessage("Payment succeeded!");
setAlertType("success");
setTimeout(() => {
router.push("/dashboard/campaigns");
}, 1000);
});
}
} catch (error) {
console.log(error);
if (error.response) {
setPaymentStatus(error.response.data);
setShowAlert(true);
setAlertMessage(error.response.data);
setAlertType("error");
}
setIsSubmitting(false);
}
};
return (
<>
{showAlert && <AlertMessage type={alertType} message={alertMessage} />}
<button
onClick={() => setShowPayment(false)}
className="flex items-center justify-center text-sm font-semibold gap-2 px-4 py-2 text-black bg-transparent focus:outline-none mb-4"
>
<ArrowLeftIcon className="h-5 w-5" />
<span>{campaign.title}</span>
</button>
<div className="bg-white rounded-lg shadow-md overflow-hidden min-w-full min-h-[80vh] p-4">
<h2 className="text-center text-xl font-bold my-4">Pay for campaign</h2>
<form onSubmit={onSubmit} className="w-96 mx-auto ">
<div className="mb-4">
Amount:
<input
id="amount"
type="number"
value={payingAmount}
className="block w-full rounded-md border-gray-300 shadow-sm h-10"
onChange={(e) => setPayingAmount(e.target.value)}
placeholder="Amount"
/>
</div>
<div className="mb-4">
Amount:
<input
id="curr"
type="text"
value={currency}
className="block w-full rounded-md border-gray-300 shadow-sm h-10"
disabled
placeholder="Currency"
/>
</div>
<div className="mb-4">
Card Number:
<CardNumberElement className="border p-3 rounded-md text-base text-gray-800 font-sans" />
</div>
<div className="grid grid-cols-2 gap-4">
<div className="mb-4">
Expiration Date:
<CardExpiryElement className="border p-3 rounded-md text-base text-gray-800 font-sans" />
</div>
<div className="mb-4">
CVC:
<CardCvcElement className="border p-3 rounded-md text-base text-gray-800 font-sans" />
</div>
</div>
<div>
<button
onClick={() => setShowPayment(false)}
type="button"
className="mt-4 w-full bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded cursor-pointer"
>
Cancel
</button>
<button
type="submit"
className={`mt-4 w-full ${
isSubmitting
? "bg-blue-400 cursor-not-allowed"
: "bg-blue-500 hover:bg-blue-700"
} text-white font-bold py-2 px-4 rounded cursor-pointer`}
disabled={isSubmitting}
>
{isSubmitting ? "Submitting..." : "Submit"}
</button>
</div>
</form>
{paymentStatus && (
<div className="text-center mt-4">{paymentStatus}</div>
)}
</div>
</>
);
}
<form onSubmit={onSubmit} className="w-96 mx-auto ">
<div className="mb-4">
Amount:
<input
id="amount"
type="number"
value={payingAmount}
className="block w-full rounded-md border-gray-300 shadow-sm h-10"
onChange={(e) => setPayingAmount(e.target.value)}
placeholder="Amount"
/>
</div>
<div className="mb-4">
Amount:
<input
id="curr"
type="text"
value={currency}
className="block w-full rounded-md border-gray-300 shadow-sm h-10"
disabled
placeholder="Currency"
/>
</div>
<div className="mb-4">
Card Number:
<CardNumberElement className="border p-3 rounded-md text-base text-gray-800 font-sans" />
</div>
<div className="grid grid-cols-2 gap-4">
<div className="mb-4">
Expiration Date:
<CardExpiryElement className="border p-3 rounded-md text-base text-gray-800 font-sans" />
</div>
<div className="mb-4">
CVC:
<CardCvcElement className="border p-3 rounded-md text-base text-gray-800 font-sans" />
</div>
</div>
<div>
<button
onClick={() => setShowPayment(false)}
type="button"
className="mt-4 w-full bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded cursor-pointer"
>
Cancel
</button>
<button
type="submit"
className={`mt-4 w-full ${
isSubmitting
? "bg-blue-400 cursor-not-allowed"
: "bg-blue-500 hover:bg-blue-700"
} text-white font-bold py-2 px-4 rounded cursor-pointer`}
disabled={isSubmitting}
>
{isSubmitting ? "Submitting..." : "Submit"}
</button>
</div>
</form>
here is the route.ts file
import { NextResponse, NextRequest } from "next/server";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
typescript: true,
apiVersion: "2023-08-16",
});
export async function POST(req: NextRequest) {
const { data } = await req.json();
const { amount, currency } = data;
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: Number(amount) * 100,
currency: currency,
payment_method_types: ["card"],
});
return new NextResponse(paymentIntent.client_secret, { status: 200 });
} catch (error: any) {
return new NextResponse(error, {
status: 400,
});
}
}
You can see the Stripe fee on the
PaymentIntent.latest_charge.balance_transaction.fee_details.amount:You should use webhooks to receive notifications about your PaymentIntent. You will receive
payment_intent.succeededevent when a payment is successful, and the event payload will contain the relevant PaymentIntent. On it you can find thelatest_chargeproperty. You can use the Charges API to retrieve the Charge by the ID. You can expand the API responses to avoid making multiple calls to Stripe.