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

1

There are 1 best solutions below

0
vanya On

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.succeeded event when a payment is successful, and the event payload will contain the relevant PaymentIntent. On it you can find the latest_charge property. 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.