idempotency for stripe transfer, refund, charge

This commit is contained in:
jackiettran
2026-01-09 14:14:49 -05:00
parent e2e32f7632
commit 8aea3c38ed
2 changed files with 253 additions and 112 deletions

View File

@@ -104,12 +104,20 @@ class StripeService {
metadata = {},
}) {
try {
const transfer = await stripe.transfers.create({
amount: Math.round(amount * 100), // Convert to cents
currency,
destination,
metadata,
});
// Generate idempotency key from rental ID to prevent duplicate transfers
const idempotencyKey = metadata?.rentalId
? `transfer_rental_${metadata.rentalId}`
: undefined;
const transfer = await stripe.transfers.create(
{
amount: Math.round(amount * 100), // Convert to cents
currency,
destination,
metadata,
},
idempotencyKey ? { idempotencyKey } : undefined
);
return transfer;
} catch (error) {
@@ -216,12 +224,20 @@ class StripeService {
reason = "requested_by_customer",
}) {
try {
const refund = await stripe.refunds.create({
payment_intent: paymentIntentId,
amount: Math.round(amount * 100), // Convert to cents
metadata,
reason,
});
// Generate idempotency key - include amount to allow multiple partial refunds
const idempotencyKey = metadata?.rentalId
? `refund_rental_${metadata.rentalId}_${Math.round(amount * 100)}`
: undefined;
const refund = await stripe.refunds.create(
{
payment_intent: paymentIntentId,
amount: Math.round(amount * 100), // Convert to cents
metadata,
reason,
},
idempotencyKey ? { idempotencyKey } : undefined
);
return refund;
} catch (error) {
@@ -252,20 +268,28 @@ class StripeService {
metadata = {}
) {
try {
// Generate idempotency key to prevent duplicate charges for same rental
const idempotencyKey = metadata?.rentalId
? `charge_rental_${metadata.rentalId}`
: undefined;
// Create a payment intent with the stored payment method
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100), // Convert to cents
currency: "usd",
payment_method: paymentMethodId,
customer: customerId, // Include customer ID
confirm: true, // Automatically confirm the payment
off_session: true, // Indicate this is an off-session payment
return_url: `${
process.env.FRONTEND_URL || "http://localhost:3000"
}/complete-payment`,
metadata,
expand: ["latest_charge.payment_method_details"], // Expand to get payment method details
});
const paymentIntent = await stripe.paymentIntents.create(
{
amount: Math.round(amount * 100), // Convert to cents
currency: "usd",
payment_method: paymentMethodId,
customer: customerId, // Include customer ID
confirm: true, // Automatically confirm the payment
off_session: true, // Indicate this is an off-session payment
return_url: `${
process.env.FRONTEND_URL || "http://localhost:3000"
}/complete-payment`,
metadata,
expand: ["latest_charge.payment_method_details"], // Expand to get payment method details
},
idempotencyKey ? { idempotencyKey } : undefined
);
// Check if additional authentication is required
if (paymentIntent.status === "requires_action") {